1. 程式人生 > >Android-超簡單的流式佈局

Android-超簡單的流式佈局

流式佈局,一般在商城類的專案中用到會非常多比如

淘寶中,購物選擇商品列表的時候,這個就是流式佈局

 

 創作起來也很簡單,

只要你計算出寬度,和高度,如果超出螢幕寬度,則換行擺放即可

然後我就嘗試著寫了一下,果然還是可以的

效果圖

核心方法主要是viewgroup的layout方法

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 為什麼定義x,因為是從這個座標開始出發
        // 往右進行擺放
        int x = (int) (this.getPaddingLeft() + leftMargin);

        int paddingRight = this.getPaddingRight();
        // 為什麼定義y,因為從上往下,top等於y軸線
        int y        = (int) (this.getPaddingTop() + topMargin);
        int sumWidth = r - l;

        int childCount = this.getChildCount();

        int childMaxHeight = 0;
        for (int i = 0; i < childCount; i++) {
            View view = this.getChildAt(i);

            // 如果大於了。需要規整
            if (sumWidth < x + view.getMeasuredWidth() + paddingRight) {
                // 跨行
                // 改變x軸起始點
                x = (int) (getPaddingLeft() + leftMargin);
                // 改變y軸起始點
                y += childMaxHeight;
                childMaxHeight = 0;
            }
            view.layout(x, y, x + view.getMeasuredWidth(), y + view.getMeasuredHeight());
            // 改變橫座標,切記加入view的寬度.否則會出問題
            x += view.getMeasuredWidth() + rightMargin;
            // 取最大寬度,為下一步跨行做準備
            childMaxHeight = (int) Math.max(childMaxHeight, view.getMeasuredHeight() + topMargin + bottomMargin);
        }

    }

其次就是測量方法了.測量方法需要測量出總大小來控制view的大小

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 獲取子view的數量
        int childCount = this.getChildCount();
        // 獲取到本view的寬度最大值
        int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - this.getPaddingLeft() - this.getPaddingRight();
        // 需要測量view的寬度以及view的高度。
        // 所有的合集
        // 總高度
        int sumHeight = 0;
        // 一行的子類總高
        int childMaxHeight = 0;
        // 總寬度
        int sumWidth = 0;
        // 一行的子類總寬
        int sumChildWidth = 0;
        for (int i = 0; i < childCount; i++) {
            View view = this.getChildAt(i);
            measureChild(view, widthMeasureSpec, heightMeasureSpec);
            // 如果有leftMargin的話,需要在測量的時候,加上這個
            sumChildWidth = (int) (sumChildWidth + leftMargin + rightMargin);
            // 如果小於兩者相加,所以超了,需要計算高度
            // 取高度最大值,也就是所有控制元件的最大值
            // 加完之後要清除,否則下一行高度無法計算
            if (maxWidth < (sumChildWidth + view.getMeasuredWidth())) {
                // 跨行
                sumHeight += childMaxHeight;
                childMaxHeight = 0;
                // 跟自己比較,獲取最大值,優先取最大
                sumWidth = Math.max(sumChildWidth, sumChildWidth);
                sumChildWidth = 0;

            }
            // 判斷子類高度最大值
            childMaxHeight = (int) Math.max(childMaxHeight, view.getMeasuredHeight() + topMargin + bottomMargin);
            // 取子類行總寬,需要判斷父類的寬度
            sumChildWidth += view.getMeasuredWidth();
        }
        // 因為最後一行可能沒有超過,所以不會進入,則需要重新加一下最後一行
        sumHeight += childMaxHeight;
        sumWidth = Math.max(sumChildWidth, sumWidth);

        setMeasuredDimension(measureWidth(widthMeasureSpec, sumWidth), measureHeight(heightMeasureSpec, sumHeight));

    }


    private int measureHeight(int heightMeasureSpec, int sumHeight) {
        int result = 0;
        int mode   = MeasureSpec.getMode(heightMeasureSpec);
        int size   = MeasureSpec.getSize(heightMeasureSpec);
        //EXACTLY
        //精確值模式,當控制元件的layout_width和layout_height屬性指定為具體數值或match_parent時。

        //AT_MOST
        //最大值模式,當空間的寬高設定為wrap_content時。

        //UNSPECIFIED
        //未指定模式,View想多大就多大,通常在繪製自定義View時才會用。

        // 如果為精確值模式,那麼不用判斷了,直接返回
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
            return result;
        }
        result = sumHeight + this.getPaddingTop() + this.getPaddingBottom();
        if (mode == MeasureSpec.AT_MOST) {
            result = Math.min(size, result);
        }
        return result;
    }

    private int measureWidth(int widthMeasureSpec, int sumWidth) {
        int result = 0;
        int mode   = MeasureSpec.getMode(widthMeasureSpec);
        int size   = MeasureSpec.getSize(widthMeasureSpec);
        // 如果為精確值模式,那麼不用判斷了,直接返回
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
            return result;
        }
        result = widthMeasureSpec + this.getPaddingLeft() + this.getPaddingRight();
        if (mode == MeasureSpec.AT_MOST) {
            result = Math.min(size, result);
        }
        return result;
    }

演示結果就更簡單了

        int[] colors = {
                Color.DKGRAY,
                Color.GRAY,
                Color.LTGRAY,
                Color.WHITE,
                Color.RED,
                Color.GREEN,
                Color.BLUE,
                Color.YELLOW,
                Color.CYAN,
                Color.MAGENTA,
                Color.TRANSPARENT
        };
        FlowLayout viewById = (FlowLayout) findViewById(R.id.fl);
        viewById.setItemMargin(10, 10, 10, 10);
        for (int i = 0; i < 1000; i++) {
            TextView textView = new TextView(MainActivity.this);
            textView.setText("我是條目 " + i);
            int i1 = new Random().nextInt(colors.length);
            textView.setBackgroundColor(colors[i1]);
            viewById.addView(textView);
        }

詳細可以移步我的github

超簡單的流式佈局