自定义View,流式布局实践
一、效果图:
二、FlowLayout实现
package com.example.widgetsview; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; public class FlowLayout extends ViewGroup { private int mHorizontalSpacing = dp2px(16); //每个item横向间距 private int mVerticalSpacing = dp2px(8); //每个item纵向间距 private List<List<View>> allLines = new ArrayList<>(); // 记录所有的行,一行一行的存储,用于layout private List<Integer> lineHeights = new ArrayList<>(); // 记录每一行的行高,用于layout 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); } public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } private void clearMeasureParams() { allLines.clear(); lineHeights.clear(); } /* * 1、度量子view * 2、获取子view的宽、高、换行等 * 3、向父类索要宽高,判断是哪种MeasureSpecMode,根据不同的mode给出不同的区域 * 4、保存记录 * */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ clearMeasureParams(); List<View> lineViews = new ArrayList<>(); // 保存一行中的所有的view int lineWidthUsed = 0; // 记录这行已经使用了多宽的size int lineHeight = 0; // 一行的行高 int selfWidth = MeasureSpec.getSize(widthMeasureSpec); // ViewGrooup解析的父亲给我的宽度,父view给我的宽度 int selfHeight = MeasureSpec.getSize(heightMeasureSpec); // ViewGroup解析的父亲给我的高度,父view给我的高度 int flowLayoutNeedWidth = 0; // measure过程中,FlowLayout要求的父ViewGroup的宽 int flowLayoutNeedHeight = 0; // measure过程中,FlowLayout要求的父ViewGroup的高 // 度量孩子大小 int childCount = getChildCount(); // 获取子view数 int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); for (int i = 0; i < childCount; i++){ View childView = getChildAt(i); // 获取子view的layoutParams,通过layoutParams可得到子view的宽高具体值或者MATCH_PARENT还是WRAP_CONTENT LayoutParams childLp = childView.getLayoutParams(); // xml里的layout_width等 if (childView.getVisibility() != View.GONE) { //将layoutParams转变成为 measureSpec 即设置子view的measureSpec /* * widthMeasureSpec表示父view给予FlowLayout的宽度 * paddingLeft + paddingRight表示父view所设置的左右padding值 * childLP.width 表示 子view的宽度 * */ int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, childLp.width); int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, childLp.height); // 通过子view的measureSpec度量子view childView.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 获取子view的度量宽高 int childViewMeasuredWidth = childView.getMeasuredWidth(); int childViewMeasuredHeight = childView.getMeasuredHeight(); // 换行, 通过宽度来判断是否需要换行,通过换行后的每行的行高来获取整个ViewGroup的行高 // 如果需要换行 if (childViewMeasuredWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) { //一旦换行,我们就可以判断当前行需要的宽和高,所以此时要记录下来 allLines.add(lineViews); lineHeights.add(lineHeight); //判断flowLayout到底需要多宽、多高 flowLayoutNeedWidth = Math.max(flowLayoutNeedWidth, lineWidthUsed); flowLayoutNeedHeight = flowLayoutNeedHeight + lineHeight + mVerticalSpacing; // 换行后初始化 // 此处不能用clear,用clear则allLines里面的item所指向的就是同一个内存地址了 lineViews = new ArrayList<>(); lineWidthUsed = 0; lineHeight = 0; } //每行的设置,// 将节点布局进去,view是分行layout的,所以要记录每一行有哪些view,这样可以方便layout布局 lineViews.add(childView); lineWidthUsed = lineWidthUsed + childViewMeasuredWidth + mHorizontalSpacing ; lineHeight = Math.max(lineHeight, childViewMeasuredHeight); //最后一行数据(因为最后一行的时候到不了换行的那句代码,所以不会显示,因此要单独判断) if(i == childCount - 1){ allLines.add(lineViews); lineHeights.add(lineHeight); //判断flowLayout到底需要多宽、多高 flowLayoutNeedWidth = Math.max(flowLayoutNeedWidth, lineWidthUsed); flowLayoutNeedHeight = flowLayoutNeedHeight + lineHeight + mVerticalSpacing; } } } //根据子View的度量结果,来重新度量自己ViewGroup, 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父view给它提供的宽高来度量 //首先获取到父view的MeasureSpec的mode int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); // 如果父view的MeasureSpec的mode是EXACTLY表示宽度是确切的,则selfWidth为最终宽度,否则为 int flowLayoutWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : flowLayoutNeedWidth; int flowLayoutHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : flowLayoutNeedHeight; // 保存记录 setMeasuredDimension(flowLayoutWidth, flowLayoutHeight); } // 布局: 所有的子View进行布局(每一行每一行的布局) @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 获取行数 int lineCount = allLines.size(); // 获取flowLayout所设置的pandding值,布局从左上角开始 int curL = getPaddingLeft(); int curT = getPaddingTop(); for (int i = 0; i < lineCount; i++) { // 获取到每一行的所有view List<View> lineViews = allLines.get(i); for (int j = 0; j < lineViews.size(); j++){ //获取单个view View view = lineViews.get(j); //设置view的视图坐标系, int left = curL; int top = curT; int right = left + view.getMeasuredWidth(); // 度量之前必须用getMeasuredWidth不能用getWidth() int bottom = top + view.getMeasuredHeight(); // view添加到布局 view.layout(left,top,right,bottom); // 计算下一个view的宽度的开始位置 curL = right + mHorizontalSpacing; } // 宽度位置初始化 curL = getPaddingLeft(); // 计算下一行view的高度的开始位置 curT = curT + lineHeights.get(i) + mVerticalSpacing; } } // 动画:1s 12帧 @Override protected void onDraw(Canvas canvas){ } public static int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics()); } }
三、布局activity xml文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginTop="10dp" android:layout_marginBottom="30dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:text="搜索历史" android:textColor="@android:color/black" android:textSize="18sp"/> <com.example.widgetsview.FlowLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="4dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="欧美影视" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="婚姻育儿" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="散文" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="程序员" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="大学生生活" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="运营互助帮" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="设计" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="读书" android:textColor="#130F01" /> </com.example.widgetsview.FlowLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:text="热门搜索" android:textColor="@android:color/black" android:textSize="18sp"/> <com.example.widgetsview.FlowLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="4dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="新冠疫情最新资讯" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="新冠疫情最新资讯test" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="散文" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="程序员" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="大学生生活" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="运营互助帮" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="设计" android:textColor="#130F01" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FDF0C7" android:padding="5dp" android:text="读书" android:textColor="#130F01" /> </com.example.widgetsview.FlowLayout> </LinearLayout> </LinearLayout>