基本概念
ViewGroup 继承 View ,但是用来作为一个容器,装载各种 View 以及对它们做 UI 布局,比如高、宽、对齐方式等等,布局文件中凡是以 layout_ 开头的属性,都是传递给 ViewGroup 来解析和使用的。ViewGroup 主要是计算子 View 的测量高宽并决定他们的位置。 重写 LayoutParams 可以自定义子 View 的特定参数,比如 weight 等。
框架和层级结构
View 和 ViewGroup 的绘制流程框架:

层级结构如下:

重要 API
- onMeasure
测量自己的高宽;测量所有子View的高宽 - onLayout
抽象函数,必须重写。自定义子View的排列规则
自定义 ViewGroup 步骤
自定义布局属性及 LayoutParams
同样在 res/values/attr.xml 文件中定义 ViewGroup 的属性及样式。
1 | <attr name="custom_orientation"> |
在布局文件使用时,示例如下:
1 | <com.***.view.CustomLayout |
获取自定义布局属性
在 ViewGroup 或者自定义 LayoutParams 的构造方法中获取自定义属性值。
1 | public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) { |
重写 LayoutParams 相关方法
自定义类 LayoutParams 继承 ViewGroup.LayoutParams,并定义布局所需的几个变量。
1 | public static class LayoutParams extends ViewGroup.LayoutParams{ |
如果自定义了 LayoutParams ,必须重写下面四个方法,确保能做类型转换。
1 |
|
注意:如果没有重写这四个方法,会导致子 View 获取的 LayoutParams 转换为自定义时抛出异常:CustomLayout.LayoutParams lp = (CustomLayout.LayoutParams) childView.getLayoutParams();
转换失败异常 Log 打印如下:java.lang.ClassCastException: android.view.ViewGroup$LayoutParams cannot be cast to com.***.view.CustomLayout$LayoutParams
重写 onMeasure
实现两个功能:
- 计算子
View的测量高宽 - 计算自身测量高宽
1 |
|
重写 onLayout
计算子 View 的具体布局位置
1 |
|
自定义
ViewGroup中,至少需要重写onMeasure和onLayout
总结
onMeasure主要计算wrap_content模式下的测量高宽,包含自己和所有的子ViewonLayout主要计算子View布局的具体位置onDraw绘制自己,展现需要显示的内容
自定义 ViewGroup 主要计算自身和子 View 的测量高宽,以及子 View 布局的具体位置。
自定义 View 主要计算自身的测量高宽,以及绘制自己。
问题
在 Log 跟踪中发现 onLayout 和 onMeasure 会被调用执行两次
目标
- 自定义
ViewGroup常见流程 - 必须重写
onLayout及需要实现那些功能 - 是否处理事件分发流程