参考自 http://blog.csdn.net/lmj623565791/article/details/38352503/
自定义ViewGroup实现流式布局的效果,支持Margin
代码
public class FlowViewGroup extends ViewGroup {
private static final String TAG = "FlowViewGroup";
/*所有子View,按照行进行存储*/
private List<List<View>> mEachLineViews = new ArrayList<>();
/*每行的高度*/
private List<Integer> mLineHeight = new ArrayList<>();
public FlowViewGroup(Context context) {
this(context, null);
}
public FlowViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/*针对wrap_content的情况进行处理*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// mode and size
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// wrap_content时记录宽和高
int width = 0;
int height = 0;
int lineWidth = 0; // 最长的width
int lineHeight = 0; // 累加的Height
int childCount = getChildCount();
// 测量所有子View
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
int childWidth = params.leftMargin + params.rightMargin + child.getMeasuredWidth();
int childHeight = params.topMargin + params.bottomMargin + child.getMeasuredHeight();
// 加入当前View时,宽度超过ViewGroup的宽度时,进行换行
if (lineWidth + childWidth > widthSize) {
width = Math.max(lineWidth, childWidth);
lineHeight += childHeight;
lineWidth = 0;
} else {
// 没有换行时,lineWidth继续累加
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight); // 最大高度
}
if (i == childCount - 1) { // 处理最后一个子View的情况
width = Math.max(width, lineWidth);
height += lineHeight;
}
}
setMeasuredDimension(widthMode == MeasureSpec.AT_MOST ? width : widthSize
, heightMode == MeasureSpec.AT_MOST ? height : heightSize);
}
/*对子View位置的摆放*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
/*计算每个ChildView的位置*/
mEachLineViews.clear();
mLineHeight.clear();
int width = getWidth();
int childCount = getChildCount();
int lineHeight = 0;
int lineWidth = 0;
List<View> lineViews = new ArrayList<>();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
int childWidth = childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
int childHeight = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
// 需要换行的情况
if (lineWidth + childWidth > width) {
mLineHeight.add(lineHeight);
mEachLineViews.add(lineViews);
lineWidth = 0;
lineHeight = 0;
lineViews = new ArrayList<>();
}
// 没有换行,进行累加
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
lineViews.add(childView);
}
// 记录最后一行
mLineHeight.add(lineHeight);
mEachLineViews.add(lineViews);
/*对每个ChildView进行显示*/
int left = 0;
int top = 0;
int lineNums = mEachLineViews.size();
for (int i = 0; i < lineNums; i++) {
// 获取每一行所有的View
lineViews = mEachLineViews.get(i);
lineHeight = mLineHeight.get(i);
// 对当前行的所有View排列位置
for (View childView : lineViews) {
if (childView.getVisibility() == View.GONE) {
continue;
}
MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
// 确定位置
int lc = left + params.leftMargin; // left
int tc = top + params.topMargin; // top
int rc = lc + childView.getMeasuredWidth(); // right
int bc = tc + childView.getMeasuredHeight(); // bottom
childView.layout(lc, tc, rc, bc);
left += childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
}
left = 0;
top += lineHeight;
}
}
/* 生成默认的LayoutParams */
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
}
测试的布局代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="cn.edu.hebust.customviewgroup.MainActivity">
<cn.edu.hebust.customviewgroup.FlowViewGroup
android:layout_width="match_parent"
android:background="#dddddd"
android:layout_height="match_parent">
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Hello World" />
<View
android:layout_width="120dp"
android:layout_height="30dp"
android:layout_margin="10dp"
android:background="#dd4564" />
<View
android:layout_width="40dp"
android:layout_height="10dp"
android:layout_margin="10dp"
android:background="#d652" />
<View
android:layout_width="220dp"
android:layout_height="20dp"
android:layout_margin="10dp"
android:background="#dd641d" />
<View
android:layout_width="20dp"
android:layout_height="50dp"
android:layout_margin="10dp"
android:background="#dd23ee" />
<View
android:layout_width="220dp"
android:layout_height="70dp"
android:layout_margin="10dp"
android:background="#d4d564" />
</cn.edu.hebust.customviewgroup.FlowViewGroup>
</RelativeLayout>