浅谈Android之Activity 窗口显示流程介绍(一)

7 Activity 窗口显示流程介绍

Activity 窗口显示,其实就是Décor View绘制并显示的过程,但是在绘制之前,Décor View需要先做下面两件事情:

1)  确定Décor View的大小

2)  对Décor View进行布局操作,也就是确定DécorView所有child views的显示位置

由于Décor View的LayoutParams中宽高默认设置的是MatchParent,所以Décor View初始状态下,肯定是想要塞满整个屏幕的,也就是宽高都是屏幕的尺寸,但是想归想,实际上真正能拿到多大的区域,这个还是要看WMS这个大总管怎么分配了

简单点说就是,Décor View可以告知WMS窗口想要显示的大小,但是WMS会根据状态栏,输入法窗口是否显示等等,来对这个大小做适当的裁剪,然后将最终结果告知到Décor View,

我们可以称这个过程叫Décor View显示大小确认,这个是通过调用WindowSession. Relayout来完成的

5.1 Activity Base Window介绍中已经介绍过,上面窗口显示的这些操作,都是由

ViewRootImpl. performTraversals来触发完成的,接下去通过代码做简单介绍

7.1 Activity窗口大小获取

ViewRootImpl.performTraverals函数完整的代码太长了,这里就不全部贴出,接下去分析时会贴出局部代码片段来介绍

首先看ViewRootImpl定义的三个变量:

final Rect mWinFrame; // frame given by window manager.

int mWidth;

int mHeight;

mWinFrame,mWidth和mHeight保存的,都是WMS返回的窗口大小,如果是第一次运行(mFirst=True), 这三个值默认都为空,如果不是第一次运行,则为当前窗口的显示大小

接下去设置Activity Window的desire rectangle:

  Rect frame = mWinFrame;

        if (mFirst) {

            mFullRedrawNeeded = true;

            mLayoutRequested = true;

            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL

                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {

                // NOTE -- system code, won't try to do compat mode.

                ……

            } else {

                DisplayMetrics packageMetrics =

                    mView.getContext().getResources().getDisplayMetrics();

                desiredWindowWidth = packageMetrics.widthPixels;

                desiredWindowHeight = packageMetrics.heightPixels;

            }

            ……

        } else {

            desiredWindowWidth = frame.width();

            desiredWindowHeight = frame.height();

            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {

                mFullRedrawNeeded = true;

                mLayoutRequested = true;

                windowSizeMayChange = true;

            }

        }

如果mFirst为true,将desiredWindowWidth和desiredWindowHeight设置为屏幕宽高,否则就将其设置为mWinFrame中保存的当前窗口的大小值

接着基于desiredWindowWidth和desiredWindowHeight对Décor View做一次measure,确认Décor View的大小:

  boolean layoutRequested = mLayoutRequested && !mStopped;

        if (layoutRequested) {

            final Resources res = mView.getContext().getResources();

            ……

            // Ask host how big it wants to be

            windowSizeMayChange |= measureHierarchy(host, lp, res,

                    desiredWindowWidth, desiredWindowHeight);

}

Décor View的大小确认后,这个只不过是Activity希望请求到的窗口大小,接着还需要调用relayoutWindow-> mWindowSession.relayout并传入请求的大小,然后WMS会根据当前窗口情况返回最终分配的窗口大小,比如,现在有一个输入法窗口已经弹出,并且Activity设置的windowSoftInputMode为adjustResize,在这种情况下,Activity期望的高度是肯定无法满足的,WMS会返回减去输入法窗口的高度,并将结果返回到Activity

接着看mWindowSession.relayout的调用:

int relayoutResult = mWindowSession.relayout(

                mWindow, mSeq, params,

                (int) (mView.getMeasuredWidth() * appScale + 0.5f),

                (int) (mView.getMeasuredHeight() * appScale + 0.5f),

                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,

                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,

                mPendingStableInsets, mPendingConfiguration, mSurface);

mWindow是对应的窗口,第三和第四个参数就是窗口期望的宽和高,这里加上0.5f用以实现四舍五入,WMS那边调整后的宽高数据会返回到mWinFrame中

mWinFrame保存的就是这次Activity窗口显示的最终大小,接着保存数据到mWidth和

mHeight:

  if (mWidth != frame.width() || mHeight != frame.height()) {

                mWidth = frame.width();

                mHeight = frame.height();

  }

