今天在網上看到兩篇關於自定義Flowlayout的實現方式,本身按着兩篇文章的思路也嘗試寫個demo學習一下,最後實現了效果。這個功能實現起來不算太複雜,主要是父容器的高度和換行的計算。下面是我最終實現的效果圖以及源碼.java
package com.hdd.androidreview.customView;
import android.annotation.SuppressLint;
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 List<LineView> mLineViewList;
private int marginLeft = 0;
private int marginTop = 0;
private int marginRight = 0;
private int marginBottom = 0;
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);
}
@SuppressLint("DrawAllocation")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
LineView mLineView = null;
mLineViewList = new ArrayList<>();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int totalHeight = 0;//總高度
int remaining = 0;//剩餘空間
int usedWidth = 0;//已用寬度
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
//先測量子View
measureChild(childView, widthMeasureSpec, widthMeasureSpec);
//獲取Margin值
MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
marginLeft = Math.max(marginLeft, lp.rightMargin);
marginTop = Math.max(marginTop, lp.topMargin);
marginRight = Math.max(marginRight, lp.rightMargin);
marginBottom = lp.bottomMargin;
int childWidth = childView.getMeasuredWidth() + marginLeft + marginRight;
//判斷LineView是否爲空
if (mLineView == null) {
mLineView = new LineView();
mLineViewList.add(mLineView);
}
//計算剩餘空間
remaining = widthSize - usedWidth;
//判斷子view的測量寬度是否大於剩餘空間,若是大於則另起一行
if (childWidth > remaining) {
//另起一行,已用寬度應該爲零
usedWidth = 0;
//添加View
mLineView = new LineView();
mLineViewList.add(mLineView);
}
mLineView.addView(childView);
//已用寬度累加
usedWidth += childWidth;
mLineView.setTotalWidth(usedWidth);
}
for (int i = 0; i < mLineViewList.size(); i++) {
//總高度=全部行數相加
totalHeight += mLineViewList.get(i).mHeight;
}
//父容器的總高度=上下padding的高度,在最後額外加一個底部marginBottom
totalHeight += getPaddingTop() + getPaddingBottom() + marginBottom;
setMeasuredDimension(widthSize, heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = l + getPaddingLeft();
int top = t + getPaddingTop();
for (int i = 0; i < mLineViewList.size(); i++) {
LineView lineView = mLineViewList.get(i);
lineView.layout(left, top);
top += lineView.mHeight;
}
}
public class LineView {
private int mHeight = 0;//View的高
private int totalWidth = 0;//共佔用多少寬度
private List<View> childViews = new ArrayList<>();
public void addView(View child) {
childViews.add(child);
mHeight = child.getMeasuredHeight() + marginTop;
}
//設置這一行共佔用了多少寬度
public void setTotalWidth(int totalWidth) {
this.totalWidth = totalWidth;
}
public void layout(int l, int t) {
int left = l;
int top = t;
int parentWidth = getMeasuredWidth();
//求爲每一個子View平均分配的偏移寬度
int cMeanWith = (parentWidth - totalWidth) / (childViews.size() + 1);
//設置每一個View的位置
for (int i = 0; i < childViews.size(); i++) {
View childView = childViews.get(i);
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
int cLeft = cMeanWith + left + marginLeft;
int cRight = cMeanWith + left + childWidth + marginRight;
int cTop = top + marginTop;
int cBottom = top + childHeight + marginBottom;
//設置View的具體位置
childView.layout(cLeft, cTop, cRight, cBottom);
left += childWidth + marginLeft + marginRight + cMeanWith;
}
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
}
複製代碼
自定義FlowLayout佈局android