
Screenshot_2019-12-06-16-32-12.png
一 概述
當一行顯示不了子view時自動換行,就和淘寶的歷史記錄使用的布局一樣。
二 原理
繪制控件的流程圖

image
如果是重寫viewGroup 則測量自身尺寸的代碼寫在onMeasure里,布局子view位置的代碼寫在onLayout里。
重寫onMeasure步驟:
1 測量所有子view的尺寸
2 遍歷所有子view,計算每個子view在布局里的位置
3設(shè)置布局的尺寸
三 實現(xiàn)
1,在values 新建attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FlowLayout">
<attr name="maxLine" format="integer"/>
<attr name="noRightMargin" format="boolean"/>
</declare-styleable>
</resources>
2,新建FlowLayout類
public class FlowLayout extends ViewGroup {
protected List<Rect> viewRects;//子view布局
protected int allChildWidth = 0, allChildHeight = 0;//當前布局包括padding的最大寬高
int maxLine;//最大行數(shù)
boolean noRightMargin;//每行最右邊子view是否需要右邊距
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ary = this.getContext().obtainStyledAttributes(attrs, R.styleable.FlowLayout);
maxLine = ary.getInt(R.styleable.FlowLayout_maxLine, 0);
noRightMargin = ary.getBoolean(R.styleable.FlowLayout_noRightMargin,false);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < viewRects.size(); i++) {
View child = getChildAt(i);
if (child == null) {
return;
}
Rect rect = viewRects.get(i);
child.layout(rect.left, rect.top, rect.right, rect.bottom);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
viewRects = new ArrayList<>();
/*測量子view尺寸*/
measureChildren(widthMeasureSpec, heightMeasureSpec);
int measureWidth, measureHeight;
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//預(yù)布局
compute(widthSize);
//給定大小和match_parent
if (widthMode == MeasureSpec.EXACTLY) {
measureWidth = widthSize;
//未規(guī)定大小
} else {
measureWidth = allChildWidth;
}
if (heightMode == MeasureSpec.EXACTLY) {
measureHeight = heightSize;
} else {
measureHeight = allChildHeight;
}
//設(shè)置布局寬高
setMeasuredDimension(measureWidth, measureHeight);
}
private void compute(int widthSize) {
boolean aRow = false;//是否換行
int line = 1;
int paddingLeft = getPaddingLeft();
int maxWidth = widthSize - getPaddingRight();//最大寬度
if (maxWidth <= 0) {
return;
}
int nextRowWidth = paddingLeft;//預(yù)計下一步起點已經(jīng)占寬度
int nextColumnTopHeight = getPaddingTop();//預(yù)計下一步行頂部所占高度
int upRowHeight = 0;//上一步最大行高
MarginLayoutParams margin;
int left,top,right,bottom;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
int meausredWidth = child.getMeasuredWidth();//view測量寬度
int measuredHeight = child.getMeasuredHeight();//view測量高度
margin = (MarginLayoutParams) child.getLayoutParams();//view邊距,必須重寫generateDefaultLayoutParams不然會報錯
int childMarginLeft = margin.leftMargin;
int childMarginRight = margin.rightMargin;
int childMarginTop = margin.topMargin;
int childMarginBottom = margin.bottomMargin;
int childWidth = childMarginLeft + meausredWidth + childMarginRight;//view實際寬度
int childHeight = childMarginTop + measuredHeight + childMarginBottom;//view實際高度
//預(yù)計本次可能需要布局在下一行
if (nextRowWidth + childWidth > maxWidth) {
//布局在本行
if (noRightMargin && (nextRowWidth + childWidth - childMarginRight <= maxWidth)) {
left = nextRowWidth + childMarginLeft;
top = nextColumnTopHeight + childMarginTop;
right = nextRowWidth + childWidth - childMarginRight;
bottom = nextColumnTopHeight + childHeight - childMarginBottom;
viewRects.add(new Rect(left, top, right, bottom));
//重置數(shù)據(jù)
int s = Math.max(childHeight, upRowHeight);//比較高度
nextRowWidth = paddingLeft;
nextColumnTopHeight += s;
allChildHeight = nextColumnTopHeight + getPaddingBottom();
upRowHeight = 0;
aRow = true;
line++;
if (line == maxLine) {
break;
}
} else if (!noRightMargin || (noRightMargin && (nextRowWidth + childWidth - childMarginRight > maxWidth))) {
//布局在下一行
if (line == maxLine) {
break;
}
left = paddingLeft + childMarginLeft;
top = nextColumnTopHeight + upRowHeight + childMarginTop;
right = paddingLeft + childWidth - childMarginRight;
bottom = nextColumnTopHeight + upRowHeight + childHeight - childMarginBottom;
viewRects.add(new Rect(left, top, right, bottom));
//重置數(shù)據(jù)
nextColumnTopHeight+= upRowHeight;
nextRowWidth = right + childMarginRight;
upRowHeight = childHeight;
aRow = true;
line++;
allChildHeight = nextColumnTopHeight + upRowHeight + getPaddingBottom();
}
} else {
//在當前行
left = nextRowWidth + childMarginLeft;
top = nextColumnTopHeight + childMarginTop;
right = nextRowWidth + childWidth - childMarginRight;
bottom = nextColumnTopHeight + childHeight - childMarginBottom;
viewRects.add(new Rect(left, top, right, bottom));
//重置數(shù)據(jù)
nextRowWidth += childWidth;
upRowHeight = Math.max(childHeight, upRowHeight);
allChildHeight = nextColumnTopHeight + upRowHeight + getPaddingBottom();
}
}
if (aRow) {
allChildWidth = widthSize;
} else {
allChildWidth = nextRowWidth;
}
}
//inflater 的時候調(diào)用
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
//addView的時候
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
//child沒有設(shè)置LayoutParams的時候調(diào)用
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
//addView的時候
@Override
protected boolean checkLayoutParams(LayoutParams p) {
return p instanceof MarginLayoutParams;
}
}
3,在xml里
<com.wuzhiping.kitchens.selfView.FlowLayout
app:maxLine="5"
android:id="@+id/xun_rage"
app:noRightMargin="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
4,添加子view
flow1 = view.findViewById(R.id.xun_rage);
int mg = 10;
FlowLayout.MarginLayoutParams pa = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
pa.setMargins(0,mg,mg*4,0);
for (int i=0;i < 20; i ++){
TextView item = new TextView(view.getContext());
item.setText("萬一");
item.setBackgroundResource(R.drawable.xun_top_edit);
item.setTextSize(TypedValue.COMPLEX_UNIT_DIP,18);
item.setPadding(mg,mg,mg,mg);
flow1.addView(item,pa);
}