Android - LinearLayout水平与包装儿童

时间:2010-06-02 21:36:04

标签: android

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

含义 - 我有多少个孩子,并希望将它们水平放置,如:

Example: Control1, Control2, Control3, ...

我通过设置:

来做到这一点
ll.setOrientation(LinearLayout.HORIZONTAL);
foreach (Child c in children)
  ll.addView(c);

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

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

11 个答案:

答案 0 :(得分:54)

这应该是你想要的:

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;
            }
        }
    }
}

和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"/>

答案 1 :(得分:43)

截至2016年5月,Google已创建了自己的FlexBoxLayout,可以解决您的问题。

您可以在此处找到GitHub回购:https://github.com/google/flexbox-layout

答案 2 :(得分: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);
    }
}

答案 3 :(得分:10)

老问题,但如果有人在这里结束,两个库就是这样做的:

https://github.com/blazsolar/FlowLayout

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

答案 4 :(得分:1)

寻找类似但更简单的问题的解决方案,即将子文本内容包装在水平布局中。 kape123的解决方案运行正常。但是使用ClickableSpan找到一个更简单的问题。 也许它可能对一些简单的情况有用。 片段:

        String[] stringSource = new String[sourceList.size()];
        for (int i = 0; c < sourceList.size(); i++) {
            String text = sourceList.get(i);
            stringSource[i] = text;
        }

        SpannableString totalContent = new SpannableString(TextUtils.join(",", stringSource));
        int start = 0;
        for (int j = 0; j < stringSource.length(); j++) {
            final String text = stringSource[j];
            ClickableSpan span = new ClickableSpan() {

        @Override
                public void updateDrawState(TextPaint ds) {
                    ds.setUnderlineText(true);
                    ds.setColor(getResources().getColor(R.color.green));
                }
                @Override
                public void onClick(View widget) {
                    // the text clicked
                }
            };
    int end = (start += text.length());
            totalContent.setSpan(span, start, end, 0);
            star = end + 1;
        }

        TextView wrapperView = (TextView) findViewById(horizontal_container_id);
        wrapperView.setMovementMethod(LinkMovementMethod.getInstance());

        wrapperView.setText(totalContent, BufferType.SPANNABLE);
    }

答案 5 :(得分:0)

//this method will add image view to liner grid and warp it if no space in new child LinearLayout grid 
private void addImageToLinyerLayout(LinearLayout ll , ImageView v)
{
    //set the padding and margin and weight 
    v.setPadding(5, 5, 5, 5);

    Display display = getWindowManager().getDefaultDisplay();
    int maxWidth = display.getWidth() - 10;
    int maxChildeNum = (int) ( maxWidth / (110)) ; 
    Toast.makeText(getBaseContext(), "c" + v.getWidth() ,
            Toast.LENGTH_LONG).show();
    //loop through all child of the LinearLayout
    for (int i = 0; i < ll.getChildCount(); i++) {
        View chidv = ll.getChildAt(i);
        Class c = chidv.getClass();
        if (c == LinearLayout.class) {
            //here we are in the child lay out check to add the imageView if there is space 
            //Available else we will add it to new linear layout 
            LinearLayout chidvL = (LinearLayout)chidv; 
            if(chidvL.getChildCount() < maxChildeNum)
            {
                chidvL.addView(v);
                return;
            }
        } else{
           continue;
        } 
    }

    //if you reached here this means there was no roam for adding view so we will 
    //add new linear layout 
    LinearLayout childLinyer = new LinearLayout(this);
    childLinyer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
            LayoutParams.WRAP_CONTENT));

    childLinyer.setOrientation(LinearLayout.HORIZONTAL);
    ll.addView(childLinyer);
    childLinyer.addView(v);

}

上面的方法会像agrid和你的布局一样并排添加imgeview

  <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" 
    android:id="@+id/imageslayout"
    ></LinearLayout>

