熱門標籤之流式佈局

說到流式佈局第一時間想起的就是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的監聽器,並進行監聽調用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章