View框架之measure()流程

来源:互联网 发布:数据库unique 编辑:程序博客网 时间:2024/04/30 19:26

先来张View体系中measure()流程的时序图

注意:1.以下MeasureSpec用ms来代替(ms具体会在后面介绍)     2.该时序图描述的是从ViewRootImpl开始的measure()过程,因为ViewRootImpl是window与view连接的枢纽     3.该时序图着重于大概流程及重要方法的作用,具体代码细节会放到后面介绍     4.DecorView作为顶级View,本身是一个frameLayout,所以也是一个ViewGroup

这里写图片描述

MesureSpec简介 (以下简称ms)

在view的测量过程中,ms起着非常重要的作用,ms在某种程度上来说更像是一种测量的标准。ms是一个32位的int值,前2位代表测量模式,后30位代表测量大小测量模式(Specmode)一共分为3种:    UNSPECIFIED: 父容器不对子view限制,这种情况一般我们用不到,系统内部使用    EXACTLY :  父容器可以算出View的具体的大小,一般对应于 具体值或者match_parent    AT_MOST : 父容器指定一个值,子view不能超过这个值,一般对应于 wrap_content

View关于MesureSpec的计算

一般情况下,一个view的ms的测量是根据父容器的ms和本身的LayoutParams来决定的,根据一些规则,在父容器中算出ms,然后父容器调用此view的measure()方法将结果传给该view,该view接着会回掉onMeasure()将结果传给该view的实现类。  但是DecorView作为顶级View是没有父容器的,所以DecorView的ms是根据窗口的大小和自身的LayoutParams来决定的。

1.DecorView的ms计算过程:

--         /**           *     desireWindowWidth : 代表屏幕的宽带 ,同理,dwh 代表屏幕的高度          **/      private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,        final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {              *************************省略           //计算出DecorView的ms            ** childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);               childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); **             *************************省略}    /**    * 计算出decorView的ms*    **/  private static int getRootMeasureSpec(int windowSize, int rootDimension) {    int measureSpec;    switch (rootDimension) {    case ViewGroup.LayoutParams.MATCH_PARENT:        //如果设置的是math_parent,屏幕长度就是view长度,模式为EXACTLY        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);        break;    case ViewGroup.LayoutParams.WRAP_CONTENT:       //如果设置的是wrap_conentt,屏幕长度就是view长度,模式为AT_MOST        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);        break;    default:        // 如果设置的是具体的数值,这个值就是View的长度,模式威EXANTLY        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);        break;    }    return measureSpec;

2**.一般View的ms计算过程:**

 protected void measureChildWithMargins(View child,        int parentWidthMeasureSpec, int widthUsed,        int parentHeightMeasureSpec, int heightUsed) {    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();          //求出view的ms,           //第二个参数的意思 在父容器中最大可用大小    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);   //将测量好的ms,传递给子view    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}//根据父容器的ms和自身的LayoutParams求出该view的ms的具体操作 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);}

View及ViewGroup的measure()操作

一般view的measure()方法如下:public final void measure(int widthMeasureSpec, int heightMeasureSpec) {    boolean optical = isLayoutModeOptical(this);    final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec            || heightMeasureSpec != mOldHeightMeasureSpec;    final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY            && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;    final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)            && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);    final boolean needsLayout = specChanged            && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);        //判断view的ms与存好的是否一样,以及需要根据标志,需要重新测量吗    if (forceLayout || needsLayout) {        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;        }        // flag not set, setMeasuredDimension() was not invoked, we raise        // an exception to warn the developer        if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {            throw new IllegalStateException("View with id " + getId() + ": "                    + getClass().getName() + "#onMeasure() did not set the"                    + " measured dimension by calling"                    + " setMeasuredDimension()");        }        mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;    }

// 将ms值保存起来,为了提高性能,如果下次进来并且标志没变,就不会再重复执行onMeasure()
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;

    mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |            (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension}ViewGroup 的 measure(); View是一个容器,他不但要完成自己的测量方法,还需要遍历onMeasure()计算出下一层级的ms,下面以FrameLayout的onMeasure方法为讲解 @Overrideprotected 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++) {    //遍历计算childView的ms        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);            }            //调用子类的measure()方法进行传递            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);        }    }}
1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 服务器开机账号改了密码忘了怎么办 小米儿童电话手表开不开机怎么办? 上海拍牌超过5次违章怎么办 拍拍贷不能更新还不了款怎么办 起诉网贷平台不知道地址怎么办 网贷不知道在哪个平台借的怎么办 车过户后出现问题车主不承认怎么办 卖了车买家一直不过户怎么办 我的车卖了但是买家不过户怎么办 如果买家拍了赠品但联系不上怎么办 淘宝产品处罚下架顾客退货怎么办 生源地贷款续贷密码忘了怎么办 助学贷款续贷密码忘了怎么办 京东上回收东西如果是坏的怎么办 58.同城找的工作被骗了怎么办 京东第三方买了二手机怎么办 手机无法显示百度视频的视频怎么办 如果微信被盗号朋友钱被骗怎么办 绑定银行卡的电话号码换了怎么办办 银行卡绑定的手机号空号了怎么办 微信提示绑定银行卡次数超限怎么办 手机卡太久没用被注销了怎么办 电信宽带欠费缴费后上不了网怎么办 电信宽带欠费后缴费连不上网怎么办 电脑开不了机屏亮但不开机怎么办 手机信息探探链接点开了怎么办 买的钻戒的票丢掉了怎么办 如果我过户了原来的积分怎么办? 英雄联盟安装到了一半卡住了怎么办 微信登录不上怎么办一直在转圈 lol老是忘了放装备技能怎么办 英雄联盟屏幕出现红框锁定了怎么办 钢三开局修改对电脑有用怎么办 我的世界为什么一直黑屏闪退怎么办 苹果6plus玩游戏闪退怎么办 电脑重置开机黑屏了怎么办才好? 龟头有一小块和鱼鳞一样脱皮怎么办 海盗来了赠送碎片密码忘了怎么办 王者荣耀还差几百金币买英雄怎么办 英雄联盟更新后画面突然很卡怎么办 苹果手机微信登陆没反应怎么办