View的measure

作者: 一个冬季 | 来源:发表于2017-11-12 14:30 被阅读19次

    昨天爬了一天的山~,我的脚已经崩溃了,嘻嘻不过还是挺开心的,今天我们要总结下View的measure(测量)ViewGroup的measure
    我们平常继承View 和ViewGroup 这2个方法呢,都是会有一个测量的步骤的,如果只是一个View那么measure完了就完了,如果是ViewGroup不仅自己要测量下,自己的子View也要测量下。
    View里面的measure

     public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
            boolean optical = isLayoutModeOptical(this);
           .....省略
            if (forceLayout || needsLayout) {
                mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
                resolveRtlPropertiesIfNeeded();
                int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
                if (cacheIndex < 0 || sIgnoreMeasureCache) {
                          //我们直接看这里,因为这里是设置布局大小的地方
                    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;
                }
          .....省略
    
    
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    //setMeasuredDimension 方法直接设置宽高了
        }
    

    getDefaultSize方法主要返回的是一个大小值(宽度和高度)

    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;
        //不管你是wrap还是match最后的结果由specSize决定
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            }
            return result;
        }
    

    UNSPECIFIED测量模式我们先不理它,其它2种的测量模式最后的结果都是specSize,就是View测量后的大小
    我们这里面还有一个getSuggestedMinimumWidth()和getSuggestedMinimumHeight()2个方法,我们看其中一个

     protected int getSuggestedMinimumWidth() {
            return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
        }
    //如果backgroud(背景)==null,我们就取mMinWidth,否则我们就去后面2个比较的那个最大值
    //这里的mMinWidth==android:minWidth,当然如果我们没有去指定的话,mMinWidth=0了
    

    我们点击mBackground.getMinimumWidth()这个方法看

    public int getMinimumWidth() {
            final int intrinsicWidth = getIntrinsicWidth();
            return intrinsicWidth > 0 ? intrinsicWidth : 0;
        }
    

    现在我们知道了一个View不管你设置wrap还是match,最后的结果都是由
    specSize 它决定,现在有一个问题来了,当我们自定义View的时候,我们平常写宽度/高度为wrap,我们会在脑海中想象他是个包裹的状态,但是当我们真正去实验的时候,发现他却是全屏的状态(match),这个就让我们很诧异,很疑惑?。
    现在我们目前只知道2点
    1、我们不管是wrap还是match最后的结果都是由specSize决定你的大小(代码可以看出来)
    2、Linlayout在绘制布局的时候会调用measureChildWithMargins方法,在这个方法里面会调用child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    当然第二点我们后面再讲到
    如果你看过我对MeasureSpece的总结的话你应该就知道了

    3.png
    我们可以明白这一点,Linlayout(父布局)决定了子布局的大小,本文中specSize的大小由父容器来给你决定,他决定你你有多大的使用空间(父容器剩余的空间),我们可以通过上面的图片知道,如果我们的 childLayoutParams的参数是wrap_content那么我们不管父容器是wrap还是match,子容器的测量模式都是AT_MOST,但是它的宽高大小都是parentSize的大小,也就是父容器的大小(你要明白这一点,你需要看我前面的学习总结)
    下面这个例子很好的展示了当子容器是wrap的时候,沾满全屏的情况
    1.png
    现在大家看到的这个效果是一个Linlayout,嵌套一个自定义的view
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <scanning.mobile.com.viewstudy.viewStu.st
            android:background="@color/colorAccent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    

    但是我嵌套里面的宽/高都是wrap_content,但是我们却看到的是沾满全屏了
    这个是为啥?我们可以采用刚刚我们学到的知识来解释,
    我们拿着我们刚刚参考那张表格来看
    首先父容器是match_parent,那么对应的测量模式是:EXACTLY(精确模式),子容器是wrap_content,那么它对应的模式就是AT_MOST

            int specMode = MeasureSpec.getMode(spec);
            int specSize = MeasureSpec.getSize(spec);//父容器的大小
            int size = Math.max(0, specSize - padding);//获取剩下的空间
    else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
    

    我们知道我们最后的size就是Math.max(0, specSize - padding);//获取剩下的空间,
    所以我们就是父容器的大小了,那么问题了来了,我们要如何来解决这样的问题?

    public class st extends View{
        private static final String TAG = st.class.getSimpleName();
        public st(Context context) {
            super(context);
        }
    
        public st(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public st(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
        int height=200;
        int width = 200;
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    
            if (widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){//他会进入这里,所以我们在这里直接来解决
                setMeasuredDimension(width,height);
            }else if (widthSpecMode==MeasureSpec.AT_MOST){
                setMeasuredDimension(width,heightSpecSize);
            }else if (heightSpecMode == MeasureSpec.AT_MOST){
                setMeasuredDimension(height,widthSpecSize);
            }
        }
    }
    

    获取它的测量模式,根据测量模式来修改


    `{~3GBS2$20GCFYV$0W)%FO.png

    学习资源书籍:《Android 开发艺术探索》

    相关文章

      网友评论

        本文标题:View的measure

        本文链接:https://www.haomeiwen.com/subject/vswnmxtx.html