美文网首页
UI绘制流程(三)

UI绘制流程(三)

作者: 又是那一片天 | 来源:发表于2017-12-06 14:12 被阅读0次

    UI绘制流程的起始点 ViewRootImpl#performTraversals()方法中:

    此方法里分别调用了:
    ///测量
    performMeasure()
    // 摆放布局
    performLayout()
    // 绘制
    performDraw()

    这也是我们自定义UI布局时注意的过程 : 测量(Measure)—>布局(Layout)—>绘制(Draw)


    Measure测量过程:

    1. 通过getRootMeasureSpec(int windowSize, int rootDimension)方法传入父容器windowSize(具体数值) rootDimension (LayoutParams.(width|height))获取子view宽高测量模式;
    具体代码:
     private static int getRootMeasureSpec(int windowSize, int rootDimension) {
            int measureSpec;
            switch (rootDimension) {
    
            case ViewGroup.LayoutParams.MATCH_PARENT:
                // 窗口不能调整大小。强制根视图为windowSize
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
                break;
            case ViewGroup.LayoutParams.WRAP_CONTENT:
                // 窗口可以调整大小。设置根视图的最大大小。
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
                break;
            default:
                // 窗口想成为一个确切的大小。强制根视图是那个大小。
                measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
                break;
            }
            return measureSpec;
        }
    
    MeasureSpec:

    在Measure流程中,系统将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,在onMeasure中根据这个MeasureSpec来确定view的测量宽高

    测量模式:
    • EXACTLY :父容器已经测量出所需要的精确大小,这也是childview的最终大小------match_parent,精确值
    • ATMOST : child view最终的大小不能超过父容器的给的------wrap_content
    • UNSPECIFIED: 不确定,源码内部使用-------一般在ScrollView,ListView
    MeasureSpec里通过和一个数值M size 模式 与或非运算 得到一个数值(测量模式值) 而后通过M进行一些运算可以拿到父容器 size 模式
    2. 调用performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) 两个参数是根据父容器的宽高测量模式 ,在方法中 调用 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)又在这个方法里调用了onMeasure(int widthMeasureSpec, int heightMeasureSpec) 而 mView 为DecorView 而,DecorView继承FrameLayout ,onMeasure方法在FrameLayout被重写了,所以最终调用的是FrameLayout onMeasure方法;
    3. FrameLayout onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法:
    容器view

    1.获取子view数并遍历;
    2.遍历过程 获取view child 判断 child.getVisibility() != GONE 时调用measureChildWithMargins()方法测量子view
    遍历代码:

     for (int i = 0; i < count; i++) 
    
                final View child = getChildAt(i);//获取子view
    
                if (mMeasureAllChildren || child.getVisibility() != GONE) {//判断GONE 是GONE不用测量
    
                    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);//测量child以及它的子view
    
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();//获取view LayoutParams 
                    //获取所有子view中最大的宽或高
                    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);
                        }
                    }
                }
            }
    

    2.1.measureChildWithMargins()方法中传入了 child:子view, parentWidthMeasureSpec:父容器宽模式 parentHeightMeasureSpec:父容器高模式 等
    2.2.measureChildWithMargins()具体执行为 代码:

        protected void measureChildWithMargins(View child,  int parentWidthMeasureSpec, int widthUsed,  int parentHeightMeasureSpec, int heightUsed) {
            //获取子view MarginLayoutParams
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
           //根据子view宽或高对应的  父容器模式  Padding Margin width 获取该控件的测量模式
            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);
             //继续测量子view 的子view 直到视图view为止
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    
    1. 遍历完成之后获取了子view中最大宽 或 高 调用setMeasuredDimension()方法为该view设置宽高
    setMeasuredDimension()后才可以获得view的宽高

    小结:ViewGroup遍历测量Child三方法 自定义中使用:

    • measureChildWithMargins // 有Margin测量
    • measureChild// 测量这个view 没有Margin测量 自己遍历
    • measureChildren//遍历所有子view完成没有Margin测量
    视图view

    根据view设置内容 或呈现内容 来完成测量
    setMeasuredDimension()调用完成测量

    测量总结 自定义View,ViewGroup:

    View:
    • 套路:根据父容器传来的测量模式确定view宽高 以及自己内容 最终调用setMeasuredDimession方法来保存自己的测量宽高
                final int specMode = MeasureSpec.getMode(measureSpec);
                final int specSize =  MeasureSpec.getSize(measureSpec);
                switch (specMode) {
                case MeasureSpec.UNSPECIFIED:
                 
                    break;
                case MeasureSpec.AT_MOST:
                    
                    break;
                case MeasureSpec.EXACTLY:
                //完成宽高测量
                    break;
            }
           setMeasuredDimension(width, height);
    
    ViewGroup
    • 1、测量子view的规格大小 measureChildWithMargins measureChild measureChildren等方法
    • 2、通过子view的规格大小来确定自己的大小 setMeasuredDimession

    Layout测量过程:

    大概过程是Measure一样

    ViewGroup

    重写onLayout() 根据里要的样式计算每个view left, top, right, bottom 在调用子view layout(left, top, right, bottom)方法完成布局

    相关文章

      网友评论

          本文标题:UI绘制流程(三)

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