接着重新设置ViewRootImpl关联的Surface的frame size,以便调整graphic buffer size:

  if (mSurfaceHolder != null) {

                // The app owns the surface; tell it about what is going on.

                if (mSurface.isValid()) {

                    // XXX .copyFrom() doesn't work!

                    //mSurfaceHolder.mSurface.copyFrom(mSurface);

                    mSurfaceHolder.mSurface = mSurface;

                }

                mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);

}

最后,如果WMS返回的最终窗口大小和之前通过desire Width和desire height测量出来的décor view的mesure width和height不一致,那décor view需要基于最终的窗口大小再次做测量操作:

if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()

                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {

     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);

     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

     ……

     // Ask host how big it wants to be

     performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

     ……

}

Activity窗口大小的获取介绍结束,接下去介绍Décor View是怎么测量自身以及childviews.

7.2 Activity Décorview大小测量(measure)

上头调用performMeasure触发measure操作,对每个View的宽高来说,必须设置 Measure Mode,主要有三个mode:

Mode name

Desc

MeasureSpec.AT_MOST

对应WRAP_CONTENT

MeasureSpec.EXACTLY

对应MATCH_PARENT

MeasureSpec .UNSPECIFIED

未定义

由于layout默认必须要配置View的宽高属性,所以MeasureSpec .UNSPECIFIED基本是用不到的

如果要调用view.measure对某个view执行measure操作,必须要传入宽高以及对应的measure mode值,android将宽/高的值以及measure mode合并存储到int里头,具体的数据分布是这样的:


Int的0-30位保存的是宽高的值,30-32位保存measure mode

对此,Android专门定义了MeasureSpec辅助类用于将measuremode和value合并成int,或者从int里头取出value或者measure mode:

Method name

Desc

MeasureSpec. makeMeasureSpec

基于value和measure mode合成measure spec

MeasureSpec.getMode

从measure spec取出高2位的值,也就是mode的值

MeasureSpec.getSize

从measure spec中取出低30位的值,也是就宽/高的值

先来看下ViewRootImpl中getRootMeasureSpec的实现:

  private static int getRootMeasureSpec(int windowSize, int rootDimension) {

        int measureSpec;

        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:

            // Window can't resize. Force root view to be windowSize.

         measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);

            break;

        case ViewGroup.LayoutParams.WRAP_CONTENT:

            // Window can resize. Set max size for root view.

       measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);

            break;

        default:

            // Window wants to be an exact size. Force root view to be that size.

            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);

            break;

        }

        return measureSpec;

 }

从函数中可以看出,MATCH_PARENT对应MeasureSpec.EXACTLY,WRAP_CONTENT则对应

MeasureSpec.AT_MOST,并且,不管是MeasureSpec.EXACTLY还是MeasureSpec.AT_MOST,其对应的value都是the max size of parentview.

接下去看performMeasure:

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {

        try {

            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

        } finally {

            Trace.traceEnd(Trace.TRACE_TAG_VIEW);

        }

}

直接调用décor view的measure函数,接下去看viewmeasure的完整流程:

由于DecorView是一个FrameLayout,所以接下去专门分析下FrameLayout的measure代码,它先调用DecorView.measure,由于DecorView没有实现这个函数,所以最终调用View.measure:

  public final void measure(int widthMeasureSpec, int heightMeasureSpec) {

        ……

        // Suppress sign extension for the low bytes

        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;

        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);

        final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;

        final boolean isExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY &&

                MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;

        final boolean matchingSize = isExactly &&

                getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) &&

                getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);

        if (forceLayout || !matchingSize &&

                (widthMeasureSpec != mOldWidthMeasureSpec ||

                        heightMeasureSpec != mOldHeightMeasureSpec)) {

            // first clears the measured dimension flag

            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;

            resolveRtlPropertiesIfNeeded();

            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);

            if (cacheIndex < 0 || sIgnoreMeasureCache) {

                // measure ourselves, this should set the measured dimension flag back

                onMeasure(widthMeasureSpec, heightMeasureSpec);

                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

            } else {

                long value = mMeasureCache.valueAt(cacheIndex);

                // Casting a long to int drops the high 32 bits, no mask needed

                setMeasuredDimensionRaw((int) (value >> 32), (int) value);

                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

            }

            ……

            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;

        }

        mOldWidthMeasureSpec = widthMeasureSpec;

        mOldHeightMeasureSpec = heightMeasureSpec;

        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |

                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension

    }

mPrivateFlags变量基于位运算保存各种标签值,比如PFLAG_MEASURED_DIMENSION_SET,用于标识view是否已经measure;mOldWidthMeasureSpec和mOldHeightMeasureSpec保存上一次请求完成的measure spec;forceLayout标明这次measure是否是由View.requestLayout触发的,如果为true,则强制执行onMeasure

