1. 程式人生 > >FrameLayout 測量過程(程式碼流水線)

FrameLayout 測量過程(程式碼流水線)

   /**
     * {@inheritDoc}
     * 如果,子 view如果設定屬性是 match_parent,
     * 如果,measureMatchParentChildren是true (其實就是framlayout模式是非精準模式)
     * 那麼這種子view是被測量了兩次
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();

        //當FrameLayout的寬和高 只有同時設定為 match_parent
//當FrameLayout的寬和高 指定的size ///measureMatchParentChlidren = false,否則為true。 //MeasureSpec.EXACTLY時,就表示當前檢視的大小等於引數measureSpec中所指定的值。 //當FrameLayout 非精準模式,這時候要把所有寬高設定為 //match_parent的子View都記錄下來 // 這時候FrameLayout的寬高同時受子View的影響 final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int
maxHeight = 0; int maxWidth = 0; int childState = 0;//寬高的期望型別 //測量孩子同時,計算出一個最大寬高 for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { //一次遍歷每一個不為GONE的子view //第一次測量孩子
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 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()); //的mMatchParentChlidren的list裡存的是設定為match_parent的子view。 if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } // Account for padding too //加上父親的padidng maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width // 校驗最小寬高 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width // 與前景圖片比較,獲取寬高相對較大的值 final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } //設定測量過的寬高 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); //上面的測量,得到了framlayout的寬,高,下面,需要根據寬高,重新測量子view - 主要是 match_parent 屬性的孩子 //子view中設定為match_parent的個數 //重新測量 count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; //將屬性為match_parent的再測量一遍 if (lp.width == LayoutParams.MATCH_PARENT) {//子view的寬是match_parent,則寬度期望值是總寬度-padding-margin final int width = Math.max(0, getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( width, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } //將屬性為match_parent的再測量一遍 final int childHeightMeasureSpec; if (lp.height == LayoutParams.MATCH_PARENT) { final int height = Math.max(0, getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( height, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } //子view的寬是match_parent,則寬度期望值是總寬度-padding-margin // //把這部分子view重新計算大小 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }
    protected void measureChildWithMargins(View child,
                                           int parentWidthMeasureSpec, int widthUsed,
                                           int parentHeightMeasureSpec, int heightUsed) {

        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        //根據parentWidthMeasureSpec,ViewGroup剩餘寬度,child的寬度重新計算child的widthMeasureSpec
        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);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    /**
     * @param spec           The requirements for this view
     *                       parentHeightMeasureSpec
     * @param padding        The padding of this view for the current dimension and
     *                       margins, if applicable
     *                       父親已經用的 + 父親的pading + 父親的margin
     * @param childDimension How big the child wants to be in the current
     *                       dimension
     * @return a MeasureSpec integer for the child
     * 孩子的佈局引數
     */
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

        //得到父親的測量模式和大小
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        //父親大小 - 父親的冗餘
        //當前父View還剩的空間大小
        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
            // Parent has imposed an exact size on us
            case MeasureSpec.EXACTLY:// 父View強制設定了一個大小給子View

                if (childDimension >= 0) {//大小就是 孩子引數的大小
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;

                } else if (childDimension == LayoutParams.MATCH_PARENT) {//大小就是 父親剩餘空間的大小
                    // Child wants to be our size. So be it.
                    resultSize = size;
                    resultMode = MeasureSpec.EXACTLY;

                } else if (childDimension == LayoutParams.WRAP_CONTENT) {//大小設定為父親剩餘空間大小,意味著讓子View去處理,子View最大的大小不能超過父View
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;

            // Parent has imposed a maximum size on us
            case MeasureSpec.AT_MOST:// 父View給子View一個限定的大小,子View不能超過這個大小

                if (childDimension >= 0) {// 子View的size設定成XML中配置的大小
                    // Child wants a specific size... so be it
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;

                } else if (childDimension == LayoutParams.MATCH_PARENT) {// 子View的大小為父View的size 意味著讓子View去處理,子View最大的大小不能超過父View
                    // Child wants to be our size, but our size is not fixed.
                    // Constrain child to not be bigger than us.
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;

                } else if (childDimension == LayoutParams.WRAP_CONTENT) {//同上
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;

            // Parent asked to see how big we want to be
            case MeasureSpec.UNSPECIFIED:// 意味著子View要多大都可以

                if (childDimension >= 0) {//子View的size設定成XML中配置的大小
                    // Child wants a specific size... let him have it
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;//意味著,我可以給讓你想顯示多大就顯示多大,但是在XML中或者addView的時候, 寫死了childDimension,那麼它的大小就這麼大吧

                } else if (childDimension == LayoutParams.MATCH_PARENT) {//意味著大小讓它自己決定到底要多大,隨便多大都可以
                    // Child wants to be our size... find out how big it should
                    // be
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;

                } else if (childDimension == LayoutParams.WRAP_CONTENT) {// 意味著大小讓它自己決定到底要多大,隨便多大都可以
                    // Child wants to determine its own size.... find out how
                    // big it should be
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                }
                break;

        }

        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);

    }