效果图:
image.png
package com.zsw.mycustomviewlearn.customview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* @author zsw
* @date 2022/4/2
* @desc
*/
public class FlowLayout extends ViewGroup {
private List<List<View>> allViewList = new ArrayList<>();
private List<Integer> mLineMaxHeight = new ArrayList<>();
public FlowLayout(Context context) {
super(context);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
super.generateLayoutParams(p);
return new MarginLayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
/**
* 摆放控件 子控件排布
*
* @param b 表示该ViewGroup的大小或者位置是否发生变化
* 以下参数控件的位置
* @param i
* @param i1
* @param i2
* @param i3
*/
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
allViewList.clear();
mLineMaxHeight.clear();
//每一行View的集合
List<View> lineViewList = new ArrayList<>();
//子View的个数
int childCount = getChildCount();
//记录每一行的宽度
int lineWidth = 0;
//记录每一行子View最高的高度
int maxLineHeight = 0;
/*************遍历所有View,将View添加到List<List<View>>集合中***************/
for (int j = 0; j < childCount; j++) {
View childView = getChildAt(j);
MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
int childWidth = lp.leftMargin + childView.getMeasuredWidth() + lp.rightMargin;
int childHeight = lp.topMargin + childView.getMeasuredHeight() + lp.bottomMargin;
//如果当前这行的已用宽度+这个子view的宽度 大于 父view的宽度那说明折行放不下了需要另外起一行
if (lineWidth + childWidth > getWidth()) {
//把这一行的View和高信息加入到集合中
allViewList.add(lineViewList);
mLineMaxHeight.add(maxLineHeight);
//另起一行
lineWidth = 0;
maxLineHeight = 0;
lineViewList = new ArrayList<>();
}
lineWidth += childWidth;
lineViewList.add(childView);
maxLineHeight = Math.max(maxLineHeight, childHeight);
//单独处理最后一行
if (j == childCount - 1) {
allViewList.add(lineViewList);
mLineMaxHeight.add(maxLineHeight);
}
}
/************遍历集合中的所有View并显示出来*****************/
//表示一个view距离父容器的左边距
int mLeft = 0;
//表示一个view距离父容器的上边距
int mTop = 0;
for (int j = 0; j < allViewList.size(); j++) {
List<View> lineViews = allViewList.get(j);
int lineHeight = mLineMaxHeight.get(j);
for (int k = 0; k < lineViews.size(); k++) {
View childView = lineViews.get(k);
MarginLayoutParams mlp = (MarginLayoutParams) childView.getLayoutParams();
int childLeft = mLeft + mlp.leftMargin;
int childTop = mTop + mlp.topMargin;
int childRight = childLeft + childView.getMeasuredWidth();
int childBottom = childTop + childView.getMeasuredHeight();
//四个参数分别表示View的左上角和右下角 每个子View放在对应的位置
childView.layout(childLeft, childTop, childRight, childBottom);
mLeft += mlp.leftMargin + childView.getMeasuredWidth() + mlp.rightMargin;
}
mLeft = 0;
mTop += lineHeight;
}
}
/**
* 测量大小
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获得宽高的测量模式和测量值
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//获得容器中子View的个数
int childCount = getChildCount();
//记录每一行View的总宽度
int lineWidth = 0;
//记录每一行最高View的高度
int lineHeightMax = 0;
//记录当前ViewGroup的总高度
int totalHeight = 0;
//对子View进行测量
for (int i = 0; i < childCount; i++) {
//获得子view
View childView = getChildAt(i);
//测量子View
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
//获取子View margin信息
MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
//获得子View的测量宽度
int childWidth = childView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
//获得子View的测量高度
int childHeight = childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
//如果当前这行的已用宽度+这个子view的宽度 大于 父view的宽度那说明折行放不下了需要另外起一行
if (lineWidth + childWidth > widthSize) {
//统计总高度
totalHeight += lineHeightMax;
//开启新的一行
lineWidth = childWidth;
//新的一行的高度
lineHeightMax = childHeight;
} else {
//记录每一行的总宽度
lineWidth += childWidth;
//比较每一行最高的View
lineHeightMax = Math.max(lineHeightMax, childHeight);
}
//当该View已是最后一个View时,将该行最大高度添加到totalHeight中
// (因为上面的代码如果是最后一个时并没有加上这一行的高度) 只有lineHeightMax 赋值,并没有统计到totalHeight
if (i == childCount - 1) {
totalHeight += lineHeightMax;
}
}
//如果高度的测量模式是EXACTLY,则高度用测量值,否则用计算出来的总高度(这时高度的设置为wrap_content)
int realHeightSize = 0;
//EXACTLY 表示在 XML 布局文件中宽高使用 match_parent 或者固定大小的宽高;表示使用固定值,否则wrap_content
if (heightMode == MeasureSpec.EXACTLY) {
//使用固定值
realHeightSize = heightSize;
} else {
//使用计算出来的值
realHeightSize = totalHeight;
}
//设置当前view的大小
setMeasuredDimension(widthSize, realHeightSize);
}
}
使用方式一
<com.zsw.mycustomviewlearn.customview.FlowLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="150dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#00ff00">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="aaaaaaaaaa" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="bbbbbbb" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="CCCCCCCCC" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="rrrrrrrr" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="eeee" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="vvvvvvv" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="ttt" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="666666666666" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="uuuuuuuuuu" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="qqqqqqqqqq" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="#ff4000"
android:text="eeeeeeeeeeee" />
</com.zsw.mycustomviewlearn.customview.FlowLayout>
使用方式二:
xml中:
<com.zsw.mycustomviewlearn.customview.FlowLayout
android:id="@+id/flowLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" >
activity中:
flowLayout = findViewById(R.id.flowLayout);
List<String> stringList = new ArrayList<>();
stringList.add("盘发发簪");
stringList.add("茶歇连衣裙");
stringList.add("亲子装");
stringList.add("情侣对戒");
stringList.add("气泡");
stringList.add("旗袍");
stringList.add("泡泡机");
stringList.add("蘑菇屋");
bindDataView(stringList);
public void bindDataView(List<String> strings) {
if (flowLayout == null) return;
flowLayout.removeAllViews();
for (int i = 0; i < strings.size(); i++) {
View view = createSingleView(strings.get(i));
flowLayout.addView(view);
}
}
private View createSingleView(String text) {
View view = LayoutInflater.from(this).inflate(R.layout.item_flow, flowLayout, false);
TextView textView = view.findViewById(R.id.tv_content);
textView.setText(text);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
}
});
return view;
}
item_flow.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:background="@drawable/bg_item_flow"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="10dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="优惠券"
android:textColor="#333333"
android:textSize="14dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="10dp"/>
<ImageView
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginLeft="10dp"
android:src="@drawable/xiaolian"/>
</LinearLayout>
网友评论