Android - LinearLayout水平与包装儿童

nik*_*3ro 79 android

是否有为Android的LinearLayout设置的属性,使其能够正确包装子控件?

含义 - 我的孩子数量多变,并希望水平布置,如:

Example: Control1, Control2, Control3, ...

我这样设置:

ll.setOrientation(LinearLayout.HORIZONTAL);
foreach (Child c in children)
  ll.addView(c);

但是,如果我有大量的孩子,最后一个会被切断,而不是去下一行.

知道如何解决这个问题吗?

Ran*_*ku' 55

这应该是你想要的:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
 *
 * @author RAW
 */
public class FlowLayout extends ViewGroup {

    private int line_height;

    public static class LayoutParams extends ViewGroup.LayoutParams {

        public final int horizontal_spacing;
        public final int vertical_spacing;

        /**
         * @param horizontal_spacing Pixels between items, horizontally
         * @param vertical_spacing Pixels between items, vertically
         */
        public LayoutParams(int horizontal_spacing, int vertical_spacing) {
            super(0, 0);
            this.horizontal_spacing = horizontal_spacing;
            this.vertical_spacing = vertical_spacing;
        }
    }

    public FlowLayout(Context context) {
        super(context);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED);

        final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
        int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
        final int count = getChildCount();
        int line_height = 0;

        int xpos = getPaddingLeft();
        int ypos = getPaddingTop();

        int childHeightMeasureSpec;
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
        } else {
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }


        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec);
                final int childw = child.getMeasuredWidth();
                line_height = Math.max(line_height, child.getMeasuredHeight() + lp.vertical_spacing);

                if (xpos + childw > width) {
                    xpos = getPaddingLeft();
                    ypos += line_height;
                }

                xpos += childw + lp.horizontal_spacing;
            }
        }
        this.line_height = line_height;

        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
            height = ypos + line_height;

        } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
            if (ypos + line_height < height) {
                height = ypos + line_height;
            }
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(1, 1); // default of 1px spacing
    }

    @Override
    protected android.view.ViewGroup.LayoutParams generateLayoutParams(
        android.view.ViewGroup.LayoutParams p) {
        return new LayoutParams(1, 1, p);
    }

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

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        final int width = r - l;
        int xpos = getPaddingLeft();
        int ypos = getPaddingTop();

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final int childw = child.getMeasuredWidth();
                final int childh = child.getMeasuredHeight();
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (xpos + childw > width) {
                    xpos = getPaddingLeft();
                    ypos += line_height;
                }
                child.layout(xpos, ypos, xpos + childw, ypos + childh);
                xpos += childw + lp.horizontal_spacing;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

和XML文件

/* you must write your package name and class name */
<org.android.FlowLayout
                android:id="@+id/flow_layout"
                android:layout_marginLeft="5dip"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"/>
Run Code Online (Sandbox Code Playgroud)

  • 没有构造函数与`new LayoutParams(1,1,p)`,不知道为什么人们在没有尝试代码的情况下upvote (9认同)
  • 看来,没有一个赞成者使用过这段代码.因为如果你这样做,它会打破`ClassCastException`.此异常的原因是另一个Stackoverflow问题(http://stackoverflow.com/q/549451/1741542)的不完整复制.如果你添加`generateLayoutParams(ViewGroup.LayoutParams p)`方法,它按预期工作. (5认同)
  • 比接受的kape123答案更好,更优雅. (2认同)

mys*_*obo 46

截至2016年5月,Google已经创建了自己的FlexBoxLayout解决方案.

你可以在这里找到GitHub回购:https://github.com/google/flexbox-layout

  • @fractalwrench如何在答案中包含Java库的"基本部分"? (7认同)

nik*_*3ro 42

对于任何需要这种行为的人:

private void populateLinks(LinearLayout ll, ArrayList<Sample> collection, String header) {

    Display display = getWindowManager().getDefaultDisplay();
    int maxWidth = display.getWidth() - 10;

    if (collection.size() > 0) {
        LinearLayout llAlso = new LinearLayout(this);
        llAlso.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
                LayoutParams.WRAP_CONTENT));
        llAlso.setOrientation(LinearLayout.HORIZONTAL);

        TextView txtSample = new TextView(this);
        txtSample.setText(header);

        llAlso.addView(txtSample);
        txtSample.measure(0, 0);

        int widthSoFar = txtSample.getMeasuredWidth();
        for (Sample samItem : collection) {
            TextView txtSamItem = new TextView(this, null,
                    android.R.attr.textColorLink);
            txtSamItem.setText(samItem.Sample);
            txtSamItem.setPadding(10, 0, 0, 0);
            txtSamItem.setTag(samItem);
            txtSamItem.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    TextView self = (TextView) v;
                    Sample ds = (Sample) self.getTag();

                    Intent myIntent = new Intent();
                    myIntent.putExtra("link_info", ds.Sample);
                    setResult("link_clicked", myIntent);
                    finish();
                }
            });

            txtSamItem.measure(0, 0);
            widthSoFar += txtSamItem.getMeasuredWidth();

            if (widthSoFar >= maxWidth) {
                ll.addView(llAlso);

                llAlso = new LinearLayout(this);
                llAlso.setLayoutParams(new LayoutParams(
                        LayoutParams.FILL_PARENT,
                        LayoutParams.WRAP_CONTENT));
                llAlso.setOrientation(LinearLayout.HORIZONTAL);

                llAlso.addView(txtSamItem);
                widthSoFar = txtSamItem.getMeasuredWidth();
            } else {
                llAlso.addView(txtSamItem);
            }
        }

        ll.addView(llAlso);
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 10

老问题,但万一有人在这里结束,两个库正是这样做的:

https://github.com/blazsolar/FlowLayout

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


Aki*_*fas 5

过去,许多自定义解决方案和库都尝试过并确实解决了这个问题。

\n

从现在开始Constraint Layout 2.0我们可以使用Flow

\n
\n

Flow 是一种新的虚拟布局,用于构建链,当空间不足时,可以包裹到下一行,甚至屏幕的另一部分。当您\xe2\x80\x99在链中布置多个项目但您\xe2\x80\x99不太确定容器在运行时有多大时,这非常有用。您可以使用它来根据应用程序中的动态尺寸构建布局,例如旋转时的屏幕宽度。

\n
\n

xml 如下所示:

\n
<androidx.constraintlayout.helper.widget.Flow\n   android:layout_width="0dp"\n   android:layout_height="wrap_content"\n   app:layout_constraintStart_toStartOf="parent"\n   app:layout_constraintEnd_toEndOf="parent"\n   app:layout_constraintTop_toTopOf="parent"\n   app:flow_wrapMode="chain"\n   app:constraint_referenced_ids="card1, card2, card3"\n   />\n
Run Code Online (Sandbox Code Playgroud)\n

注意app:constraint_referenced_ids和app:flow_wrapMode属性。

\n

我们使用第一个视图传递视图,然后选择如何用第二个视图包装它们。

\n

app:flow_wrapMode接受 3 种不同的选项:

\n

没有任何:create a single chain, overflowing if the content doesn\xe2\x80\x99t fit

\n

链:on overflow, create add another chain for the overflow elements

\n

对齐:similar to chain, but align rows into columns

\n

有关更多详细信息,请查看Android 开发者帖子

\n

和官方文档

\n