先來張效果圖
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 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); } 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); //父view給我的寬度 int selfHeight = MeasureSpec.getSize(heightMeasureSpec); //父view給我的高度 int flowLayoutNeedWidth = 0; // measure過程中,FlowLayout要求的父ViewGroup的寬 int flowLayoutNeedHeight = 0; // measure過程中,FlowLayout要求的父ViewGroup的高 int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); // 獲取子view數 int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { // 獲取子view View childView = getChildAt(i); // 獲取子view的layoutParams,通過layoutParams可得到子view的寬高具體值或者MATCH_PARENT還是WRAP_CONTENT LayoutParams childLP = childView.getLayoutParams(); 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 childMeasureWidth = childView.getMeasuredWidth(); int childMeasureHeight = childView.getMeasuredHeight(); //換行 if(lineWidthUsed + mHorizontalSpacing + childMeasureWidth > 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; } //每行的設置 lineViews.add(childView); lineWidthUsed = lineWidthUsed + mHorizontalSpacing + childMeasureWidth; lineHeight = Math.max(lineHeight, childMeasureHeight); //最后一行數據(因為最后一行的時候到不了換行的那句代碼,所以不會顯示,因此要單獨判斷) 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); } // 布局(每一行每一行的布局) @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(); int bottom = top + view.getMeasuredHeight(); // view添加到布局 view.layout(left,top,right,bottom); // 計算下一個view的寬度的開始位置 curL = right + mHorizontalSpacing; } // 計算下一行view的高度的開始位置 curT = curT + lineHeights.get(i) + mVerticalSpacing; // 寬度位置初始化 curL = getPaddingLeft(); } } public static int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics()); } }