主要包括:布局,绘制,触摸反馈
1,布局,主要是View在父容器的位置以及自身的尺寸。
布局中,所有layout开头的都是给父容器看的,
子布局通过onMeasure 的measure方法获取自身所需要的尺寸,父容器获取子布局的期望值,
在计算然后父容器通过layout方法把实际尺寸发给子布局
onDraw和onMeasure是 总调度。draw和measure是实际代码的地方。
2,layout是把父容器传过来的尺寸值保存起来,是操作自己的布局,一般layout是不需要重写的。因为这里操作布局,父容器是不知道的,会对其他布局有影响,应该去操作onMeasure()方法,然后把这个值传给父容器再次计算。
onLayout是对子控件进行布局的,是自定义ViewGroup的时候用的。值是onMeasure中测量出来的。
3,onMeasure:自己对自己的测量,getMeasureWidth() 获取自己的期望宽度,setMeasureDimension() ,保存测得的尺寸。也可以说把自己的尺寸通过这个方法,告诉父容器,进行自己尺寸的修改。父容器接收到,进行对子容器的布局。
onMeasure 中有具体值,有上限,和无上限三种区别。
resolverSize( ,) 方法修正 自己测量的宽高,主要是为了替换,根据模式来获取width和height
4,整体流程:
子View在onMeasure方法,计算自己的大小,通过setMeasureDimension()方法,把自己的期望值传给父容器。
父容器接受到子控件传给自己的数据,再onLayout()方法中,把所有的控件的实际值计算出来,通过layout()
方法传回给子控件。子View在自己的onLayout() 方法中接受自己实际值,从而绘画布局。
5,View的控件,重写onMeasure()和onDraw()方法。
ViewGroup的控件,重写onMeasure(),onLayout()方法。
自定义View
自定义ViewGroup之瀑布流
public class TagLayout extends ViewGroup {
List<Rect> childrenBounds = new ArrayList<>();
public TagLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthUsed = 0; // 最宽宽度
int heightUsed = 0; // 已用高度
int lineWidthUsed = 0; // 每行已用宽度
int lineMaxHeight = 0; // 每行的最大高度
int specMode = MeasureSpec.getMode(widthMeasureSpec); // 一行的宽度
int specWidth = MeasureSpec.getSize(widthMeasureSpec); // 一行的宽度
// 子布局的尺寸
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
// 重新测量子View的位置 widthUsed = 0 是为了避免压缩子View
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed);
if (specMode != MeasureSpec.UNSPECIFIED && lineWidthUsed + child.getMeasuredWidth() > specWidth){
// 换行
lineWidthUsed = 0;
heightUsed += lineMaxHeight;
lineMaxHeight = 0;
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed);
}
Rect childBound;
if (childrenBounds.size() <= i) {
childBound = new Rect();
childrenBounds.add(childBound);
} else {
childBound = childrenBounds.get(i);
}
childBound.set(lineWidthUsed, heightUsed, lineWidthUsed + child.getMeasuredWidth(), heightUsed + child.getMeasuredHeight());
lineWidthUsed += child.getMeasuredWidth();
widthUsed = Math.max(widthUsed,lineWidthUsed);
lineMaxHeight = Math.max(lineMaxHeight, child.getMeasuredHeight());
}
// 自身尺寸
int width = widthUsed;
int height = heightUsed + lineMaxHeight;
setMeasuredDimension(width, height);
}
//测量各个子View的尺寸
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
Rect childBounds = childrenBounds.get(i);
//把子View的尺寸传给他们
view.layout(childBounds.left, childBounds.top, childBounds.right, childBounds.bottom);
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(),attrs);
}
}