UI绘制流程(三)

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)方法完成布局

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容