View的繪制流程之Layout

View的繪制流程(二)

每一個視圖的繪制過程都必須經(jīng)歷三個最主要的階段,即onMeasure()、onLayout()和onDraw()

Layout

mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 
......
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());

子View的Measure的過程結(jié)束后抹沪,View Tree上的所有View的大小都已經(jīng)確定洋闽。接下來就是Layout的過程。這個過程就是用于給視圖進(jìn)行布局的休涤,也就是確定視圖的位置咱圆。ViewRootImpl的performTraversals()方法會在measure結(jié)束后繼續(xù)執(zhí)行,并調(diào)用View的layout()方法來執(zhí)行此過程功氨,如上述代碼(其中mView就是DecorView序苏,mView.getMeasureWidth/Height()返回的數(shù)值就是Measure過程計算出來的)。

layout的主要作用:根據(jù)子視圖的大小以及布局參數(shù)將View樹放到合適的位置上疑故。
注意:Android中的每個空間都會在界面中占得一塊矩形區(qū)域杠览。我們通過Measure過程計算出來的大小就是這塊矩形的大小。

由于mView就是DecorView纵势,本質(zhì)上就是一個FrameLayout踱阿,所以首先調(diào)用的就是ViewGroup的layout()方法,傳入的參數(shù)都是0钦铁,0软舌,measuredWidth, measuredHeight(位置從左上角開始,將整個DecorView完整顯示牛曹,measuredWidth/Height都是通過ViewRootImpl計算得到的佛点,一般是屏幕大小)

public final void layout(int l, int t, int r, int b) {    
   if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {        
    if (mTransition != null) {            
       mTransition.layoutChange(this);        
    }       
    super.layout(l, t, r, b);    
    } else {        
    // record the fact that we noop'd it; request layout when transition finishes        
      mLayoutCalledWhileSuppressed = true;    
   }
}

這里大致看一下代碼,如果此時對ViewGroup中的子View操作時(增加或者刪除),那么就調(diào)用View.java中的layout()超营;如果此時正在操作鸳玩,那么設(shè)置flag(mLayoutCalledWhileSuppressed)為true,表明需要等操作完成再調(diào)用requestLayout()(重新遍歷View Tree演闭,調(diào)用onMeasure()和onLayout())不跟。

由于這個方法有final修飾詞,所以無法覆寫米碰,所有ViewGroup的子類都會調(diào)用這個方法作為Layout過程的第一步窝革。

 public final void layout(int l, int t, int r, int b) {
       int oldL = mLeft;  
       int oldT = mTop;  
       int oldB = mBottom;  
       int oldR = mRight;
      //設(shè)置View位于父視圖的坐標(biāo)軸
       boolean changed = setFrame(l, t, r, b); 
       //判斷View的位置是否發(fā)生過變化,看有必要進(jìn)行重新layout嗎
       if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
           if (ViewDebug.TRACE_HIERARCHY) {
               ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
           }
           //調(diào)用onLayout(changed, l, t, r, b); 函數(shù)
           onLayout(changed, l, t, r, b);
           mPrivateFlags &= ~LAYOUT_REQUIRED;
       }
       mPrivateFlags &= ~FORCE_LAYOUT;
       .....
   }

當(dāng)ViewGroup中對其子視圖的操作都完成了吕座,調(diào)用View.layout()虐译。這邊理解幾個關(guān)鍵方法:setFrame()和onLayout()。
首先會調(diào)用setFrame()方法來判斷視圖的大小是否發(fā)生過變化吴趴,以確定有沒有必要對當(dāng)前的視圖進(jìn)行重繪漆诽,同時還會在這里把傳遞過來的四個參數(shù)分別賦值給mLeft、mTop史侣、mRight和mBottom這幾個變量拴泌。這幾個值構(gòu)成的矩形區(qū)域就是該View顯示的位置,這里的具體位置都是相對與父視圖的位置惊橱。

以后我們在代碼中調(diào)用View子類的getTop/Right/Bottom/Left()返回的值都是這里設(shè)置的值

接著我們回調(diào)ViewGroup.onlayout()方法蚪腐,但是在View.java中發(fā)現(xiàn)這是一個空方法,一般情況下我們不需要覆寫View.java的該方法税朴。

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  

}

