上效果圖
package com.zt.flowlayout.weget;
import android.content.Context;
import android.util.AttributeSet;
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);
//縱向分割 寬度
private int mVerticalSpacing = dp2px(8);
private List<List<View>> allLineViews = new ArrayList<>();
private List<Integer> lineHeights = new ArrayList<>();
/**
* 通過new 創(chuàng)建對象
*
* @param context
*/
public FlowLayout(Context context) {
super(context);
}
/**
* 通過反射
*
* @param context
* @param attrs
*/
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 防止內(nèi)存抖動 , 每次清空而不是 重新創(chuàng)建
*/
private void clearMeasureParams() {
allLineViews.clear();
lineHeights.clear();
}
/**
* 框架的寬高
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
clearMeasureParams();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
//獲取父控件的寬度高度
int selfWidth = MeasureSpec.getSize(widthMeasureSpec); //ViewGroup解析的父親給我的寬度
int selfHeight = MeasureSpec.getSize(heightMeasureSpec); // ViewGroup解析的父親給我的高度
//保存當前行所有view
List<View> lineViews = new ArrayList<>();
//當前行高度
int lineHeight = 0;
//當前行寬度
int lineWidthUsed = 0;
int parentNeededWidth = 0; // measure過程中猎物,子View要求的父ViewGroup的寬
int parentNeededHeight = 0; // measure過程中何恶,子View要求的父ViewGroup的高
//獲取所有孩子計算 遞歸計算孩子所需寬高
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
LayoutParams lp = childView.getLayoutParams();
int childMeasureSpecWidth = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, lp.width);
int childMeasureSpecHeight = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, lp.height);
childView.measure(childMeasureSpecWidth, childMeasureSpecHeight);
//當前view 的具體寬 高
int childMeasuredWidth = childView.getMeasuredWidth();
int childMeasuredHeight = childView.getMeasuredHeight();
//判斷是否需要換行
if (lineWidthUsed + childMeasuredWidth + mHorizontalSpacing > selfWidth) {
//保存當前行 所有控件
allLineViews.add(lineViews);
//保存當前行高
lineHeights.add(lineHeight);
//記錄 當前父控件所需寬高
parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed);
parentNeededHeight += lineHeight + mVerticalSpacing;
//當前行 相關(guān)保存數(shù)據(jù) 重置
lineViews = new ArrayList<>();
lineHeight = 0;
lineWidthUsed = 0;
}
//保存當前行所有控件
lineViews.add(childView);
//計算出最大高度//如每個控件高度不一樣
lineHeight = Math.max(lineHeight, childMeasuredHeight);
//當前行寬度
lineWidthUsed = lineWidthUsed + childMeasuredWidth + mHorizontalSpacing;
if (i == childCount - 1) {//防止最后一個控件 是需要換行
//保存當前行 所有控件
allLineViews.add(lineViews);
//保存當前行高
lineHeights.add(lineHeight);
//記錄 當前父控件所需寬高
parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed);
parentNeededHeight += lineHeight;
}
}
//再度量自己,保存
//根據(jù)子View的度量結(jié)果,來重新度量自己ViewGroup
// 作為一個ViewGroup票髓,它自己也是一個View,它的大小也需要根據(jù)它的父親給它提供的寬高來度量
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : parentNeededWidth;
int realHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : parentNeededHeight;
setMeasuredDimension(realWidth, realHeight);
}
/**
* 確定 每個子view 的位置
*
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//確定第一個控件的xy位置 就是 paddingTop 和 paddingLeft w
int curL = getPaddingLeft();
int curT = getPaddingTop();
int lineCount = allLineViews.size();
for (int i = 0; i < lineCount; i++) {
//獲取所有行數(shù) 的控件
List<View> views = allLineViews.get(i);
//獲取當前行高
int height = lineHeights.get(i);
//當前行數(shù)有幾個控件
int nowLienView = views.size();
for (int j = 0; j < nowLienView; j++) {
//確定第一個控件位置
View view = views.get(j);
//調(diào)用過 onMeasure 這個就有值,
//右邊位置 需要當前寬度 加上左邊位置
int curR = view.getMeasuredWidth() + curL;
//下邊位置 需要當前高度 加上上邊位置
int curB = view.getMeasuredHeight() + curT;
view.layout(curL, curT, curR, curB);
//第二個或者之后的 x 軸需要改變也就是 curL 需要加上當前控件的寬 和 橫向分割寬度
curL = curR + mHorizontalSpacing;
}
//第二行 left 需要重置,top 需要加上 上一行的高度 和 縱向分割寬度
curL = getPaddingLeft();
curT = curT + height + mVerticalSpacing;
}
}
public int dp2px(float dpValue) {
float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者