我发布此解决方案可能它可以帮助一些并保存一次,我在我的应用程序中使用它

答案 6 :(得分:0)

我最终使用TagView

<com.cunoraz.tagview.TagView
        android:id="@+id/tag_group"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp" />




  TagView tagGroup = (TagView)findviewById(R.id.tag_view);
 //You can add one tag
 tagGroup.addTag(Tag tag);
 //You can add multiple tag via ArrayList
 tagGroup.addTags(ArrayList<Tag> tags);
 //Via string array
 addTags(String[] tags);


   //set click listener
      tagGroup.setOnTagClickListener(new OnTagClickListener() {
            @Override
            public void onTagClick(Tag tag, int position) {
            }
        });

   //set delete listener
            tagGroup.setOnTagDeleteListener(new OnTagDeleteListener() {
            @Override
            public void onTagDeleted(final TagView view, final Tag tag, final int position) {
            }
        });

enter image description here

答案 7 :(得分:0)

Randy Sugianto 'Yuku's answer中代码的修改版本,以及我最终得到的内容:

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.View.MeasureSpec.*
import android.view.ViewGroup
import androidx.core.content.withStyledAttributes
import androidx.core.view.children
import *.*.*.R


class FlowLayout(context: Context, attributeSet: AttributeSet) : ViewGroup(context, attributeSet) {

    private var lineHeight: Int = 0

    private var horizontalSpacing = 0F
    private var verticalSpacing = 0F

    init {
        context.withStyledAttributes(attributeSet, R.styleable.FlowLayout) {
            horizontalSpacing = getDimension(R.styleable.FlowLayout_horizontalSpacing, 0F)
            verticalSpacing = getDimension(R.styleable.FlowLayout_verticalSpacing, 0F)
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

        val width = getSize(widthMeasureSpec) - paddingLeft - paddingRight
        var height = getSize(heightMeasureSpec) - paddingTop - paddingBottom

        var xPosition = paddingLeft
        var yPosition = paddingTop

        val childHeightMeasureSpec = makeMeasureSpec(
            height, if (getMode(heightMeasureSpec) == AT_MOST) AT_MOST else UNSPECIFIED
        )

        children.forEach { child ->
            if (child.visibility != GONE) {
                val layoutParams = child.layoutParams as LayoutParamsWithSpacing
                child.measure(makeMeasureSpec(width, AT_MOST), childHeightMeasureSpec)
                val childWidth = child.measuredWidth
                lineHeight =
                        Math.max(lineHeight, child.measuredHeight + layoutParams.verticalSpacing)

                if (xPosition + childWidth > width) {
                    xPosition = paddingLeft
                    yPosition += lineHeight
                }

                xPosition += childWidth + layoutParams.horizontalSpacing
            }
        }

        if (getMode(heightMeasureSpec) == UNSPECIFIED ||
            getMode(heightMeasureSpec) == AT_MOST && yPosition + lineHeight < height
        ) {
            height = yPosition + lineHeight
        }

        setMeasuredDimension(width, height)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        val width = right - left
        var xPosition = paddingLeft
        var yPosition = paddingTop

        children.forEach { child ->
            if (child.visibility != View.GONE) {
                val layoutParams = child.layoutParams as LayoutParamsWithSpacing
                val childWidth = child.measuredWidth
                if (xPosition + childWidth > width) {
                    xPosition = paddingLeft
                    yPosition += lineHeight
                }
                child.layout(
                    xPosition, yPosition, xPosition + childWidth,
                    yPosition + child.measuredHeight
                )
                xPosition += layoutParams.horizontalSpacing
                xPosition += childWidth
            }
        }
    }

    override fun generateDefaultLayoutParams(): ViewGroup.LayoutParams =
        LayoutParamsWithSpacing(1, 1)

    override fun generateLayoutParams(layoutParams: LayoutParams) =
        LayoutParamsWithSpacing(horizontalSpacing.toInt(), verticalSpacing.toInt())

    override fun checkLayoutParams(layoutParams: LayoutParams) =
        layoutParams is LayoutParamsWithSpacing

    class LayoutParamsWithSpacing(val horizontalSpacing: Int, val verticalSpacing: Int) :
        ViewGroup.LayoutParams(0, 0)
}

在style / attrs.xml文件中:

<resources>
    <declare-styleable name="FlowLayout">
        <attr name="horizontalSpacing" format="dimension" />
        <attr name="verticalSpacing" format="dimension" />
    </declare-styleable>
</resources>

用法:

<*.*.*.*.FlowLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:horizontalSpacing="8dp"
    app:verticalSpacing="8dp">

