这里先实现一个简单的ViewGroup,通过一个简单的自定义ViewGroup来一窥measure和layout的流程。
image
这个ViewGroup的规则是:它要求每个孩子按照顺序垂直排列,但是垂直排列的同时,每个View还需要在前一个的基础上继续缩进100像素。并且ViewGroup的宽度和高度,是自适应的。
一:ViewGroup的measure过程包括:
- 1:先测量自己
- 2:再测量孩子
- 3:再测量自己
- 4:保存宽高。
按照这四个步骤,我们的onMeasure代码如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//先测量ViewGroup自身
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//测量child
measureChildren(widthMeasureSpec, heightMeasureSpec);
// int childCount = getChildCount();
// for(int i=0;i<childCount;i++){
// View child = getChildAt(i);
// LayoutParams layoutParams = child.getLayoutParams();
// int widthChild = getChildMeasureSpec(widthMeasureSpec, 0, layoutParams.width);
// int heightChild = getChildMeasureSpec(heightMeasureSpec, 0, layoutParams.height);
// child.measure(widthChild,heightChild);
// }
//测量自己
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
int childCount = getChildCount();
for(int i=0;i<childCount;i++){
View childAt = getChildAt(i);
width = Math.max(width,childAt.getMeasuredWidth()+i*100);
}
break;
default:break;
}
switch (heightMode){
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
int childCount = getChildCount();
for(int i=0;i<childCount;i++){
View childAt = getChildAt(i);
height +=childAt.getMeasuredHeight();
}
break;
}
// 保存宽高
setMeasuredDimension(width,height);
}
若是没有第三步,那么这个ViewGroup的宽高,就将始终是父View的宽高了。而关于上面的注释一段代码,是我们可以自己测量,也可以交由系统提供的代码来测量。这里选择了交给系统的代码来测量。
二:layout过程:
- 1:计算各个child的left,top,right,bottom
- 2:调用child的layout方法开始摆放位置。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = 0;
int right = 0;
int top = 0;
int bottom = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childAt = getChildAt(i);
left = i * 100;
right = left + childAt.getMeasuredWidth();
bottom = top + childAt.getMeasuredHeight();
childAt.layout(left, top, right, bottom);
top += childAt.getMeasuredHeight();
}
}
完整代码如下:
activity_my_view_group.xml
<com.goodsnow.view.custom.MyViewGroup
android:background="@color/colorPrimary"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:text="first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="secondsecondsecondsecondsecondsecondsecondsecondsecondsecondsecondsecond"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="third"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="four"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</com.goodsnow.view.custom.MyViewGroup>
MyViewGroup.java
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
super(context);
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//先测量ViewGroup自身
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//测量child
measureChildren(widthMeasureSpec, heightMeasureSpec);
// int childCount = getChildCount();
// for(int i=0;i<childCount;i++){
// View child = getChildAt(i);
// LayoutParams layoutParams = child.getLayoutParams();
// int widthChild = getChildMeasureSpec(widthMeasureSpec, 0, layoutParams.width);
// int heightChild = getChildMeasureSpec(heightMeasureSpec, 0, layoutParams.height);
// child.measure(widthChild,heightChild);
// }
//测量自己
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
int childCount = getChildCount();
for(int i=0;i<childCount;i++){
View childAt = getChildAt(i);
width = Math.max(width,childAt.getMeasuredWidth()+i*100);
}
break;
default:break;
}
switch (heightMode){
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
int childCount = getChildCount();
for(int i=0;i<childCount;i++){
View childAt = getChildAt(i);
height +=childAt.getMeasuredHeight();
}
break;
}
// 保存宽高
setMeasuredDimension(width,height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = 0;
int right = 0;
int top = 0;
int bottom = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childAt = getChildAt(i);
left = i * 100;
right = left + childAt.getMeasuredWidth();
bottom = top + childAt.getMeasuredHeight();
childAt.layout(left, top, right, bottom);
top += childAt.getMeasuredHeight();
}
}
}