自定义View、ViewGroup的最基本的支持要点
-
自定义View - onMeasure、onDraw
-
wrap_content;
- padding;
- 自定义ViewGroup - onMeasure、onLayout
- wrap_content;
- padding;
- margin;
为啥View只需支持padding,而ViewGroup需要支持padding和margin?
这里注意了,margin属于父子View之间的布局关系,是用于父容器计算内部子View的显示位置(onLayout)的,为啥padding不需要父容器干涉?padding是View内部需要支持的属性,不属于父子View之间的布局关系。
- 红色框线 - 父容器
- 蓝色框线 - 子View的显示区域
- 绿色框线 - 子View的绘制区域
- A、B、C、D - paddingTop、paddingLeft、paddingRight 、paddingBottom
- E - 子View的绘制区域
- 1、2、3、4 - marginTop、marginLeft、marginBottom、marginRight
其中,子View的显示区域 = 子View的绘制区域 (E)+ Padding(A、B、C、D),这才是子View实际的地盘。
而margin是父容器的地盘,有一句话叫各扫门前雪,所以子View处理好自己的Padding就好了。ViewGroup也是一个View(ps:可以包括子View的容器View),因此需要处理Padding,又因为它是一个容器,需要计算子View(在父容器中)的显示位置,这就需要将margin考虑在内了,因此必须处理margin。
父View的大小 = 子View大小总和 + 父View的padding总和 + 子View的margin总和;
那么wrap_content为啥也要处理?后面说。
自定义View
自定义一个View,只需处理测量(onMeasure)和重绘(onDraw)即可。
可以发现自定义View和自定义ViewGroup需要重写的方法是不同的,除了都需要重写onMeasure之外:
- 自定义View只需额外重写onDraw即可
- 自定义ViewGroup只需额外重写onLayout即可
于是就有了下面的对话:
父容器:你们(Views)在lz的地盘摆摊,摊位的位置老子定(onLayout),你们说了不算;
子View:好的,这个摊位咋个摆放货品,lz说的算(onDraw),你(父容器)管不着了;
父容器:好啊,但是这个摊位面积问题,咱俩得商量一下撒?不然我咋个知道!lz定完了(onLayout),会告诉你们。之后lz就不管了!
子View:可以啊,听起来你(父容器)很叼的样子!
其实,说白了,还是地盘问题~
看过View源码的同学都知道,View的onLayout是空实现,为啥呢?因为View不是容器,所以没有子View,自然onLayout是需要实现的。那么一个View到底要绘制到屏幕的位置呢?不用我们管么?确实,一般这块是不需要我们处理的。View的layout方法已经为我们实现好了。
public void layout(int l, int t, int r, int b) {
......
// 关键方法:setOpticalFrame 、 setFrame
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
......
}
至于onLayout的四个参数,表示View在屏幕中的位置(左上右下),这四个参数的值是父容器的事情,由父容器计算子View该在屏幕的哪个位置显示,计算完成然后告诉子View(childView.layout(l,t,r,b)),然后子View就乖乖的在这个区域进行onDraw了。
- onMeasure
注意:此处可以解答为啥要支持wrap_content
-----------View的onMeasure处理逻辑----------
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
正常情况下,如果我们自定义View或者ViewGroup不重写onMeasure方法的话,则会调用View的onMeasure方法。注意,在View或者ViewGroup中,测量完成都是通过setMeasuredDimension保存宽高尺寸的。
可以看到,在默认的处