    <!-- ... -->

</*.*.*.*.FlowLayout>

答案 8 :(得分:0)

Google提供了自己的解决方案:FlowLayout类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.google.android.material.internal;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.core.view.MarginLayoutParamsCompat;
import androidx.core.view.ViewCompat;
import com.google.android.material.R.styleable;

@RestrictTo({Scope.LIBRARY_GROUP})
public class FlowLayout extends ViewGroup {
  private int lineSpacing;
  private int itemSpacing;
  private boolean singleLine;

  public FlowLayout(Context context) {
    this(context, (AttributeSet)null);
  }

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

  public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.singleLine = false;
    this.loadFromAttributes(context, attrs);
  }

  @TargetApi(21)
  public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    this.singleLine = false;
    this.loadFromAttributes(context, attrs);
  }

  private void loadFromAttributes(Context context, AttributeSet attrs) {
    TypedArray array = context.getTheme().obtainStyledAttributes(attrs, styleable.FlowLayout, 0, 0);
    this.lineSpacing = array.getDimensionPixelSize(styleable.FlowLayout_lineSpacing, 0);
    this.itemSpacing = array.getDimensionPixelSize(styleable.FlowLayout_itemSpacing, 0);
    array.recycle();
  }

  protected int getLineSpacing() {
    return this.lineSpacing;
  }

  protected void setLineSpacing(int lineSpacing) {
    this.lineSpacing = lineSpacing;
  }

  protected int getItemSpacing() {
    return this.itemSpacing;
  }

  protected void setItemSpacing(int itemSpacing) {
    this.itemSpacing = itemSpacing;
  }

  protected boolean isSingleLine() {
    return this.singleLine;
  }

  public void setSingleLine(boolean singleLine) {
    this.singleLine = singleLine;
  }

  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int maxWidth = widthMode != -2147483648 && widthMode != 1073741824 ? 2147483647 : width;
    int childLeft = this.getPaddingLeft();
    int childTop = this.getPaddingTop();
    int childBottom = childTop;
    int maxChildRight = 0;
    int maxRight = maxWidth - this.getPaddingRight();

    int finalWidth;
    for(finalWidth = 0; finalWidth < this.getChildCount(); ++finalWidth) {
      View child = this.getChildAt(finalWidth);
      if (child.getVisibility() != 8) {
        this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
        LayoutParams lp = child.getLayoutParams();
        int leftMargin = 0;
        int rightMargin = 0;
        if (lp instanceof MarginLayoutParams) {
          MarginLayoutParams marginLp = (MarginLayoutParams)lp;
          leftMargin += marginLp.leftMargin;
          rightMargin += marginLp.rightMargin;
        }

        int childRight = childLeft + leftMargin + child.getMeasuredWidth();
        if (childRight > maxRight && !this.isSingleLine()) {
          childLeft = this.getPaddingLeft();
          childTop = childBottom + this.lineSpacing;
        }

        childRight = childLeft + leftMargin + child.getMeasuredWidth();
        childBottom = childTop + child.getMeasuredHeight();
        if (childRight > maxChildRight) {
          maxChildRight = childRight;
        }

        childLeft += leftMargin + rightMargin + child.getMeasuredWidth() + this.itemSpacing;
      }
    }

    finalWidth = getMeasuredDimension(width, widthMode, maxChildRight);
    int finalHeight = getMeasuredDimension(height, heightMode, childBottom);
    this.setMeasuredDimension(finalWidth, finalHeight);
  }

  private static int getMeasuredDimension(int size, int mode, int childrenEdge) {
    switch(mode) {
    case -2147483648:
      return Math.min(childrenEdge, size);
    case 1073741824:
      return size;
    default:
      return childrenEdge;
    }
  }

  protected void onLayout(boolean sizeChanged, int left, int top, int right, int bottom) {
    if (this.getChildCount() != 0) {
      boolean isRtl = ViewCompat.getLayoutDirection(this) == 1;
      int paddingStart = isRtl ? this.getPaddingRight() : this.getPaddingLeft();
      int paddingEnd = isRtl ? this.getPaddingLeft() : this.getPaddingRight();
      int childStart = paddingStart;
      int childTop = this.getPaddingTop();
      int childBottom = childTop;
      int maxChildEnd = right - left - paddingEnd;

      for(int i = 0; i < this.getChildCount(); ++i) {
        View child = this.getChildAt(i);
        if (child.getVisibility() != 8) {
          LayoutParams lp = child.getLayoutParams();
          int startMargin = 0;
          int endMargin = 0;
          if (lp instanceof MarginLayoutParams) {
            MarginLayoutParams marginLp = (MarginLayoutParams)lp;
            startMargin = MarginLayoutParamsCompat.getMarginStart(marginLp);
            endMargin = MarginLayoutParamsCompat.getMarginEnd(marginLp);
          }

          int childEnd = childStart + startMargin + child.getMeasuredWidth();
          if (!this.singleLine && childEnd > maxChildEnd) {
            childStart = paddingStart;
            childTop = childBottom + this.lineSpacing;
          }

          childEnd = childStart + startMargin + child.getMeasuredWidth();
          childBottom = childTop + child.getMeasuredHeight();
          if (isRtl) {
            child.layout(maxChildEnd - childEnd, childTop, maxChildEnd - childStart - startMargin, childBottom);
          } else {
            child.layout(childStart + startMargin, childTop, childEnd, childBottom);
          }

          childStart += startMargin + endMargin + child.getMeasuredWidth() + this.itemSpacing;
        }
      }

    }
  }
}

该类的工作原理与上述FlowLayout类相似 但是您不应该在项目中添加任何新类, 并且设计器在该类上的效果比在自定义上更好

答案 9 :(得分:0)

我想要一个非常简单的解决方案,该解决方案灵活(这就是我使用LinearLayouts的原因)。这就是我想出的。

https://github.com/ShalakoSnell/Wrapping_Linear_Layout

注意:我提供了一个使用textviews的示例方法(请参见textViewArrayListForExample()) XML只是具有ID和垂直方向的父视图LinearLayout,不需要其他任何操作。 使用方法:传递封装在LinearLayouts中的视图数组以及父视图和上下文。 (请参见viewAdapterArrayList(ArrayList textViews))

传入LinearLines数组是使此方法如此灵活的原因,因为它允许您添加不同的视图类型。因此,在第一个LinearLayout中可以有文本,在第二个中可以有图像,然后在第三个中可以有按钮,依此类推...

Portrait example Landscape example 50dp margins in verticalLinearLayout (对不起,我还无法添加图片...请参阅链接。

MainActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new WrappingLinearLayout(
                viewAdapterArrayList(textViewArrayListForExample()), // <-- replace this with you own array of LinearLayouts
                (LinearLayout) findViewById(R.id.verticalLinearLayout),
                this);
    }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/verticalLinearLayout"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

</LinearLayout>

WrappingLinearLayout.Java

package com.example.wrapping_linear_layout;

import android.content.Context;
import android.widget.LinearLayout;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;

public class WrappingLinearLayout {

    public WrappingLinearLayout(@NotNull final ArrayList<LinearLayout> views, @NotNull final LinearLayout verticalLinearLayout, @NotNull final Context context) {
        verticalLinearLayout.post(new Runnable() {
            @Override
            public void run() {
                execute(views, verticalLinearLayout, context);
            }
        });

    }

    private void execute(@NotNull ArrayList<LinearLayout> views, @NotNull final LinearLayout verticalLinearLayout, @NotNull final Context context) {

        ArrayList<LinearLayout> horizontalLinearLayouts = new ArrayList<>();
        LinearLayout horizontalLinearLayout = new LinearLayout(context);
        horizontalLinearLayouts.add(horizontalLinearLayout);
        int verticalLinearLayoutWidth = verticalLinearLayout.getMeasuredWidth()
                - (verticalLinearLayout.getPaddingLeft()
                        + verticalLinearLayout.getPaddingRight());

        int totalWidthOfViews = 0;

        for (LinearLayout view : views) {

            view.measure(0, 0);
            int currentViewWidth = view.getMeasuredWidth();
            if (totalWidthOfViews + view.getMeasuredWidth() > verticalLinearLayoutWidth) {
                horizontalLinearLayout = new LinearLayout(context);
                horizontalLinearLayouts.add(horizontalLinearLayout);
                totalWidthOfViews = 0;
            }

            totalWidthOfViews += currentViewWidth;

            horizontalLinearLayout.addView(view);
        }

        for (LinearLayout linearLayout : horizontalLinearLayouts) {
            verticalLinearLayout.addView(linearLayout);
        }
    }

}

示例用例的附加代码:

    private ArrayList<LinearLayout> viewAdapterArrayList(ArrayList<TextView> textViews) {
        ArrayList<LinearLayout> views = new ArrayList<>();
        for (TextView textView : textViews) {
            LinearLayout linearLayout = new LinearLayout(this);
            linearLayout.addView(textView);
            views.add(linearLayout);
        }
        return views;
    }

    private ArrayList<TextView> textViewArrayListForExample() {
        ArrayList<TextView> textViews = new ArrayList<>();
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        for (int i = 0; i < 40; i++) {
            TextView textView = new TextView(this);
            textView.setText("View " + i + " |");
            if (i < 20) {
                if (i % 5 == 0) {
                    textView.setText("View longer view " + i + " |");
                } else if (i % 7 == 0) {
                    textView.setText("View different length view " + i + " |");
                } else if (i % 9 == 0) {
                    textView.setText("View very long view that is so long it's really long " + i + " |");
                }
            }
            textView.setMaxLines(1);
            textView.setBackground(new ColorDrawable(Color.BLUE));
            textView.setTextColor(Color.WHITE);
            textView.setLayoutParams(layoutParams);
            textView.setPadding(20, 2, 20, 2);
            layoutParams.setMargins(10, 2, 10, 2);
            textViews.add(textView);
        }
        return textViews;
    }
}

答案 10 :(得分:0)

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

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

Flow是用于构建链的新虚拟布局,当它们用完时可以缠绕到下一行甚至屏幕的另一部分。当您在一个链中布置多个项目时,这很有用,但是您不确定容器在运行时的大小。您可以使用它来根据应用程序中的动态尺寸(例如旋转时的屏幕宽度)构建布局。

这是xml的样子:

<androidx.constraintlayout.helper.widget.Flow
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintEnd_toEndOf="parent"
   app:layout_constraintTop_toTopOf="parent"
   app:flow_wrapMode="chain"
   app:constraint_referenced_ids="card1, card2, card3"
   />

注意app:constraint_referenced_idsapp:flow_wrapMode属性。

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

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

create a single chain, overflowing if the content doesn’t fit

on overflow, create add another chain for the overflow elements

对齐similar to chain, but align rows into columns

有关更多详细信息,请检查Android Developers post

official docs