前言
例子是用来理解自定义 ViewGroup 流程,不建议直接使用,如需使用可根据需求进行修改。
效果图
实现思路
- 继承自 ViewGroup
- 实现 onMeasure 方法,通过子 View 的宽高来确定自己的宽高
- 实现 onLayout 方法,摆放子 View 的位置
TagLayout
public class TagLayout extends ViewGroup {
public TagLayout(Context context) {
this(context, null);
}
public TagLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mChildViews.clear();
//获取子View的个数
int childCount = getChildCount();
//父View的宽度
int width = MeasureSpec.getSize(widthMeasureSpec);
//需要计算的高度
int height = getPaddingTop() + getPaddingBottom();
//单行的宽度
int lineWidth = getPaddingLeft();
//存放一行的View
List<View> childViews = new ArrayList<>();
mChildViews.add(childViews);
//处理单行不换行情况
int maxHeight = 0;
//循环测量子View
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
//测量子View的宽高
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
//获取子View的 margin
MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
//子View占据的宽度
int childWidth = childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
//子View占据的高度
int childHeight = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
//根据子View的宽度来处理换行
if (lineWidth + childWidth > width) {
//换行
height += maxHeight;
lineWidth = childWidth;
childViews = new ArrayList<>();
mChildViews.add(childViews);
} else {
//累加宽度
lineWidth += childWidth;
maxHeight = Math.max(childHeight, maxHeight);
}
childViews.add(childView);
}
height += maxHeight;
//设置宽高
setMeasuredDimension(width, height);
}
onMeasure 方法需要记录换行的信息,每一行有多少个子 View,最终确定自己的高度
onLayout
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left;
int top = getPaddingTop();
int right;
int bottom;
int maxHeight = 0;
for (List<View> childViews : mChildViews) {
left = getPaddingLeft();
for (View childView : childViews) {
//获取子View的 margin
MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
left += params.leftMargin;
int childTop = top + params.topMargin;
right = left + childView.getMeasuredWidth();
bottom = childTop + childView.getMeasuredHeight();
Log.d("TAG", "left -> " + left + " top ->" + top + " right -> " + right + " bottom ->" + bottom);
//摆放
childView.layout(left, childTop, right, bottom);
left += childView.getMeasuredWidth() + params.rightMargin;
int childHeight = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
maxHeight = Math.max(childHeight, maxHeight);
}
top += maxHeight;
}
}
便利每行子 View 集合,设置子 View 摆放的位置,如果一行不止一个子 View 需要将 left 累加。一行行便利,最终摆放出正确的位置