Есть ли свойство для Android LinearLayout, которое позволит ему правильно оборачивать дочерние элементы управления?
Смысл - у меня изменяемое количество детей, и я хотел бы расположить их по горизонтали, например:
Пример: Control1, Control2, Control3, ...
Я делаю это, установив:
ll.setOrientation (LinearLayout.HORIZONTAL); foreach (ребенок c у детей) ll.addView (c);
Однако, если у меня много детей, последний из них обрезается вместо перехода к следующей строке.
Есть идеи, как это можно исправить?
Ответы:
По состоянию на май 2016 года Google создал собственный,
FlexBoxLayout
который должен решить вашу проблему.Вы можете найти репозиторий GitHub здесь: https://github.com/google/flexbox-layout
источник
Это должно быть то, что вы хотите:
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"/>
источник
ClassCastException
. Причина этого исключения - неполное копирование из другого вопроса Stackoverflow, stackoverflow.com/q/549451/1741542 . Если вы добавитеgenerateLayoutParams(ViewGroup.LayoutParams p)
метод, он работает, как ожидалось.new LayoutParams(1, 1, p)
, не знаю, почему люди голосуют, не пробуя кодДля тех, кому нужно такое поведение:
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); } }
источник
Старый вопрос, но на случай, если кто-то окажется здесь, две библиотеки, которые делают именно это:
https://github.com/blazsolar/FlowLayout
https://github.com/ApmeM/android-flowlayout
источник
Ищу решение аналогичной, но более простой проблемы, то есть обернуть дочерний текстовый контент горизонтальным расположением. Решение 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); }
источник
Модифицированная версия кода из ответа Рэнди Сугианто 'Юку и то, что я наконец выбрал :
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>
источник
//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); }
вышеуказанный метод добавит imgeview бок о бок, как agrid, и в ваш макет
<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>
Я публикую это решение, может оно кому-то поможет и сэкономит время, и я использую его в своем приложении
источник
В итоге я использовал 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) { } });
источник
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, описанный выше, но вам не следует добавлять новый класс в свой проект, и дизайнер лучше работает с этим классом, чем с пользовательским.
источник
Мне нужно было очень простое и гибкое решение (поэтому я использую LinearLayouts). Вот что я придумал.
https://github.com/ShalakoSnell/Wrapping_Linear_Layout
Заметка: Я включил пример метода с использованием текстовых представлений (см. TextViewArrayListForExample ()). XML - это просто родительское представление LinearLayout с идентификатором и вертикальной ориентацией, больше ничего не требуется. Для использования: передать массив представлений, заключенных в LinearLayouts, вместе с родительским представлением и контекстом. (см. viewAdapterArrayList (ArrayList textViews))
Передача массива LinearLayouts - вот что делает этот подход таким гибким, поскольку он позволяет добавлять различные типы представлений. Итак, в первом LinearLayout у вас может быть текст, а во втором - изображение, в третьем - кнопка и так далее ...
Пример портретной ориентации Пример ландшафта Поля 50dp в 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; } }
источник
В прошлом многие пользовательские решения и библиотеки пытались решить эту проблему.
Начиная с
Constraint Layout 2.0
теперь мы можем использовать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_ids
иapp: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.
и официальные документы
источник