4.1 初识ViewRoot和DecorView
- ActivityThread#handleResumeActivity
- 当 Activity 对象被创建完毕后,会将 DecorView 通过 WindowManager 添加到 Window 中。
- WindowManagerGlobal#addView
- 创建 ViewRootImpl 对象,并将 ViewRootImpl 对象和 DecorView 建立关联。
- ViewRootImpl#setView
- ViewRootImpl#requestLayout
- ViewRootImpl#scheduleTraversals
- ViewRootImpl.TraversalRunnable#performTraversals
- ViewRootImpl#performMeasure
- ViewRootImpl#performLayout
- ViewRootImpl#performDraw
4.2 理解MeasureSpec
4.2.1 MeasureSpec
- 测量规格,决定View的大小。
- 是一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize(规格大小)。
- 三种模式:
- UNSPECIFIED 父容器不对View有任何限制,要多大就给多大。常用于系统内部。
- EXACTLY 父容器已经检测出View所需要的精确大小即SpecSize。对应LyaoutParams中的match_parent或具体数值。
- AT_MOST 父容器为子视图指定一个最大尺寸SpecSize。对应LayoutParams中的wrap_content。
- View的MeasureSpec由LayoutParams和父容器的LayoutParams共同决定。
4.2.2 MeasureSpec和LayoutParams的对应关系
4.3 View的工作流程
- 先将DecorView加载到Window中
- 然后开始View的绘制,调用ViewRootImpl的PerformTraversals方法
- performTraversals()中依次调用performMeasure()、performLayout()和performDraw()三个方法,分别完成顶级 View的绘制
- performMeasure() > measure() > onMeasure(),其中会实现子View的measure过程,layout和draw同理。
4.3.1 measure过程
测量View的宽高
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
- View的Measure
- measure() > onMeasure() > setMeasuredDimension() > getDefaultSize()
- ViewGroup的Measure
- measureChildren() > measureChild() > getChildMeasureSpec() > child.measure()
直接继承View的自定义View,需要重写onMeasure()并设置wrap_content时的自身大小,否则效果相当于macth_parent。
如何保证某个View已经测量完毕?
- Activity/View#onWindowFocusChanged
- view.post(runnable)
- ViewTreeObserver
- view.measure(int widthMeasureSpec , int heightMeasureSpec)
4.3.2 layout过程
确定元素的位置
- View的layout(),通过setFrame()来设定自己的四个顶点,确定自己的位置
- onLayout()确定子元素的位置,空方法,不同的ViewGroup实现不同。
4.3.3 draw过程
将View绘制到屏幕上
- 绘制背景(drawBackground)
- 绘制自己(onDraw)
- 绘制children(dispatchDraw)
- 绘制装饰(onDrawScrollBars)
4.4 自定义View
4.4.1 自定义View的分类
- 继承View
- 重写onDraw
- 支持wrap_content、处理padding
- 继承ViewGroup
- 处理自身和子元素的测量和布局
- 继承特定的View(如TextView)
- 继承ViewGroup(如LinearLayout)
4.4.2 自定义View须知
- 尽量不要在View中使用Handler,使用post
- 及时停止线程和动画
- 滑动嵌套时,处理滑动冲突
- 自定义属性
- values下创建自定义属性的xml
- View的构造方法中解析自定义属性并处理
- 在布局中使用自定义属性