說到流式佈局第一時間想起的就是RecyclerView,但是在很多很多種情況下,並不適用於用它。因爲RecyclerView是一個帶滾動的view,而當我們需要多個RecyclerView在一個界面上拼接一起滑動的時候,也就是說可能會遇到ScrollView嵌套RecyclerView的情況下,這個時候無論是改寫RecyclerView的LayoutParams還是重寫RecyclervView的滑動事件都不是一個好主意,因爲RecyclerView是一個佈局複用的控件,改寫了這些東西意味着你將不能再使用複用模式而導致內耗加大,當item不可見時,內存不會回收,從而造成OOM的風險。
所以使用新的方式替代RecyclerView是我們目前唯一的選擇,新的選擇和RecyclerView的最大區別在於是否有滑動事件,也就是說,它不是一個AbsListView,也沒有滑動事件。它是一個ViewGroup,這個GroupView像LinearLayout、RadioGroup一樣是一個可以裝載子控件的佈局集合,我們可以通過動態的添加子控件方式,進行界面效果的實現。
第一步,自定義View,繼承ViewGroup,重寫onMeasure()方法,動態的適配子控件的寬高從而達到流式佈局的效果。
public class FlowLayout extends ViewGroup {
private float mVerticalSpacing; //每個item縱向間距
private float mHorizontalSpacing; //每個item橫向間距
private int mMinimumWidth = 0;
private FlowLayoutAdapter mAdapter;
public FlowLayout(Context context) {
super(context);
this.mMinimumWidth = (ConstantsUtils.DISPLAYW - 100) / 2;
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.mMinimumWidth = (ConstantsUtils.DISPLAYW - 100) / 2;
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mMinimumWidth = (ConstantsUtils.DISPLAYW - 100) / 2;
}
public void setHorizontalSpacing(float pixelSize) {
mHorizontalSpacing = pixelSize;
}
public void setVerticalSpacing(float pixelSize) {
mVerticalSpacing = pixelSize;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int selfWidth = resolveSize(0, widthMeasureSpec);
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int childLeft = paddingLeft;
int childTop = paddingTop;
int lineHeight = 0;
//通過計算每一個子控件的高度,得到自己的高度
for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
View childView = getChildAt(i);
childView.setMinimumWidth(mMinimumWidth);
LayoutParams childLayoutParams = childView.getLayoutParams();
childView.measure(
getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
childLayoutParams.width),
getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
childLayoutParams.height));
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
// 如果子控件有需要設置最小寬度的可以在此重設
if (childWidth < mMinimumWidth){
childView.setMinimumWidth(mMinimumWidth);
}
lineHeight = Math.max(childHeight, lineHeight);
if (childLeft + childWidth + paddingRight > selfWidth) {
childLeft = paddingLeft;
childTop += mVerticalSpacing + lineHeight;
lineHeight = childHeight;
} else {
childLeft += childWidth + mHorizontalSpacing;
}
}
int wantedHeight = childTop + lineHeight + paddingBottom;
setMeasuredDimension(selfWidth, resolveSize(wantedHeight, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int myWidth = r - l;
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int childLeft = paddingLeft;
int childTop = paddingTop;
int lineHeight = 0;
//根據子控件的寬高,計算子控件應該出現的位置。
for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
View childView = getChildAt(i);
if (childView.getVisibility() == View.GONE) {
continue;
}
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
lineHeight = Math.max(childHeight, lineHeight);
if (childLeft + childWidth + paddingRight > myWidth) {
childLeft = paddingLeft;
childTop += mVerticalSpacing + lineHeight;
lineHeight = childHeight;
}
childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + mHorizontalSpacing;
}
}
public void setAdapter(FlowLayoutAdapter adapter){
this.mAdapter = adapter;
mAdapter.init();
}
// 數據適配器
public interface FlowLayoutAdapter{
/** 初始化佈局,可在此方法內動態添加控件*/
void init();
/** 刷新佈局*/
void refresh();
/** 設置動作事件的刷新回調*/
void setChoosed(String name,String value);
}
}
第二步,佈局文件中寫入ScrollView,內包含一個子控件LinearLayout,並在java代碼中動態添加FlowLayout控件。
第三步,重寫FlowLayout的監聽器,並進行監聽調用