ViewGroup?
ViewGroup是一个放置View的容器。我们在用XML来写布局时,会告诉容器我们的宽(layout_width),高(layout_height),对齐方式(layout_gravity)等。(以layout开头的属性,都是为了告诉容器的)。
ViewGroup是给childView计算出建议的宽高和测量模式的,并且决定childView的位置。建议的宽高是因为当childView设置为wrap_content时,只有childView才能算出自己的宽高。
View的三种测量模式
ViewGroup会为其childView设置以下三种模式。
EXACTLY:表示精确的值,一般当childView设置精确的宽高或者match_parent时。
AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽高为wrap_content时。
UNSPECIFIED:表示子布局想要多大就多大。
View?
View根据测量模式和ViewGroup给出建议的宽高,计算出自己的宽高,并且在ViewGroup的指定区域内绘制自己的形态。
API角度
View根据ViewGroup传入的测量值和模式,在onMeasure时确定自己的宽高,在onDraw时完成自己的绘制。
ViewGroup在onMeasure中计算childView的测量值及模式,以及完成计算和确定自己的宽高。在onLayout中确定所有childView的绘制区域。
简单实现TagFlowLayout
onMeasure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); int width = MeasureSpec.getSize(widthMeasureSpec); int actuallyWidth = width - Margin * 2; int actuallyHeight = 0; int row = 1; int cHeight = 0; int addWidth = 0; for (int i = 0; i < count; i++) { View child = getChildAt(i); child.setPadding(padding, padding, padding, padding); child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int cWidth = child.getMeasuredWidth(); cHeight = child.getMeasuredHeight(); addWidth += cWidth + padding; if (addWidth > actuallyWidth) { row++; addWidth = cWidth; } } actuallyHeight = (cHeight + Margin) * row; setMeasuredDimension(actuallyWidth, actuallyHeight); }
|
首先获取其父容器传入的宽高,然后遍历所有子布局,根据子布局测量出的宽高计算出自身的实际大小,然后设置自己的宽高。
onLayout
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); int actuallyWidth = r - l; int x = Margin; int y; int row = 1; for (int i = 0; i < count; i++) { View child = getChildAt(i); child.setBackgroundColor(Color.GRAY); int width = child.getMeasuredWidth(); int height = child.getMeasuredHeight(); x += width + padding; if (x > actuallyWidth) { x = width + padding; row++; } y = row * (height + Margin); if (i == 0) child.layout(x - width - padding, y - height, x - padding, y); else child.layout(x - width, y - height, x, y); } }
|
遍历所有的子布局,确定它们的绘制区域。
具体代码:整个项目 单个文件