主要包括:布局,绘制,触摸反馈
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); } }