View 的绘制流程

 View 的基本原理

绘制流程

当一个activity对象创建完成,会将一个DecorView添加进Window中,同时创建ViewRoot的实现对象ViewRootImpl与之关联,ViewRootImpl会调用perFromTraVersals来进行View 的绘制过程。

Measure

测量每个控件的大小;

当一个view开始时,肯定会是从DecorView 依次往下调用measure来测量的。

测量之后根据getMeasureWidth()和。。来获取View 的测量后的宽和高,几乎等于最终的宽高,但是也有例外:
- view.getMeasuredHeight() 是由view中的测量方法赋值的,这个值包含了隐藏的高度(比如一个view部分超出屏幕,他也会计算出来)
- view.getHeight() 由view的底部位置减去顶部位置,即实际显示的View的高度,不包含隐藏了的高度

其实在layout过程中决定了View的四个点,和实际的宽和高,可以通过getTop,getBottom,getLeft,getRight来获取四个点的位置,并且可以使用getWidith,getHeight来获取实际的宽和高。

measure过程

说道测量过程就必须得说MeasureSpec:
- 32位int值
- 高2位是测量模式,低30位是这个测量模式下的规格
- Exactly:父容器已经根据参数检测出了View的大小(比如,如果View设置了match_perent或者具体的多少px那么这就是View 的最终大小。)
- AT_Most: 表示父容器指定了一个大小可用的数值,记录在SpecSize中,View 不能大于它,但是具体的值不知道,对应View的wrap_Content。(源码中并没有实现)
- Unspecified:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。

DecorView测量的大小由自身的layoutParams和窗口大小决定,具体逻辑在getRootMeasureSpec决定,如果是具体值或者是match_parent就是精准模式,如果是wrap_content 就是最大模式。
普通的View:measure实际上是由父元素进行便利调用的,父元素调用子View的measure之前使用getChildMeasureSpec来转化得到子View的MeasureSpec,总结:
- View的大小与父元素的SpecMode和自身的参数有关系
- 如果View 参数是具体值,父元素不管是什么mode,子元素都是Exactly模式大小是View的参数大小;
- View的参数是match_parent那么父元素是什么模式他就是什么模式,但是如果View也是精准模式,那么大小就是父元素剩余大小。
- wrap_content:View的模式就是最大话模式,但不能超过父容器的剩余空间;

View 的measure过程:
Viewz自身的onMeasure方法就是吧MeasureSpec和size设置为最终的测量结果,这样测量问题就在于match_parent和Wrap_content 的结果是一样的(wrap_content的size就是最大可用size)

ViewGrop的测量过程:
他是一个抽象类,没有onMeasure方法,他mearsue时会便利调用MeasureChild,

流程

这里写图片描述

Layout

Layout的作用ViewGroup来确定子元素的位置,当ViewGroup的位置被确定以后,(在Layout方法中) 它就在自己的onLayout方法中遍历所有的子元素,并调用layout方法,来确定子元素的位置, 对于子元素,Layout中又会调用其onLayout方法。
View和ViewGroup都没有实现真正实现onLayout方法,但是View和ViewGroup的Layout方法是一样的,作用都是确定自己的位置,layout方法通过调用setFrame()方法来设定View的四个点,这样就确定了View 在父元素中的位置了。

小知识点
- isLayoutModeOptical() 用来判断是否使用视觉边界布局效果(就是让布局显得好看整齐)
- setOpticalFrame()内部也是调用了setFrame()
- 在layout()方法内部刚开始执行的时候,首先会根据mPrivateFlags3变量是否具有标志位,如果具有标志位PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT,则执行onMeasure()方法,从而对View进行量算,量算的结果会保存到View的成员变量中
- getMeasuredHeight和getHeight区别:在measure之后就可以使用getMeasuredHeight来获取到测量的高,而layout过程后于measure,ViewGroup的setChildFrame会调用child的layout来确定真实的位置,如果layout不重写那么两个就一样,重写的话可能会不一样。

 流程

这里写图片描述

Draw

View的绘制过程遵循如下几步:
1. 绘制背景 background.draw(canvas)
2. 绘制自己(onDraw)
3. 绘制Children(dispatchDraw)
4. 绘制装饰(onDrawScrollBars)

绘制源码:

public void draw(Canvas canvas) {

// 所有的视图最终都是调用 View 的 draw ()绘制视图( ViewGroup 没有复写
此方法)
585View测量、布局及绘制原理
// 在自定义View时,不应该复写该方法,而是复写 onDraw(Canvas) 方法进行绘
制。
// 如果自定义的视图确实要复写该方法,那么需要先调用 super.draw(canvas)完
成系统的绘制,然后再进行自定义的绘制。
...
int saveCount;
if (!dirtyOpaque) {
// 步骤1: 绘制本身View背景
drawBackground(canvas);
}
// 如果有必要,就保存图层(还有一个复原图层)
// 优化技巧:
// 当不需要绘制 Layer 时,“保存图层“和“复原图层“这两步会跳过
// 因此在绘制的时候,节省 layer 可以提高绘制效率
final int viewFlags = mViewFlags;
if (!verticalEdges && !horizontalEdges) {
if (!dirtyOpaque)
// 步骤2:绘制本身View内容
默认为空实现,
自定义View时需
要进行复写
onDraw(canvas);
......
// 步骤3:绘制子View
默认为空实现 单一View中不需要实现,ViewG
roup中已经实现该方法
dispatchDraw(canvas);
........
// 步骤4:绘制滑动条和前景色等等
onDrawScrollBars(canvas);
..........
return;
}
...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值