android自定義流式布局(仿淘寶歷史記錄標簽)

Screenshot_2019-12-06-16-32-12.png

一 概述

當一行顯示不了子view時自動換行,就和淘寶的歷史記錄使用的布局一樣。

二 原理

繪制控件的流程圖

image

如果是重寫viewGroup 則測量自身尺寸的代碼寫在onMeasure里,布局子view位置的代碼寫在onLayout里。

重寫onMeasure步驟:
1 測量所有子view的尺寸
2 遍歷所有子view,計算每個子view在布局里的位置
3設(shè)置布局的尺寸

三 實現(xiàn)

1,在values 新建attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FlowLayout">
        <attr name="maxLine" format="integer"/>
        <attr name="noRightMargin" format="boolean"/>
    </declare-styleable>
</resources>

2,新建FlowLayout類

public class FlowLayout extends ViewGroup {
    protected List<Rect> viewRects;//子view布局
    protected int allChildWidth = 0, allChildHeight = 0;//當前布局包括padding的最大寬高
    int maxLine;//最大行數(shù)
    boolean noRightMargin;//每行最右邊子view是否需要右邊距

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray ary = this.getContext().obtainStyledAttributes(attrs, R.styleable.FlowLayout);
        maxLine = ary.getInt(R.styleable.FlowLayout_maxLine, 0);
        noRightMargin = ary.getBoolean(R.styleable.FlowLayout_noRightMargin,false);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < viewRects.size(); i++) {
            View child = getChildAt(i);
            if (child == null) {
                return;
            }
            Rect rect = viewRects.get(i);
            child.layout(rect.left, rect.top, rect.right, rect.bottom);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        viewRects = new ArrayList<>();
        /*測量子view尺寸*/
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int measureWidth, measureHeight;

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        //預(yù)布局
        compute(widthSize);
        //給定大小和match_parent
        if (widthMode == MeasureSpec.EXACTLY) {
            measureWidth = widthSize;
            //未規(guī)定大小
        } else {
            measureWidth = allChildWidth;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            measureHeight = heightSize;
        } else {
            measureHeight = allChildHeight;
        }
        //設(shè)置布局寬高
        setMeasuredDimension(measureWidth, measureHeight);
    }

    private void compute(int widthSize) {
        boolean aRow = false;//是否換行
        int line = 1;
        int paddingLeft = getPaddingLeft();
        int maxWidth = widthSize - getPaddingRight();//最大寬度
        if (maxWidth <= 0) {
            return;
        }
        int nextRowWidth = paddingLeft;//預(yù)計下一步起點已經(jīng)占寬度

        int nextColumnTopHeight = getPaddingTop();//預(yù)計下一步行頂部所占高度
        int upRowHeight = 0;//上一步最大行高

        MarginLayoutParams margin;
        int left,top,right,bottom;

        for (int i = 0; i < getChildCount(); i++) {

            View child = getChildAt(i);
            int meausredWidth = child.getMeasuredWidth();//view測量寬度
            int measuredHeight = child.getMeasuredHeight();//view測量高度

            margin = (MarginLayoutParams) child.getLayoutParams();//view邊距,必須重寫generateDefaultLayoutParams不然會報錯
            int childMarginLeft = margin.leftMargin;
            int childMarginRight = margin.rightMargin;
            int childMarginTop = margin.topMargin;
            int childMarginBottom = margin.bottomMargin;
            int childWidth = childMarginLeft + meausredWidth + childMarginRight;//view實際寬度
            int childHeight = childMarginTop + measuredHeight + childMarginBottom;//view實際高度

            //預(yù)計本次可能需要布局在下一行
            if (nextRowWidth + childWidth > maxWidth) {
                //布局在本行
                if (noRightMargin && (nextRowWidth + childWidth - childMarginRight <= maxWidth)) {
                    left = nextRowWidth + childMarginLeft;
                    top = nextColumnTopHeight + childMarginTop;
                    right = nextRowWidth + childWidth - childMarginRight;
                    bottom = nextColumnTopHeight + childHeight - childMarginBottom;
                    viewRects.add(new Rect(left, top, right, bottom));

                    //重置數(shù)據(jù)
                    int s = Math.max(childHeight, upRowHeight);//比較高度
                    nextRowWidth = paddingLeft;
                    nextColumnTopHeight += s;
                    allChildHeight = nextColumnTopHeight + getPaddingBottom();
                    upRowHeight = 0;

                    aRow = true;
                    line++;
                    if (line == maxLine) {
                        break;
                    }
                } else if (!noRightMargin || (noRightMargin && (nextRowWidth + childWidth - childMarginRight > maxWidth))) {
                    //布局在下一行
                    if (line == maxLine) {
                        break;
                    }
                    left = paddingLeft + childMarginLeft;
                    top = nextColumnTopHeight + upRowHeight + childMarginTop;
                    right = paddingLeft + childWidth - childMarginRight;
                    bottom = nextColumnTopHeight + upRowHeight + childHeight - childMarginBottom;
                    viewRects.add(new Rect(left, top, right, bottom));

                    //重置數(shù)據(jù)
                    nextColumnTopHeight+= upRowHeight;
                    nextRowWidth = right + childMarginRight;
                    upRowHeight = childHeight;
                    aRow = true;
                    line++;
                    allChildHeight = nextColumnTopHeight + upRowHeight + getPaddingBottom();
                }

            } else {
                //在當前行
                left = nextRowWidth + childMarginLeft;
                top = nextColumnTopHeight + childMarginTop;
                right = nextRowWidth + childWidth - childMarginRight;
                bottom = nextColumnTopHeight + childHeight - childMarginBottom;
                viewRects.add(new Rect(left, top, right, bottom));

                //重置數(shù)據(jù)
                nextRowWidth += childWidth;
                upRowHeight = Math.max(childHeight, upRowHeight);
                allChildHeight = nextColumnTopHeight + upRowHeight + getPaddingBottom();
            }


        }

        if (aRow) {
            allChildWidth = widthSize;
        } else {
            allChildWidth = nextRowWidth;
        }
    }

    //inflater 的時候調(diào)用
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    //addView的時候
    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }

    //child沒有設(shè)置LayoutParams的時候調(diào)用
    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

    //addView的時候
    @Override
    protected boolean checkLayoutParams(LayoutParams p) {
        return p instanceof MarginLayoutParams;
    }

}

3,在xml里

<com.wuzhiping.kitchens.selfView.FlowLayout
        app:maxLine="5"
        android:id="@+id/xun_rage"
        app:noRightMargin="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

4,添加子view

        flow1 = view.findViewById(R.id.xun_rage);
        int mg = 10;
        FlowLayout.MarginLayoutParams pa = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        pa.setMargins(0,mg,mg*4,0);
        for (int i=0;i < 20; i ++){
            TextView item = new TextView(view.getContext());
            item.setText("萬一");
            item.setBackgroundResource(R.drawable.xun_top_edit);
            item.setTextSize(TypedValue.COMPLEX_UNIT_DIP,18);
            item.setPadding(mg,mg,mg,mg);
            flow1.addView(item,pa);
        }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容