如果 ViewGroup 的 children 超出设备宽度,则强制移动到下一行

Force ViewGroup's children move to next line if they exceed the device width

我想要一个视图组,其中可能有一些水平放置在一起的视图。我的代码的问题是,一切都很好,但是当 children 计数越来越大时,所有 children 水平放置在一起并超过设备宽度。如果它们超过设备宽度,我需要强制它们移动到下一行。

这是我的代码:

<LinearLayout
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="92dp"
    android:layout_marginTop="?attr/actionBarSize"
    android:background="@color/divider"
    android:padding="6dp">

    <LinearLayout
        android:id="@+id/linear_layout_container"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="horizontal"></LinearLayout>

    <ImageButton
        android:id="@+id/image_button_add_item"
        android:layout_width="78dp"
        android:layout_height="match_parent"
        android:background="@color/secondary_text"
        android:contentDescription="@string/content_description_add_new_file"
        android:src="@drawable/ic_plus"/>

</LinearLayout>

注意: linear_layout_container 视图组包含所有视图,它们都是 linearLayout 视图组的 children。

为了将它们移到下一行,您必须进行一些测量,但如果您想要一个简单的解决方案:

  • 你可以直接制作它们 MATCH_PARRENT
  • 或者给它们一个固定的宽度大小并调整重量(你需要用为 MATCH_PARRENT 和水平布局设置的线性布局来包装它们)。
  • 或者您可以使用 2015 年 8 月推出的新 Percentage Layout

LinearLayout 默认以这种方式工作。为了您的目的,您需要一个 FlowLayout。

Romain 家伙的 FlowLayout

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

public class FlowLayout extends ViewGroup {
        private int mHorizontalSpacing;
        private int mVerticalSpacing;
        private Paint mPaint;

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

                TypedArray a = context.obtainStyledAttributes(attrs,
                                R.styleable.FlowLayout);
                try {
                        mHorizontalSpacing = a.getDimensionPixelSize(
                                        R.styleable.FlowLayout_horizontalSpacing, 0);
                        mVerticalSpacing = a.getDimensionPixelSize(
                                        R.styleable.FlowLayout_verticalSpacing, 0);
                } finally {
                        a.recycle();
                }

                mPaint = new Paint();
                mPaint.setAntiAlias(true);
                mPaint.setColor(0xffff0000);
                mPaint.setStrokeWidth(2.0f);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                int widthSize = MeasureSpec.getSize(widthMeasureSpec)
                                - getPaddingRight();
                int widthMode = MeasureSpec.getMode(widthMeasureSpec);

                boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED;

                int width = 0;
                int height = getPaddingTop();

                int currentWidth = getPaddingLeft();
                int currentHeight = 0;

                boolean breakLine = false;
                boolean newLine = false;
                int spacing = 0;

                final int count = getChildCount();
                for (int i = 0; i < count; i++) {
                        View child = getChildAt(i);
                        measureChild(child, widthMeasureSpec, heightMeasureSpec);

                        LayoutParams lp = (LayoutParams) child.getLayoutParams();
                        spacing = mHorizontalSpacing;
                        if (lp.horizontalSpacing >= 0) {
                                spacing = lp.horizontalSpacing;
                        }

                        if (growHeight
                                        && (breakLine || currentWidth + child.getMeasuredWidth() > widthSize)) {
                                height += currentHeight + mVerticalSpacing;
                                currentHeight = 0;
                                width = Math.max(width, currentWidth - spacing);
                                currentWidth = getPaddingLeft();
                                newLine = true;
                        } else {
                                newLine = false;
                        }

                        lp.x = currentWidth;
                        lp.y = height;

                        currentWidth += child.getMeasuredWidth() + spacing;
                        currentHeight = Math.max(currentHeight, child.getMeasuredHeight());

                        breakLine = lp.breakLine;
                }

                if (!newLine) {
                        height += currentHeight;
                        width = Math.max(width, currentWidth - spacing);
                }

                width += getPaddingRight();
                height += getPaddingBottom();

                setMeasuredDimension(resolveSize(width, widthMeasureSpec),
                                resolveSize(height, heightMeasureSpec));
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
                final int count = getChildCount();
                for (int i = 0; i < count; i++) {
                        View child = getChildAt(i);
                        LayoutParams lp = (LayoutParams) child.getLayoutParams();
                        child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y
                                        + child.getMeasuredHeight());
                }
        }

        @Override
        protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
                boolean more = super.drawChild(canvas, child, drawingTime);
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (lp.horizontalSpacing > 0) {
                        float x = child.getRight();
                        float y = child.getTop() + child.getHeight() / 2.0f;
                        canvas.drawLine(x, y - 4.0f, x, y + 4.0f, mPaint);
                        canvas.drawLine(x, y, x + lp.horizontalSpacing, y, mPaint);
                        canvas.drawLine(x + lp.horizontalSpacing, y - 4.0f, x
                                        + lp.horizontalSpacing, y + 4.0f, mPaint);
                }
                if (lp.breakLine) {
                        float x = child.getRight();
                        float y = child.getTop() + child.getHeight() / 2.0f;
                        canvas.drawLine(x, y, x, y + 6.0f, mPaint);
                        canvas.drawLine(x, y + 6.0f, x + 6.0f, y + 6.0f, mPaint);
                }
                return more;
        }

        @Override
        protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
                return p instanceof LayoutParams;
        }

        @Override
        protected LayoutParams generateDefaultLayoutParams() {
                return new LayoutParams(LayoutParams.WRAP_CONTENT,
                                LayoutParams.WRAP_CONTENT);
        }

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

        @Override
        protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
                return new LayoutParams(p.width, p.height);
        }

        public static class LayoutParams extends ViewGroup.LayoutParams {
                int x;
                int y;

                public int horizontalSpacing;
                public boolean breakLine;

                public LayoutParams(Context context, AttributeSet attrs) {
                        super(context, attrs);
                        TypedArray a = context.obtainStyledAttributes(attrs,
                                        R.styleable.FlowLayout_LayoutParams);
                        try {
                                horizontalSpacing = a
                                                .getDimensionPixelSize(
                                                                R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing,
                                                                -1);
                                breakLine = a.getBoolean(
                                                R.styleable.FlowLayout_LayoutParams_layout_breakLine,
                                                false);
                        } finally {
                                a.recycle();
                        }
                }

                public LayoutParams(int w, int h) {
                        super(w, h);
                }
        }

}

只需使用 FlowLayout 扭曲您的视图。

您可以通过扩展 ViewGroup 来编写自己的代码。 github

中有一些图书馆

https://github.com/ApmeM/android-flowlayout