UI繪制流程(三)

UI繪制流程的起始點(diǎn) ViewRootImpl#performTraversals()方法中:

此方法里分別調(diào)用了:
///測(cè)量
performMeasure()
// 擺放布局
performLayout()
// 繪制
performDraw()

這也是我們自定義UI布局時(shí)注意的過(guò)程 : 測(cè)量(Measure)—>布局(Layout)—>繪制(Draw)


Measure測(cè)量過(guò)程:

1. 通過(guò)getRootMeasureSpec(int windowSize, int rootDimension)方法傳入父容器windowSize(具體數(shù)值) rootDimension (LayoutParams.(width|height))獲取子view寬高測(cè)量模式;
具體代碼:
 private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // 窗口不能調(diào)整大小。強(qiáng)制根視圖為windowSize
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // 窗口可以調(diào)整大小。設(shè)置根視圖的最大大小。
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // 窗口想成為一個(gè)確切的大小。強(qiáng)制根視圖是那個(gè)大小。
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }
MeasureSpec:

在Measure流程中,系統(tǒng)將View的LayoutParams根據(jù)父容器所施加的規(guī)則轉(zhuǎn)換成對(duì)應(yīng)的MeasureSpec,在onMeasure中根據(jù)這個(gè)MeasureSpec來(lái)確定view的測(cè)量寬高

測(cè)量模式:
  • EXACTLY :父容器已經(jīng)測(cè)量出所需要的精確大小,這也是childview的最終大小------match_parent,精確值
  • ATMOST : child view最終的大小不能超過(guò)父容器的給的------wrap_content
  • UNSPECIFIED: 不確定,源碼內(nèi)部使用-------一般在ScrollView,ListView
MeasureSpec里通過(guò)和一個(gè)數(shù)值M size 模式 與或非運(yùn)算 得到一個(gè)數(shù)值(測(cè)量模式值) 而后通過(guò)M進(jìn)行一些運(yùn)算可以拿到父容器 size 模式
2. 調(diào)用performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) 兩個(gè)參數(shù)是根據(jù)父容器的寬高測(cè)量模式 ,在方法中 調(diào)用 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)又在這個(gè)方法里調(diào)用了onMeasure(int widthMeasureSpec, int heightMeasureSpec) 而 mView 為DecorView 而,DecorView繼承FrameLayout ,onMeasure方法在FrameLayout被重寫(xiě)了,所以最終調(diào)用的是FrameLayout onMeasure方法;
3. FrameLayout onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法:
容器view

1.獲取子view數(shù)并遍歷;
2.遍歷過(guò)程 獲取view child 判斷 child.getVisibility() != GONE 時(shí)調(diào)用measureChildWithMargins()方法測(cè)量子view
遍歷代碼:

 for (int i = 0; i < count; i++) 

            final View child = getChildAt(i);//獲取子view

            if (mMeasureAllChildren || child.getVisibility() != GONE) {//判斷GONE 是GONE不用測(cè)量

                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);//測(cè)量child以及它的子view

                final LayoutParams lp = (LayoutParams) child.getLayoutParams();//獲取view LayoutParams 
                //獲取所有子view中最大的寬或高
                maxWidth = Math.max(maxWidth,child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight,child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                if (measureMatchParentChildren) {
                    if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) {
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }

2.1.measureChildWithMargins()方法中傳入了 child:子view, parentWidthMeasureSpec:父容器寬模式 parentHeightMeasureSpec:父容器高模式 等
2.2.measureChildWithMargins()具體執(zhí)行為 代碼:

    protected void measureChildWithMargins(View child,  int parentWidthMeasureSpec, int widthUsed,  int parentHeightMeasureSpec, int heightUsed) {
        //獲取子view MarginLayoutParams
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
       //根據(jù)子view寬或高對(duì)應(yīng)的  父容器模式  Padding Margin width 獲取該控件的測(cè)量模式
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);
         //繼續(xù)測(cè)量子view 的子view 直到視圖view為止
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
  1. 遍歷完成之后獲取了子view中最大寬 或 高 調(diào)用setMeasuredDimension()方法為該view設(shè)置寬高
setMeasuredDimension()后才可以獲得view的寬高

小結(jié):ViewGroup遍歷測(cè)量Child三方法 自定義中使用:

  • measureChildWithMargins // 有Margin測(cè)量
  • measureChild// 測(cè)量這個(gè)view 沒(méi)有Margin測(cè)量 自己遍歷
  • measureChildren//遍歷所有子view完成沒(méi)有Margin測(cè)量
視圖view

根據(jù)view設(shè)置內(nèi)容 或呈現(xiàn)內(nèi)容 來(lái)完成測(cè)量
setMeasuredDimension()調(diào)用完成測(cè)量

測(cè)量總結(jié) 自定義View,ViewGroup:

View:
  • 套路:根據(jù)父容器傳來(lái)的測(cè)量模式確定view寬高 以及自己內(nèi)容 最終調(diào)用setMeasuredDimession方法來(lái)保存自己的測(cè)量寬高
            final int specMode = MeasureSpec.getMode(measureSpec);
            final int specSize =  MeasureSpec.getSize(measureSpec);
            switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
             
                break;
            case MeasureSpec.AT_MOST:
                
                break;
            case MeasureSpec.EXACTLY:
            //完成寬高測(cè)量
                break;
        }
       setMeasuredDimension(width, height);
ViewGroup
  • 1、測(cè)量子view的規(guī)格大小 measureChildWithMargins measureChild measureChildren等方法
  • 2、通過(guò)子view的規(guī)格大小來(lái)確定自己的大小 setMeasuredDimession

Layout測(cè)量過(guò)程:

大概過(guò)程是Measure一樣

ViewGroup

重寫(xiě)onLayout() 根據(jù)里要的樣式計(jì)算每個(gè)view left, top, right, bottom 在調(diào)用子view layout(left, top, right, bottom)方法完成布局

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容