(自定义ViewGroup)
自定义布局主要是重写两个方法:
- onMeasure() 这个是写自定义容器的大小。
- onLayout() 这个是写子元素的布局。
我自己写了一个自定义布局,是顺序填充会延对角线进行排列。
3.1onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
*/
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
// 计算出所有的childView的宽和高
measureChildren(widthMeasureSpec, heightMeasureSpec);
/**
* width和height是当wrap_content时使用的属性。
*/
int width = 0;
int height = 0;
int cCount = getChildCount();
int cWidth = 0;
int cHeight = 0;
/**
* 在这里计算当wrap_content时,布局的大小。
*/
for (int i = 0; i < cCount; i++) {
View childView = getChildAt(i);
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
width += cWidth;
height += cHeight;
}
/**
* 如果是wrap_content设置为我们计算的值
* 否则:直接设置为父容器计算的值
*/
setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth
: width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight
: height);
}
首先要说一下布局计算模式,即最后的EXACTLY。一共有三种计算模式:
- MeasureSpec.EXACTLY:精确尺寸,相当于具体数值和match_parent。
- MeasureSpec.AT_MOST:最大尺寸,相当于 warp_content。
- MeasureSpec.UNSPECIFIED:未指定尺寸,这种情况不多,一般用于AdapterView。
最后的设定大小时,如果是精确尺寸就是用sizeWidth即获取的尺寸,如果是最大尺寸就是要我们自己计算的那个尺寸了。
onMeasure()最主要的功能就是计算wrap_content的尺寸和设置尺寸。
我将这个方法称为“建画布”,先建了画布才能在上面绘图嘛。
3.2 onLayout()
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int cCount = getChildCount();
/**
* 遍历所有childView根据其宽和高,以及margin进行布局
*/
for (int i = 0; i < cCount; i++) {
View childView = getChildAt(i);
r = l + childView.getMeasuredWidth();
b = t + childView.getMeasuredHeight();
childView.layout(l, t, r, b);
l += childView.getMeasuredWidth();
t += childView.getMeasuredHeight();
}
}
这个方法的作用是设置摆放子元素的位置。其中onLayout()传入的l、t、r、b分别是这样
- l,t分别对应子元素左上角的left,top坐标
- r,b分别对应子元素右下角的right,bottom坐标
并且可以使用childview.getMeasuredWidth()和childView.getMeasureHeight()得到子元素的宽和高。
这样就可以来对每个子元素进行布局了。
我称这个方法为“定位置”。定完位置后那么子元素就被放到了我们想要的地方。
这样一个自定义ViewGroup就可以使用了。
xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<com.example.layoutdemo.MyLayout.MyLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#b2dfdb" />
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#80cbc4" />
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#4db6ac" />
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#26a69a" />
</com.example.layoutdemo.MyLayout.MyLayout>
最后效果如图: