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_idsapp: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