View的工作流程 源碼分析

View的工作流程是指measure榨呆、layout兔魂、draw三大流程,即策略闰挡、布局、重繪礁哄。

一.Measure過程

1.view的Measure過程

1.在onMeasure調用setMeasuredDimension( )設置View的寬和高.

2.在setMeasuredDimension()中調用getDefaultSize()獲取View的寬和高.

3.在getDefaultSize()方法中又會調用到getSuggestedMinimumWidth()或者getSuggestedMinimumHeight()獲取到View寬和高

View的onMeasure方法()

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

setMeasuredDimension()方法會設置View的寬和高长酗,getDefaultSize()返回View測量后的大小(注:View的最終大小是在layout階段確定的

 public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

getSuggestedMinimumWidth()方法

 protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

如果View沒有設置背景桐绒,那么View的寬度為mMinWidth夺脾,而mMinWidth對應于android:minWidth這個屬性所指的的值,如果這個屬性不指定茉继,那么mMinWidth默認值為0咧叭;如果設置了背景則返回android:minWidth和背景的最小寬度這兩者中的最大值,getSuggestedMinimumHeight()實現(xiàn)的原理也類似烁竭。

解決在布局中使用wrap_content,效果是match_parent的問題菲茬。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec , heightMeasureSpec);  
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);  
int widthSpceSize = MeasureSpec.getSize(widthMeasureSpec);  
int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);  
int heightSpceSize=MeasureSpec.getSize(heightMeasureSpec);  

if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){  
    setMeasuredDimension(mWidth, mHeight);  
}else if(widthSpecMode==MeasureSpec.AT_MOST){  
    setMeasuredDimension(mWidth, heightSpceSize);  
}else if(heightSpecMode==MeasureSpec.AT_MOST){  
    setMeasuredDimension(widthSpceSize, mHeight);  
}  
} 

在上面的代碼中,我們只需要給View指定一個默認的寬高(mWidth, mHeight)派撕,并在wrap_content時設置此寬/高即可婉弹。對于非wrap_content的情形,使用系統(tǒng)的測量值就行终吼,指定一個默認的寬高(mWidth, mHeight)根據(jù)需要自行指定镀赌。

2.viewGroup的Measure過程

對于ViewGroup來說,除了完成自己的Measure過程际跪,還回遍歷去調用所有子元素的measure方法商佛,各個子元素在遞歸的執(zhí)行這個過程。

ViewGroup是一個抽象類姆打,沒有重寫View的onMeasure方法良姆,但是它提供了一個measureChildren方法。這是因為不同的ViewGroup子類有不同的布局特性幔戏,導致他們的測量細節(jié)各不相同歇盼,比如LinearLayout和RelativeLayout,因此ViewGroup沒辦法同一實現(xiàn)onMeasure方法。 measureChildren方法的流程:

取出子View的LayoutParams

通過getChildMeasureSpec方法來創(chuàng)建子元素的MeasureSpec

將MeasureSpec直接傳遞給View的measure方法來進行測量

   protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
    final int size = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < size; ++i) {
        final View child = children[i];
        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
    }
 }

 protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
    final LayoutParams lp = child.getLayoutParams();

    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

可以查看一下LinearLayout 的onMeasure方法

Measure完成以后就可以在onLayout()通過getMeasuredWidth/Height獲取到View的測量寬高评抚,

二.Layout過程

Layout的作用是ViewGroup用來確定子元素的位置豹缀,當ViewGroup位置確定以后伯复,他就會在onLayout中遍歷所有子元素并調用其layout過程,在layout方法中onLayout方法又會被調用邢笙。layout方法確定view本身的位置啸如,onlayout方法則確定所有子元素的位置。
View的layout源碼

public void layout(int l, int t, int r, int b) {
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);
        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnLayoutChangeListeners != null) {
            ArrayList<OnLayoutChangeListener> listenersCopy =
                    (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
            int numListeners = listenersCopy.size();
            for (int i = 0; i < numListeners; ++i) {
                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
            }
        }
    }

    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}

layout方法的大致流程:

1.通過setFrame方法來設定View的四個頂點的位置,既初始化mLeft暴构、mTop语淘、mBottom、mRight這四個值帘不,View的四個頂點位置確認了,那么View在父容器中的位置也就確認了杨箭。
2.調用onLayout方法寞焙,父容器確定子元素的位置,(同onMeasure,View 和ViewGroup 均沒有真正實現(xiàn)onLayout互婿,而是放在具體的子類如Linearlayout 中去實現(xiàn))

View的測量寬/高和最終寬/高的區(qū)別捣郊?
這個問題可以具體為:View的getMeasuredWidth和getWidth;View的getMeasuredHeight和getHeight有什么區(qū)別

public final int getWidth() {
    return mRight - mLeft;
}

public final int getHeight() {
    return mBottom - mTop;
}

在日常開發(fā)中,我們認為這兩個值相等慈参。

三.draw過程

View的繪制過程遵循如下幾步:

(1)繪制背景drawBackground(canvas)

(2)繪制自己onDraw

(3)繪制childrendispatchDraw遍歷所有子View的draw方法

(4)繪制裝飾onDrawScrollBars

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末呛牲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子驮配,更是在濱河造成了極大的恐慌娘扩,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壮锻,死亡現(xiàn)場離奇詭異畜侦,居然都是意外死亡,警方通過查閱死者的電腦和手機躯保,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門旋膳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人途事,你說我怎么就攤上這事验懊。” “怎么了尸变?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵义图,是天一觀的道長。 經常有香客問我召烂,道長碱工,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮怕篷,結果婚禮上历筝,老公的妹妹穿的比我還像新娘。我一直安慰自己廊谓,他們只是感情好梳猪,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蒸痹,像睡著了一般春弥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叠荠,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天匿沛,我揣著相機與錄音,去河邊找鬼榛鼎。 笑死逃呼,一個胖子當著我的面吹牛,可吹牛的內容都是我干的借帘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼淌铐,長吁一口氣:“原來是場噩夢啊……” “哼肺然!你這毒婦竟也來了?” 一聲冷哼從身側響起腿准,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤际起,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吐葱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體街望,經...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年弟跑,在試婚紗的時候發(fā)現(xiàn)自己被綠了灾前。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡孟辑,死狀恐怖哎甲,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情饲嗽,我是刑警寧澤炭玫,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站貌虾,受9級特大地震影響吞加,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一衔憨、第九天 我趴在偏房一處隱蔽的房頂上張望叶圃。 院中可真熱鬧,春花似錦巫财、人聲如沸盗似。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赫舒。三九已至,卻和暖如春闽瓢,著一層夾襖步出監(jiān)牢的瞬間接癌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工扣讼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缺猛,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓椭符,卻偏偏與公主長得像荔燎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子销钝,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容