? ? ?每個(gè)View從父視圖拿到的剪切區(qū)的原點(diǎn)坐標(biāo)都是View的視圖坐標(biāo)原點(diǎn),剪切區(qū)的大小由View的布局(onLayout時(shí)確定)決定,剪切區(qū)的位置由View的布局和View的mScrollX\mScrollY決定。

灰色矩形為最外層ViewGroup,不可滑動(dòng)(mScrollX\mScrollY為0),藍(lán)色矩形可滑動(dòng)。
初始狀態(tài)
? ? ? ?父視圖在給子視圖分配剪切區(qū)的時(shí)候(在2.3的源碼中是View的dispatchdraw()方法),會(huì)根據(jù)子視圖在onLayout時(shí)確定的位置和子視圖mScrollX\mScrollY的值確定,確保子視圖在onDraw(Canvas canvas)方法中拿到的canvas的坐標(biāo)起點(diǎn)是子視圖自身大小的坐標(biāo)起點(diǎn)。所謂的draw操作只管將子視圖本身畫出來,而暫時(shí)不管其能否全部顯示在屏幕上。此時(shí)子視圖自身的坐標(biāo)起點(diǎn),可能在屏幕外(如上面右邊的圖),也可能在屏幕內(nèi)。
在左圖中,所有視圖自身的大小,視圖坐標(biāo)原點(diǎn),都是顯示在屏幕上,你所看到的。
滑動(dòng)后狀態(tài)
? ? ?現(xiàn)在,手指在屏幕上向上滑動(dòng)了100px的距離,正好把T1滑出屏幕外,那么此時(shí)mScrollY=100px。
? ? ?此時(shí),灰色矩形拿到的剪切區(qū)仍然不變,因?yàn)樗膐nLayout值和mScrollX/mScrollY的值都沒變化。至于藍(lán)色矩形,他已經(jīng)滑到了屏幕外100px,滑動(dòng)并不會(huì)改變剪切區(qū)的大小,而只會(huì)改變其剪切區(qū)的位置,從而影響了他給子視圖分配的剪切區(qū)的位置,所以導(dǎo)致了一部分子視圖消失。

視圖坐標(biāo)變化
? ? ?當(dāng)灰色矩形為藍(lán)色矩形分配剪切區(qū)時(shí),他的canvas坐標(biāo)原點(diǎn)在1,而藍(lán)色矩形的視圖坐標(biāo)原點(diǎn)在2,那么怎樣將canvas的坐標(biāo)原點(diǎn)從1——>2呢?
? ? ?看看究竟他要考慮哪些參數(shù),第一個(gè)參數(shù),在onLayout時(shí)確定的mLeft,mTop,mRight,mBottom4個(gè)值,在圖中我只標(biāo)注了mLeft。這四個(gè)值確定剪切區(qū)的大小,因?yàn)闊o論你的視圖有多大,如一張很大的Bitmap顯示在ImageView中,無論你的Bitmap有多大,在Android手機(jī)上,給到你的ImageView的大小也撐破天只能是個(gè)全屏(比如1920x1080),這是從內(nèi)存等性能問題上的考慮,因?yàn)閖ava層其實(shí)就是數(shù)據(jù)的封裝,如果你告訴底層我要2000x2000的剪切區(qū)(在底層對(duì)應(yīng)著內(nèi)存分配),這完全就是浪費(fèi)內(nèi)存和性能,你在底層分配的內(nèi)存以及繪制的那部分,根本無法顯示到屏幕上。所以,onLayout()存在的意義在于確定視圖在屏幕上的位置,無論你的視圖是多大,經(jīng)過了onLayout方法之后,你在屏幕上的位置也就確定了。
? ? ?對(duì)父視圖的canvas進(jìn)行平移操作,canvas.translate(mLeft+mScrollX(0),mTop(0)+(-mScrollY)),這樣canvas坐標(biāo)就從1點(diǎn)平移到了2點(diǎn)(此時(shí)的canvas還是父視圖的canvas,只是坐標(biāo)點(diǎn)進(jìn)行了平移)。視圖坐標(biāo)原點(diǎn)確定后,就要為子視圖裁剪剪切區(qū),調(diào)用canvas.clipRect(sx,sy,sx+(mRight-mLeft),sy+(mBottom-mTop),實(shí)際截切區(qū)的位置由sx和sy所確定,而剪切區(qū)的大小就是視圖的布局大小)。
? ? ?網(wǎng)上有無數(shù)這樣對(duì)ScrollX/ScrollY的理解,如"對(duì)于ViewGroup來說,調(diào)用scrollTo()是滑動(dòng)子視圖,對(duì)于View來說,調(diào)用scrollTo()是滑動(dòng)其中的內(nèi)容。"這樣的解釋對(duì)我來說毫無意義!??!
? ? ?在View體系中,scrollTo()調(diào)用之后,并不影響onMeasure()和onLayout()之后確定的值,只會(huì)影響mScrollX/mScrollY的值,從而影響子視圖剪切區(qū)的分配。對(duì)于藍(lán)色矩形來說,他的scroll值的改變影響了灰色矩形為他分配的剪切區(qū),從而影響了藍(lán)色矩形為所有子視圖分配的剪切區(qū)。而藍(lán)色矩形了布局是確定的,scroll值的改變不會(huì)影響該區(qū)域,只有剪切區(qū)在該區(qū)域內(nèi)的子視圖才會(huì)顯示出來,其他的就隱藏了。
至于View內(nèi)容的繪制,可以用數(shù)組的方式理解。

? ? ?這樣想,給TextView設(shè)置的文本字符最終會(huì)變成一個(gè)字符數(shù)組(如0-99),然而只有50-99下標(biāo)的字符才可以顯示在TextView中,其他的字符則"隱藏了"。對(duì)于ImageView,也可以用這樣的理解,bitmap對(duì)象最終也會(huì)變成一個(gè)數(shù)組,ImageView的寬高是有限制的,而bitmap的大小則是沒有限制的,如果你給ImageView設(shè)置src屬性,那么只有一部分bitmap會(huì)顯示,而設(shè)置backgroup屬性,則bitmap會(huì)被壓縮后全部顯示。
? ? ?還有一種滑動(dòng)的方式View.offsetTopAndBottom,他其實(shí)就是改變View的mTop和mBottom,但卻不會(huì)調(diào)用onLayout。這樣的滑動(dòng)方式是改變子視圖,如T1的內(nèi)部參數(shù),從而改變T1的剪切區(qū)位置,而其父視圖不會(huì)受到任何影響。
? ? ?任何一種滑動(dòng),其本質(zhì)就是改變View內(nèi)的某種參數(shù),讓其在繪制的時(shí)候重新計(jì)算出View的剪切區(qū),達(dá)到顯示或隱藏得目的。上面的兩種方式都規(guī)避的耗時(shí)的onLayout操作,所以滑動(dòng)起來還是很流暢,應(yīng)該避免用onLayout或LayoutParams值來進(jìn)行滑動(dòng)操作。