如果measure行为不是由View.requestLayout触发的,也就是forceLayout为false,那只有如下两种情况,会触发View调用onMeasure做重新测量:

1)  Measure mode不全是MeasureSpec.EXACTLY,也就是说,宽和高有一个或者全部被设置成WRAP_CONTENT,这种情况下,isExactly就会为false,还有就是widthMeasureSpec !=mOldWidthMeasureSpec ||

heightMeasureSpec!= mOldHeightMeasureSpec 为true,最后就是mMeasureCache不存在缓存数据

2)  Measure mode全是MeasureSpec.EXACTLY,也就是时候,宽和高都被设置成了

MATCH_PARENT,然后measuare spec里设置的宽高跟getMeasuredWidth()和

getMeasuredHeight()的不一致,这个时候matchingSize肯定为false,

widthMeasureSpec!= mOldWidthMeasureSpec和

heightMeasureSpec!= mOldHeightMeasureSpec肯定也为true,最后同样的,mMeasureCache不存在缓存数据

总结下,如果反过来说呢,如果measure被重复调用,什么情况下onMeasure不会被重复执行:

1)  不是通过requestLayout触发的

2)  如果widht和height mode都是MATCH_PARENT,并且请求高度和宽度无变化,那这次请求就被会忽略

3)  如果width或者height存在一个mode为WRAP_CONTENT,那就要看请求measure spec是否是一致的,如果一致,那这次请求会被忽略

上面介绍的是onMeasure的入口控制,避免不必要的measure被重复执行进而影响系统运行效率,接下去介绍onMeasure的内部实现

我们都知道整个View是一个树形结构,这个结构的顶端是Décor View,每一个树节点只有两种类型,一个是leaf view,还有就是ViewGroup,leaf view是末端节点,由于其不包含childview,其内部只处理自身逻辑就好了,相对来说会比较简单;ViewGroup则不一样,因为它是View的群组,那它肯定就包含child view,child view可能是leaf view或者View Group,相对来说就会复杂点。

所以,如果要从Décor View遍历整个树形数据,最重要就是ViewGroup的处理,因为它起到承上启下的作用。

Android定义的ViewGroup有很多,比如RelativeLayout,LinearLayout,FrameLayout,每个ViewGroup的最大的不同之处,表现在对childview的measure,layout的处理

由于Décor View是一个FrameLayout,所以接下去重点介绍FrameLayout.onMeasure实现,其余几个ViewGroup大家可自行研究

由于FrameLayout所有的child view的位置都是相对于FrameLayout的左上角坐标原点来放置的,如果FrameLayout定义的宽高为MATCH_PARENT,那所有child view所能达到的最大宽高就是FrameLayout的宽高,反之,如果FrameLayout的宽高定义为WRAP_CONTENT,那FrameLayout的宽高则会被设置成所有child view中size最大的那个。

接下去看代码:

//FrameLayout.java

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int count = getChildCount();

        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) {

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

                if (measureMatchParentChildren) {

                    if (lp.width == LayoutParams.MATCH_PARENT ||

                            lp.height == LayoutParams.MATCH_PARENT) {

                        mMatchParentChildren.add(child);

                    }

                }

            }

        }

        // Account for padding too

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

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

                int childWidthMeasureSpec;

                int childHeightMeasureSpec;

               

                if (lp.width == LayoutParams.MATCH_PARENT) {

                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -

                            getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin,

                            MeasureSpec.EXACTLY);

                } else {

                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,

                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin,

                            lp.width);

                }

               

                if (lp.height == LayoutParams.MATCH_PARENT) {

                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -

                            getPaddingTopWithForeground() - getPaddingBottomWithForeground() -

                            lp.topMargin - lp.bottomMargin,

                            MeasureSpec.EXACTLY);

                } else {

                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,

                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +lp.topMargin + lp.bottomMargin,

                            lp.height);

                }

                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            }

        }

}

measureMatchParentChildren如果为true,说明FrameLayout存在宽/高定义为

WRAP_CONTENT,对于同样是WRAP_CONTENT的child view没问题,但是对于那些

MATCH_PARENT的child view,则需要在FrameLayout的宽/高确定后,需要再一次执行measure操作

函数先循环遍历all child views,然后调用measureChildWithMargins测量child view的宽高,然后通过view的measure width加上对应的margin size得到其在FrameLayout中需要占用的实际大小,然后将allchild views中,宽度和高度最大的保存到maxWidth和maxHeight中