對于ViewGroup 來說回季,唯一的差別就是ViewGroup中多了關(guān)鍵字abstract的修飾,要求其子類必須重載onLayout函數(shù)正林。

protected abstract void onLayout(boolean changed, int l, int t, int r, int b);

這里我的理解泡一,ViewGroup中的onLayout()方法就是用于確定視圖在布局中的位置,而這個操作應(yīng)該有ViewGroup來完成觅廓,同時不同的ViewGroup子類都用不同的放置自身子View的算法鼻忠。所以自定義ViewGroup時必須實現(xiàn)這個方法。

舉個例子:

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
         int childCount = getChildCount();
         for(int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if(child.getVisibility() != View.GONE) {
                        child.layout(1, i * mScreenHeight, r, (i + 1) * mScreenHeight)
                }
         }
}

上述代碼只是簡單的實現(xiàn)杈绸,并沒有什么實際意義帖蔓。首先我們遍歷該View的所有子View,在遍歷的同時瞳脓,我們通過調(diào)用child.layout()來設(shè)置child矩形相對于該View的位置塑娇,即child要顯示的位置。這里傳遞給child.layout()的參數(shù)應(yīng)該結(jié)合布局文件中的設(shè)置的屬性(android:gravity...)和child的Measure過程得到的矩形大小值劫侧,來確定child.layout()的參數(shù)埋酬。

傳入layout()方法的參數(shù)可以自定義哨啃,但是最好能夠?qū)hild的矩形完整顯示出來,也就是按照Measure過程得到的尺寸來確定這四個參數(shù)写妥。當(dāng)然我們可以安全不用顧忌Measure過程計算出來的child矩形大小拳球,自己自定義這四個參數(shù),顯示child矩形的一部分或者大于child的大小耳标。這種情況下醇坝,當(dāng)完成child.onlayout()方法后可以通過getWidth/Height()方法來獲取視圖的寬高度和Measure過程結(jié)束后調(diào)用getMeasureWidth/Height()得到的視圖寬高度不一致(getWidth()返回的值是child的右左邊減去左坐標(biāo),getMeasureWidth()是child.setMeasuredDimension()設(shè)置的)次坡。最好能夠保持它們的一致,是編碼的好習(xí)慣画畅。

public final int getMeasuredWidth() {  
    return mMeasuredWidth & MEASURED_SIZE_MASK;  
}  
public final int getWidth() {  
    return mRight - mLeft;  
}

參考文獻(xiàn):郭神砸琅、Kelin大牛

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市轴踱,隨后出現(xiàn)的幾起案子症脂,更是在濱河造成了極大的恐慌,老刑警劉巖淫僻,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诱篷,死亡現(xiàn)場離奇詭異,居然都是意外死亡雳灵,警方通過查閱死者的電腦和手機(jī)棕所,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悯辙,“玉大人琳省,你說我怎么就攤上這事《阕” “怎么了针贬?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拢蛋。 經(jīng)常有香客問我桦他,道長,這世上最難降的妖魔是什么谆棱? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任快压,我火速辦了婚禮,結(jié)果婚禮上础锐,老公的妹妹穿的比我還像新娘嗓节。我一直安慰自己,他們只是感情好皆警,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布拦宣。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸵隧。 梳的紋絲不亂的頭發(fā)上绸罗,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機(jī)與錄音豆瘫,去河邊找鬼珊蟀。 笑死,一個胖子當(dāng)著我的面吹牛外驱,可吹牛的內(nèi)容都是我干的育灸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼昵宇,長吁一口氣:“原來是場噩夢啊……” “哼磅崭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瓦哎,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤砸喻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蒋譬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體割岛,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年犯助,在試婚紗的時候發(fā)現(xiàn)自己被綠了癣漆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡也切,死狀恐怖扑媚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雷恃,我是刑警寧澤疆股,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站倒槐,受9級特大地震影響旬痹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜讨越,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一两残、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧把跨,春花似錦人弓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽意蛀。三九已至,卻和暖如春健芭,著一層夾襖步出監(jiān)牢的瞬間县钥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工慈迈, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留若贮,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓痒留,卻偏偏與公主長得像谴麦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子伸头,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容