热门标签之流式布局

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

发布了23 篇原创文章 · 获赞 10 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/byxyrq/article/details/50498929
今日推荐