Android View的onMeasure

View的結(jié)構(gòu)

image.png

Android中的View以樹(shù)的結(jié)構(gòu)來(lái)進(jìn)行管理,從如中可以看到,最頂層的Window到ContentView,這些都是我們?cè)趯?shí)際開(kāi)發(fā)過(guò)程中比較少用到的。

MeasureSpec

MeasureSpec是一個(gè)int類(lèi)型的變量,以下為MeasureSpec的部分源代碼:

        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        public static final int AT_MOST     = 2 << MODE_SHIFT;

可以看到MeasureSpec用最高的兩位用來(lái)存放測(cè)量的模式:
UNSPECIFIED:大小不確定的
EXACTLY:大小確定了具體的
AT_MOST:大小的上限,即最大不能超過(guò)這個(gè)

ViewGroup的測(cè)量

View一開(kāi)始從DecorView開(kāi)始進(jìn)行measure,由于DecorView本質(zhì)是FrameLayout即就是ViewGroup,所以我們一開(kāi)始從ViewGroup開(kāi)始講onMeasure。

ViewGroup從被measure調(diào)用之后開(kāi)始調(diào)用onMeasure開(kāi)始測(cè)量自己;
ViewGroup從onMeasure函數(shù)中獲得的widthMeasureSpec和heightMeasureSpec,根據(jù)這兩個(gè)變量的值來(lái)進(jìn)行對(duì)子View的測(cè)量。遍歷子View更具子View的LayoutParam進(jìn)行下一步的MeasureSpec計(jì)算,即調(diào)用getChildMeasureSpec(measureSpec,padding,size);將得到的結(jié)果傳入到measure中進(jìn)行子View的onMeasure。

       @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

            int widthSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
            int heightSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);

            super.onMeasure(widthSpec, heightSpec);
            
            for (int i = 0; i < getChildCount(); i++) {
                View view = getChildAt(i);
                LayoutParams lp = view.getLayoutParams();
                int childMeasureWidth = getChildMeasureSpec(widthMeasureSpec,0,lp.width);
                int childMeasureHeight = getChildMeasureSpec(heightMeasureSpec,0,lp.height);
                view.measure(childMeasureWidth,childMeasureHeight);
                //measureChild(view, widthSpec, heightSpec);
            }
        }

這里假設(shè)ViewGroup的大小是確定的且為寬高都為500個(gè)像素,為什么是500個(gè)像素,后面再講。接著往下走,遍歷每一個(gè)子View,根據(jù)子View的布局參數(shù)來(lái)計(jì)算以下的子View的測(cè)量規(guī)格即調(diào)用了getChildMeasureSpec,接著將處理過(guò)的測(cè)量規(guī)格傳給子View進(jìn)行measure,接著跟蹤進(jìn)measure,measure中代碼很長(zhǎng)包含了measure緩存的處理比較復(fù)雜,這里就不貼出來(lái)了,view的measure代碼大致上就是調(diào)用了View的onMeasure,接著看看View的onMeasure是怎么實(shí)現(xiàn)的:

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

可以看到直接通過(guò)setMeasuredDimension把size提交上去了,這里的getDefaultSize和getSuggestedMinimumWidth依次:
根據(jù)傳入的測(cè)量規(guī)格和建議的默認(rèn)大小進(jìn)行實(shí)際的大小確定:

    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;
    }

默認(rèn)建議的大小是根據(jù)View的背景大小來(lái)確定的,如果背景Drawable是空的話那就是默認(rèn)的mMinHeight的值,該變量默認(rèn)值是0,否則的話就取背景的最小尺寸

    protected int getSuggestedMinimumHeight() {
        return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
    }

通過(guò)以上步驟,View的測(cè)量就完成了,當(dāng)然,對(duì)于子View中的ViewGroup的測(cè)量,遞歸上述過(guò)程就OK了。

補(bǔ)充

measureChild

在View的measure中:

 int childMeasureWidth = getChildMeasureSpec(widthMeasureSpec,0,lp.width);
                int childMeasureHeight = getChildMeasureSpec(heightMeasureSpec,0,lp.height);
                view.measure(childMeasureWidth,childMeasureHeight);

這段代碼可以替換成:

measureChild(view, widthSpec, heightSpec);

他倆的實(shí)際作用是差不多的,查看系統(tǒng)源碼可以一目了然的看到:

    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);
    }

measureChildren

measureChildren實(shí)際上是遍歷各個(gè)子View,然后分別調(diào)用measureChild:

    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);
            }
        }
    }
?著作權(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)容