说到流式布局第一时间想起的就是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的监听器,并进行监听调用