效果图:
细节方面没有考虑
java:
public class FlowLayout extends ViewGroup {
private static final String TAG = "FlowLayout";
private int mHorizontalSpacing = dq2px(16);
private int mVerticlaSpacing = dq2px(8);
private List<List<View>> allLines; //记录所有行的View
private List<Integer> lineHeights = new ArrayList();//记录每一行的行高
public FlowLayout(Context context) {
super(context);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
initMeasureParams();
//测量所有子View的大小
int childCount = getChildCount();
int paddingLeft = getPaddingLeft();
int paddingRigth = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int selfWidth = MeasureSpec.getSize(widthMeasureSpec);
int selfHeight = MeasureSpec.getSize(heightMeasureSpec);
List<View> lineViews = new ArrayList<>();
//保存一行中所有的View
int lineWidthUsed = 0;
//记录每一行已经使用的宽度
int lineHeight = 0;
//一行的高度
int parentNeededWidth = 0;
int parentNeededHeight = 0;
//meauser过程中,子View要求的父ViewGroup的宽高
//遍历所有的子View
for(int i=0;i<childCount;i++){
View childView = getChildAt(i);
LayoutParams childLP = childView.getLayoutParams();
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,paddingLeft+paddingRigth,childLP.width);
int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,paddingTop+paddingBottom,childLP.height);
childView.measure(childWidthMeasureSpec,childHeightMeasureSpec);
//获取view的宽高
int childMeausreWidth = childView.getMeasuredWidth();
int childMeasureHeight = childView.getMeasuredHeight();
if(childMeausreWidth + lineWidthUsed + mHorizontalSpacing > selfWidth){
allLines.add(lineViews);
lineHeights.add(lineHeight);
parentNeededHeight = parentNeededHeight + lineHeight +mVerticlaSpacing;
parentNeededWidth = Math.max(parentNeededWidth,lineWidthUsed+mHorizontalSpacing);
//集合重置
lineViews = new ArrayList<>();
lineWidthUsed = 0;
lineHeight = 0;
}
lineViews.add(childView);
lineWidthUsed = lineWidthUsed + childMeausreWidth + mHorizontalSpacing;
lineHeight = Math.max(lineHeight,childMeasureHeight);
//判断尾行
if(i == childCount -1){
lineHeights.add(lineHeight);
allLines.add(lineViews);
parentNeededWidth = Math.max(parentNeededWidth,lineWidthUsed);
parentNeededHeight += lineHeight;
}
}
//根据子View的度量结果 来重新度量自己
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);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int lineCount = allLines.size();
int curL = 0;
int curT = 0;
for(int i=0;i<lineCount;i++){
List<View> lineViews = allLines.get(i);
int lineHeight = lineHeights.get(i);
for(int j=0;j<lineViews.size();j++){
View view = lineViews.get(j);
int left1 = curL;
int top1 = curT;
int bottom1 = top1 +view.getMeasuredHeight();
int right1 = left1 +view.getMeasuredWidth();
view.layout(left1,top1,right1,bottom1);
curL = right1 + mHorizontalSpacing;
}
curL=0;
curT = curT + lineHeight + mVerticlaSpacing;
}
}
private void initMeasureParams(){
allLines = new ArrayList<>();
lineHeights = new ArrayList<>();
}
public static int dq2px(int dp){
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp, Resources.getSystem().getDisplayMetrics());
}
}
kotiln:
class FlowLayout : ViewGroup {
var views:ArrayList<ArrayList<View>>?=null//记录子View
var lineHeights :ArrayList<Int>?=null //记录行高
var mHorizontalSpacing = dq2px(0)//子view横向间距
var mVerticalSapcing = dq2px(8)//子view行间距
//父类有参构造
constructor(context: Context):super(context){}
constructor(context: Context,attributeSet: AttributeSet):super(context,attributeSet){}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
initMeasureParams()
var mPaddingLeft = getPaddingLeft()
var mPaddingTop = paddingTop
var mPaddingRight = paddingRight
var mPaddingBottom = paddingBottom
var lineViews:ArrayList<View> = arrayListOf()
var lineWidthUsed = 0
var lineHeight = 0
var parentNeededWidth=0
var parentNeededHeight=0
var selfWidth = MeasureSpec.getSize(widthMeasureSpec)
var selfHeight = MeasureSpec.getSize(heightMeasureSpec)
for(i in 0 until childCount){
println("childCount :$childCount")
val mChildView:View = getChildAt(i)
var mChildViewLP = mChildView?.layoutParams
//根据父容器的MeasureSpec 自身宽高 以及布局参数 得到 childView的MeasureSpec
val childWidthMS = getChildMeasureSpec(
widthMeasureSpec,
mPaddingLeft + mPaddingRight,
mChildViewLP.width
)
val childHeightMS = getChildMeasureSpec(
heightMeasureSpec,
mPaddingTop + mPaddingBottom,
mChildViewLP.height
)
mChildView.measure(childWidthMS,childHeightMS)
val childMeasuredWidth = mChildView.measuredWidth
val childMeasuredHeight = mChildView.measuredHeight
if(childMeasuredWidth+lineWidthUsed+mHorizontalSpacing>selfWidth){
views?.add(lineViews)
lineHeights?.add(lineHeight)
//一行布局结束
//判断当前行需要的宽和高
parentNeededHeight = parentNeededHeight+lineHeight+mVerticalSapcing
parentNeededWidth = Math.max(parentNeededHeight,lineWidthUsed+mHorizontalSpacing)
lineViews = arrayListOf()
lineWidthUsed = 0
lineHeight = 0
}
lineViews.add(mChildView)
lineWidthUsed = lineWidthUsed+childMeasuredWidth+mHorizontalSpacing
lineHeight = Math.max(lineHeight,childMeasuredHeight)
//如果当前子view 是最后一行的最后一个
if(i == childCount-1){
lineHeights?.add(lineHeight)
views?.add(lineViews)
println("views :${views?.size}")
parentNeededWidth = Math.max(parentNeededWidth,lineWidthUsed)
parentNeededHeight+=lineHeight
}
}
//根据子view的度量结果来度量自己
var widthMode = MeasureSpec.getMode(widthMeasureSpec)
var heightMode = MeasureSpec.getMode(heightMeasureSpec)
var realWidth = when(widthMode){
MeasureSpec.EXACTLY -> selfWidth
else -> parentNeededWidth
}
var realHeight = when(heightMode){
MeasureSpec.EXACTLY -> selfHeight
else -> parentNeededHeight
}
setMeasuredDimension(realWidth,realHeight)
}
override fun onLayout(p0: Boolean, p1: Int, p2: Int, p3: Int, p4: Int) {
var lineCount = views?.size
println("lineCount:$lineCount---lineHeights;${lineHeights?.size}")
var curLeft = 0
var curTop = 0
for(i in 0 until lineCount!!){
var lineViews = views?.get(i)
val lineHeight = lineHeights?.get(i)
for (j in 0 until lineViews?.size!!){
val view = lineViews.get(j)
var left = curLeft
var top = curTop
var bottom = top+view.measuredHeight
var right = left+view.measuredWidth
view.layout(left,top,right,bottom)
curLeft = right+mHorizontalSpacing
}
curLeft = 0
curTop = curTop+lineHeight!!+mVerticalSapcing
}
}
fun initMeasureParams(){
views = arrayListOf(arrayListOf())
lineHeights = arrayListOf()
}
fun dq2px(dp:Int):Int{
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp.toFloat(),Resources.getSystem().displayMetrics)?.toInt()
}
}