view的绘制流程

先顺带提一下LayoutInflater

layoutInflater.inflate(resourceId, root,attachToRoot):

 resourceId传入要加载的xml布局root如果不为空,就相当于给这个布局外部嵌套一层root父布局;attachToRoot在两个参数的时候默认为true,如果设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。

inflate 三个参数说明见: 三个案例带你看懂LayoutInflater中inflate方法两个参数和三个参数的区别__江南一点雨的博客-CSDN博客

在inflate方法中,采用了PULL解析方法来解析XML,首先会创建XmlPullParser对象并通过next()方法不断地遍历节点直到遍历完全部节点,根据解析出来的节点名来创建View对象(通过createViewFromTag这个api);

Q1:invalidate和postInvalidate方法区别:

都是去刷新view,其中invalidate只能在UI线程中使用(如果子线程操作可用handler处理),而postInvalidate可在非UI线程中使用。 

绘制流程:

在onResume执行后,会创建一个ViewRootImpl然后将Decorview交给它进行管理,DecorView底下的view逻辑事件都是ViewRootImpl来管理;ViewRootImpl实现了ViewParent接口,包含了requestLayout方法(内部包含了两步,检查是否是UI线程和调用performTraversals方法),所以当View的ViewParent都是同一个,可以理解为这些View都在View链上,invalidate的时候会将整个Viewtree刷新一遍;   View的绘制流程是从ViewRootImpl的performTraversals方法开始,依次调用measure。layout,draw,将整个Viewtree进行遍历执行这三个方法;

关于View执行onMeasure,onLayout的次数>

分析ViewRootImpl的源码,scheduleTraversales()内部会执行postCallBack触发mTraversalRunnable重新走一遍performTraversals(),第二次执行performTraversals()就会触发performDraw()。所以performTraversals()走了两次,那么肯定会走2次measure方法,但不一定走2次onMeasure(),读过View measure方法源码的都应知道measure方法做了2级测量优化:

  • 1.如果flag不为forceLayout或者与上次测量规格(MeasureSpec)相比未改变,那么将不会进行重新测量(执行onMeasure方法),直接使用上次的测量值;
  • 2.如果满足非强制测量的条件,即前后二次测量规格不一致,会先根据目前测量规格生成的key索引缓存数据,索引到就无需进行重新测量;如果targetSDK小于API 20则二级测量优化无效,依旧会重新测量,不会采用缓存测量值

第二次走performTraversals逻辑:

即view可见时再重绘一次,同理layout、draw走两次,但各种限制条件导致onLayout和onDraw不一定也走两次;

1.OnMeasure:测量view的宽/高

2.OnLayout:决定view的四个顶点位置,以及拿到View的实际宽/高

3.OnDraw:绘制view, 绘制内容的步骤: ①、绘制视图的背景(drawBackground);②、保存画布的图层(Layer);③、绘制View的内容(onDraw);④、绘制View子视图,如果没有就不用(dispatchDraw);⑤、还原图层(Layer);⑥、绘制滚动条。

OnMeasure:

其中最重要的参数MeasureSpec,是一个32位int型,高2位是specMode,低30位是specSize;

通过父类的measureSpec,由specSize和specMode共同组成的,specSize来获取子类的size;

三种specMode:

EXACTLY:精准大小,表示父视图希望子视图大小由specSize决定或者由开发人员自己设置任意某个数值

AT_MOST:不超过父类;表示子视图最大只能小于specSize指定的数值,系统默认按照这个规则设置,开发人员也可以自己设置任意小于specSize数值;

UNSPECIFIED:可将试图按照自己意愿设置成任意大小;

内部是通过判断传入xml中的参数来产生specMode:

Match_Parent或者具体数值   --->   EXACTLY

WRAP_CONTENT  ---> AT_MOST

onMeasure方法最后会在getDefaultSize中通过判断specMode来拿到具体size:

 但是注意一点的是:AT_MOST它是和EXACTLY一样,都会返回父View规定的size;如果开发者不想在使用WRAP_CONTENT时展现出和MATCH_PARENT的样子,那么就可以在onMeasure中自己拿到specMode进行判断和设置,比如设一个具体的数值(如下);

注意:上述wrap_content表现为Match_parent只是在view或继承view的自定义view才表现这样,如果是TextView、Button或其他控件,内部已经进行过处理:

因此TextView等控件并不会像View直接展现为match_parent,而是持有一个suggestionWidth来限定宽高

在拿到实际要设置的size大小后,如下图调用setMeasuredDimension(width,height),这样一次measure过程就结束了;

常见用法:

当然上面只是view的onMeasure流程,如果存在一个父容器ViewGroup包含了多个子viewViewGroup中定义了measureChildren()方法遍历测量包含的子view,每个子view的measure流程和上述view的流程相同:

onLayout:

layout()方法中接受了左上右下四个坐标位置,并用setFrame和onLayout()方法来确定view的位置;

然而View中onLayout是一个空方法,因为onLayout()是为了确定view在布局中所在的位置,所以应该由父容器来决定;所以只有ViewGroup像LinearLayout等中才实现了onLayout方法用来约束子view的位置;

用法举例:

1.先去measure判断是否包含子视图,有的话就量出它的大小;

2.layout方法中传入的四个参数依次是0、0、childView.getMeasuredWidth()和childView.getMeasuredHeight(),其中,调用childView.getMeasuredWidth()和childView.getMeasuredHeight()方法得到的值就是在onMeasure()方法中测量出的宽和高

问题Q1:getMeasureWidth和getWidth区别?

getMeasureWidth拿到的是onMeasure时setMeasuredDimension方法进行设置的值, 在measure()过程结束后获取到;

getWidth是在onLayout时传入的右边界减去左边界的值, 要在layout()过程结束后获取到;

所以如果处理了setMeasuredDimension和onLayout传左右边界的值,就可能会让getMeasureWidth和getWidth不同;

Q2:什么时候能拿到view的高度?如果是动态变的view如何拿到最终的view高度?

如上是自定义view类中执行的方法顺序,可以看到view的宽高确定是在onMeasure后得到的,如果是动态的view会在onSizeChanged中getMeasuredHeight()拿到最终确定的宽高;

此外,如何在onCreate中拿到View的宽度和高度

  • View.post(runnable)
view.post(new Runnable() {            
            @Override
            public void run() {
                int width = view.getWidth();
                int measuredWidth = view.getMeasuredWidth();
                Log.i(TAG, "width: " + width);
                Log.i(TAG, "measuredWidth: " + measuredWidth);
            }
        });

利用Handler通信机制,发送一个Runnable到MessageQueue中,当View布局处理完成时,自动发送消息,通知UI进程。借此机制,巧妙获取View的高宽属性,代码简洁,相比ViewTreeObserver监听处理,还不需要手动移除观察者监听事件。

XML中,设置padding值失效,因为默认是不生效的,只能自己在onDraw中实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值