相关知识点
LayoutParams
每个自定义的ViewGroup都需要确定添加的子View能够拥有什么样的属性,如MarginLayoutParams,可以通过ViewGroup的generateLayoutParams()方法来自定义ViewGroup的LayoutParams。从这里我们也可以理解,当我们在代码中动态生成布局的时候,父控件如果是LinearLayout,则子控件设置的LayoutParams必须是LinearLayout.LayoutParams。
View的测量模式
- EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
- UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
- AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST。这种模式下,一般我们要测量控件的实际大小。
View/ViewGroup的职责
View的职责
- 根据ViewGroup建议的宽高和测量模式计算出自己的宽高
- 在ViewGroup中制定的区域内绘制自己
- 所以自定义ViewGroup更多的是在onMeasure(),onDraw()中处理
注意自定义View更多的是考虑padding值
ViewGroup的职责
- 给childView计算出建议的宽高和测量模式
- 决定childView的位置
- 所以自定义ViewGroup更多的是在onMeasure(),onLayout()中处理,onDraw()交由系统处理
注意自定义ViewGroup更多的是考虑children的margin
自定义控件绘制的一般流程
自定义View绘制流程
- 在onMeasure()中测量自身,主要针对warp_content来测量自己。如果你设计的控件是Match_Parent或是制定的值,则onMeasure()可以省略。
- 根据测量的结果在onDraw()中绘制自己
注意,当自定义的View的宽高是Match_parent , 或者具体值的话,则不用复写onMeasure()
自定义ViewGroup绘制流程
- 根据childView需要支持的属性自定义LayoutParams
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new MarginLayoutParams(p);
}
onMeasure()中调用measureChildren()来使childView测量自己。如果onMeasure()中测量模式是wrap_content,则需要根据childView的测量结构来决定ViewGroup自己的大小
//设定自定义View或者ViewGroup在onMeasure()测量的宽高
setMeasuredDimension(w,h)方法来设定。
//根据spec(父容器onMeasure提供的宽值),子控件支持的padding,以及子控件自身的大小
计算出子控件的MeasureSpec类型的值,这个值计算出之后一般就要调用childView.measre().
getChildMeasureSpec(int spec, int padding, int childDimension)onLayout中通过childView.layout(cl, ct, cr, cb)来设定childView的布局位置。
如果子控件支持margin则,计算位置的时候主语考虑margin
其中的自View的区域通过子View的对应LayouParams(如MarginLayoutParams)来获取自定义ViewGroup的相关类:
自定义控件优化
- onMeasure(),onLayout(),onDraw()不宜使用局部变量