如果measureMatchParentChildren为true,还需要将宽高为MATCH_PARENT的child view保存到mMatchParentChildren中

接着将得到的maxWidth和maxHeight再加上FrameLayout的padding数值,然后再通过

getForeGround拿到fore ground drawable,如果drawable的最小宽高比maxWidth和maxHeight要大,则将其作为maxWidth和maxHeight的大小

maxWidth和maxHeight到目前为止,都是基于childview的数据加上frameLayout的padding和foreground数据算出的,这个可以做为frameLayout的measure result?当然不行,因为child view给出的数据,有可能会超过framelayout对应measure spec的值,所以还需要调用

resolveSizeAndState做调整:

//View.java

public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {

        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:

            if (specSize < size) {

                result = specSize | MEASURED_STATE_TOO_SMALL;

            } else {

                result = size;

            }

            break;

        case MeasureSpec.EXACTLY:

            result = specSize;

            break;

        }

        return result | (childMeasuredState&MEASURED_STATE_MASK);

 }

这个函数很简单,如果FrameLayout设置的是AT_MOST,即WRAP_CONTENT,如果size比specSize小,那就用size,反之则用specSize;如果FrameLayout设置的是EXACTLY,也就是MATCH_PARENT,则直接设置成specSize

也就是说maxWidth和maxHeight的值,只在宽高为WRAP_CONTENT并且maxWidth和

maxHeight值比对应measure spec的value值小时才有意义

接着调用setMeasuredDimension设置resolveSizeAndState返回的宽高结果

到这里,FrameLayout的measure width/height确定后,对宽高为MATCH_PARENT的child view再一次执行measure操作,由于FrameLayout的measure width/height已定,这里再得到child view可占用的最大宽高就很简单,直接减去FrameLayout的padding和child view的margin值就可以了。

接下去回过头来分析measureChildWithMargins的实现,由于FrameLayout没有实现该函数,所以调用ViewGroup中的实现:

//ViewGroup.java 

protected void measureChildWithMargins(View child,

            int parentWidthMeasureSpec, int widthUsed,

            int parentHeightMeasureSpec, int heightUsed) {

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

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

    }

通过调用getChildMeasureSpec得到child view的measure spec,这个函数传入是第一个参数是parent measure spec,第二个参数是padding,对应FrameLayout的padding值加上child view的margin值再加上已经使用的宽/高度值,由于FrameLayout所有child view都是基于左上角坐标的,所以这里widhtUsed和heightUsed都是0,最后一个参数是layout中设置的widht和height值

接着看getChildMeasureSpec的实现:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

        int specMode = MeasureSpec.getMode(spec);

        int specSize = MeasureSpec.getSize(spec);

        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:

            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) {

                // 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:

            if (childDimension >= 0) {

                // Child wants a specific size... so be it

                resultSize = childDimension;

                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.MATCH_PARENT) {

                // 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:

            if (childDimension >= 0) {

                // Child wants a specific size... let him have it

                resultSize = childDimension;

                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.MATCH_PARENT) {

                // Child wants to be our size... find out how big it should

                // be

                resultSize = 0;

                resultMode = MeasureSpec.UNSPECIFIED;

            } else if (childDimension == LayoutParams.WRAP_CONTENT) {

                // Child wants to determine its own size.... find out how

                // big it should be

                resultSize = 0;

                resultMode = MeasureSpec.UNSPECIFIED;

            }

            break;

        }

        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);

    }

这个函数先拿到FrameLayout的measure spec,然后再将measurespec的value减去padding保存到size中,其即为child view的desire size

这个函数其实就处理两种情况

1)  如果FrameLayout的measure spec为EXACTLY,接着判断child view的width/height值;如果值大于0,说明其是一个确定的值,直接将其值保存到resultSize并且将resultMode保存为EXACTLY就可以了;如果值为MATCH_PARENT或者WRAP_CONTENT,则将result设置为size然后对应的值保存到resultMode就好了

2)  如果FrameLayout的measure spec为AT_MOST,对于child view的width/height值大于0或者WRAP_CONTENT,处理跟上面都是一样的;唯一不同的是,当值为MATCH_PARENT时,由于FrameLayout这个parent view在目前measure width/height都未确定,作为child view,你这里MATCH_PARENT就没任何意义,所以这里会把result mode改成AT_MOST

函数中调用child.measure执行child view的measure操作,也就是本节介绍的内容,如此反复,直到所有child view都measure完毕为止

发布了46 篇原创文章 · 获赞 25 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/zhejiang9/article/details/55096654