一.效果介绍:
类似如下:
二.代码实现:(有问题,待解决)
1.布局加载:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.myapplication.TagLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello world" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="just do it" />
</com.example.myapplication.TagLayout>
</LinearLayout>
2.自定义View:
package com.example.myapplication;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public class TagLayout extends ViewGroup {
public TagLayout(Context context) {
super(context);
}
public TagLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//装所有的子View的List
//其中子List是每一行所有子View的List
private final List<List<View>> mAllChildViews = new ArrayList<>();
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//父View的宽度
int width = MeasureSpec.getSize(widthMeasureSpec);
//父View的高度
int height = getPaddingTop() + getPaddingBottom();
//子View叠加起来的宽度
int addWidth = getPaddingLeft();
//每一行的子View的list
List<View> lineChildViews = new ArrayList<>();
//子View的个数
int childCount = getChildCount();
//子View高度不一致的时候需要一个最大值;
int maxHeight = 0;
//集合使用之前要记得清空
mAllChildViews.clear();
//for循环测量子View
for (int i = 0; i < childCount; i++) {
//找到子View
View childView = getChildAt(i);
//测量子View,只有执行完这个才能拿到子view的宽高信息
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
//获取子View关于padding相关的信息
ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
//多个子View叠加起来超过一行的宽度的时候,就换行
if (addWidth + childView.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin > width) {
//更新父View的高度
height += childView.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
//叠加宽度置0
addWidth = 0;
lineChildViews = new ArrayList<>();
mAllChildViews.add(lineChildViews);
}
//没有超过就继续叠加
else {
addWidth += childView.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
//把每一行的子View都装在一个List里面
lineChildViews.add(childView);
//更新所有子View的最大高度
maxHeight = Math.max(maxHeight, childView.getMeasuredHeight());
}
}
//高度是要更新的
height += maxHeight;
//设置父View的宽高
setMeasuredDimension(width, height);
}
//强转MarginLayoutParams的时候需要
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//子View的个数
int childCount = getChildCount();
//子View的左、右、顶、底
int left;
int right;
int top = getPaddingTop();
int bottom;
//for循环布局子View
for (List<View> lineChildViews : mAllChildViews) {
left = getPaddingLeft();
for (View childView : lineChildViews) {
ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
left += layoutParams.leftMargin;
int childTop = top + layoutParams.topMargin;
bottom = childTop + childView.getMeasuredHeight();
right = left + childView.getMeasuredWidth();
//子View的布局
childView.layout(left, childTop, right, bottom);
//更新lest值
left += childView.getMeasuredWidth() + layoutParams.rightMargin;
}
ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) lineChildViews.get(0).getLayoutParams();
top += lineChildViews.get(0).getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
}
}
}
三.总结:
1.这种自定义View实现ViewGroup的主要包含以下步骤:
(1)重写onMeasure()方法,实现子View的测量以及设置自身的宽高。
(2)重写onLayout()方法,实现子View的布局工作。
四.参考视频: