1.ViewRoot & DecorView
- ViewRoot (ViewRootImpl) : 连接WindowManager 和 DecorView的纽带
root = new ViewRootImpl(view.getContext() , display);
root.setView(view , wparams , panelParentView);
View的绘制流程是从ViewRoot的performTraversals方法开始。
measure过程决定View的宽高( getMeasuredWidth() & getMeasuredHeight() ),几乎所有的情况下它都等于View最终的宽高
draw过程决定了View的显示
- DecorView作为顶级View,一般会包含一个竖直方向的LinearLayout(标题栏+内容栏)
setContentView()所设置的布局文件就是被加到内容栏之中,id为content
ViewGroup content = findViewById(R.android.id.content) 得到内容栏
content.getChildAt(0)得到设置的View
2.MeasureSpec
- 32位的int值,高2位代表SpecMode , 低30位代表SpecSize
- SpecMode : UNSPECIFIED (不做限制,用于系统内部), EXACTLY(具体的数值和match_parent) , AT_MOST(wrap_content)
注意: 对于DecorView(顶级View),其MeasureSpec由窗口的尺寸和自身的LayoutParams决定
对于普通的View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams决定
UNSPECIFIED : getSuggestedMinimumWidth() getSuggestedMinimumHeight()的返回值就是在此模式下测量的宽高
3.View的工作流程
- measure -> onMeasure()
- layout -> ViewGroup用来确定子元素的位置 ; layout() 确定View本身的位置 , onLayout()确定所有子元素的位置
- draw() 遵循以下几步: 绘制背景,绘制自己,绘制children , 绘制装饰
4.自定义View
自定义View需要主要的地方:
- 让View支持wrap_content(onMeasure中处理AT_MOST)
- 支持padding(如果有必要的话)
对于View: 若不在draw中处理padding,那么padding属性不起作用
对于ViewGroup: 需要在onMeasure , onLayout中考虑padding和子元素的margin对其造成的影响 - 尽量不要在View中使用Handler,没必要
- View中若有线程或动画,需要及时停止(不可见和在onDetachedFromWindow()),避免造成内存泄漏
- 若有滑动嵌套需处理好滑动冲突