自定義流式佈局

自定義流式佈局,能夠設置margin、padding。子view實現自動換行markdown

image.png

/**
 * FlowLayout自適應容器實現
 */
public class FlowLayout extends ViewGroup {

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    /**
     * (1)什麼時候換行
     * 從效果圖中能夠看到,FlowLayout的佈局是一行行的,若是當前行已經放不下下一個控件,那就把這個控件移到下一行顯示。
     * 因此咱們要有個變量來計算當前行已經佔據的寬度,以判斷剩下的空間是否還能容得下下一個控件。
     * (2)、如何獲得FlowLayout的寬度
     * FlowLayout的寬度是全部行寬度的最大值,因此咱們要記錄下每一行的所佔據的寬度值,進而找到全部值中的最大值。
     * (3)、如何獲得FlowLayout的高度
     * 很顯然,FlowLayout的高度是每一行高度的總和,而每一行的高度則是取該行中全部控件高度的最大值。
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d(FlowLayout.class.getSimpleName(), "onMeasure");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);

        int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        //every line width
        int lineWidth = 0;
        //every line height
        int lineHeight = 0;
        //FlowLayout width
        int width = 0;
        //FlowLayout height
        int height = 0;


        int count = getChildCount();

        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
            int marginLeft = layoutParams.leftMargin;
            int marginRight = layoutParams.rightMargin;
            int marginTop = layoutParams.topMargin;
            int marginBottom = layoutParams.bottomMargin;

            measureChild(child, widthMeasureSpec, heightMeasureSpec);

            int childWidth = child.getMeasuredWidth() + marginLeft + marginRight;
            int childHeight = child.getMeasuredHeight() + marginTop + marginBottom;


            if (lineWidth + childWidth + paddingRight + paddingLeft > measureWidth) {
                //須要換行
                width = Math.max(childWidth, lineWidth);
                height += lineHeight;
                //由於因爲盛不下當前控件,而將此控件調到下一行,因此將此控件的高度和寬度初始化給lineHeight、lineWidth
                lineWidth = childWidth;
                lineHeight = childHeight;
            } else {
                // 不然累加值lineWidth,lineHeight取最大高度
                lineWidth += childWidth;
                lineHeight = Math.max(lineHeight, childHeight);
            }

            //添加最後一行
            if (i == count - 1) {
                width = Math.max(lineWidth, width);
                height += lineHeight;
            }
        }

        //增長內間距
        height = height + paddingTop + paddingBottom;
        width = width + paddingLeft + paddingRight;

        setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? measureWidth : width, (measureHeightMode == MeasureSpec.EXACTLY) ? measureHeight : height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int count = getChildCount();
        //累加當前行的行寬
        int lineWidth = 0;
        //當前行的行高
        int lineHeight = 0;
        //當前座標的top座標和left座標
        int top = paddingTop, left = paddingLeft;

        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);

            MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
            int marginLeft = layoutParams.leftMargin;
            int marginRight = layoutParams.rightMargin;
            int marginTop = layoutParams.topMargin;
            int marginBottom = layoutParams.bottomMargin;

            int childWidth = child.getMeasuredWidth() + marginLeft + marginRight;
            int childHeight = child.getMeasuredHeight() + marginTop + marginBottom;

            if (childWidth + lineWidth + paddingRight + paddingLeft> getMeasuredWidth()) {
                //若是換行,當前控件將跑到下一行,從最左邊開始,因此left就是paddingLeft,而top則須要加上上一行的行高,纔是這個控件的top點;
                top += lineHeight;
                left = paddingLeft;

                //一樣,從新初始化lineHeight和lineWidth
                lineHeight = childHeight;
                lineWidth = childWidth;

            } else {
                // 不然累加值lineWidth,lineHeight取最大高度
                lineWidth += childWidth;
                lineHeight = Math.max(lineHeight, childHeight);
            }

            int lp = left + marginLeft;
            int tp = top + marginTop;
            int rp = lp + child.getMeasuredWidth();
            int bp = tp + child.getMeasuredHeight();


            child.layout(lp, tp, rp, bp);

            //將left置爲下一子控件的起始點
            left += childWidth;
        }
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }
}
複製代碼

其中須要重寫 重寫generateLayoutParams()函數,才能實現子View設置Marginide

@Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }
複製代碼

原理以下: 首先,在container在初始化子控件時,會調用LayoutParams generateLayoutParams(LayoutParams p)來爲子控件生成對應的佈局屬性,但默認只是生成layout_width和layout_height因此對應的佈局參數,即在正常狀況下的generateLayoutParams()函數生成的LayoutParams實例是不可以取到margin值的.函數

/**
*從指定的XML中獲取對應的layout_width和layout_height值
*/
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LayoutParams(getContext(), attrs);
}
/*
*若是要使用默認的構造方法,就生成layout_width="wrap_content"、layout_height="wrap_content"對應的參數
*/
protected LayoutParams generateDefaultLayoutParams() {
     return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
複製代碼
相關文章
相關標籤/搜索