Как я могу сделать что-то вроде FlowLayout в Android?

97

Как я могу сделать что-то вроде FlowLayout в Android?

Адхам
источник
См. Ответ @arsent.
Фрэнк Р.

Ответы:

91

Если вы посмотрите мой доклад на дне университета Devoxx (доступен на parleys.com ), вы узнаете, как это сделать самостоятельно. Во время выступления я написалFlowLayout реализацию прямо на сцене, чтобы показать, насколько просто писать собственные макеты.

Реализация размещена здесь .

Ромен Гай
источник
18
спасибо, Ромен, парень, это действительно помогает. как насчет того, чтобы просто дать решение? или ссылку? ... Я не понимаю, как ваш ответ был принят.
Иоганн Хильбольд
4
@JohannHilbold: Я только что добавил ссылки
Macarse
7
Читатели должны знать, что код по ссылке содержит несколько серьезных проблем и далек от простого решения. У меня уже были две серьезные проблемы с этой небольшой базой кода. Я не буду публиковать исправления, потому что они представляют собой клейкую ленту для моих конкретных потребностей ...
Неманья Ковачевич
6
Я никогда не утверждал, что он идеален :)
Romain Guy
19
@RomainGuy дело в том, может быть, тогда все не так просто.
Джеффри Блаттман 08
72

Вы должны использовать FlexboxLayoutс flexWrap="wrap"атрибутом.

<com.google.android.flexbox.FlexboxLayout 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     app:flexWrap="wrap">

<!-- contents go here -->

</com.google.android.flexbox.FlexboxLayout>

Инструкции по сборке см. В репозитории на github.

визуальное представление FlexboxLayout Подробнее об этом - https://android-developers.googleblog.com/2017/02/build-f flexible-layouts-with.html.

арсент
источник
7
Это последнее и пока лучшее решение.
Touhid
3
Это должно быть отмечено как решение! Лучший ответ на эту проблему на данный момент
x10sion 02
1
Чтобы добавить к этому ответу, эта библиотека также имеет LayoutManager, если вы хотите иметь этот эффект в RecyclerView
Ари
1
Бинго, банго, бонго. Замечательная библиотека для использования Flexbox в Android. Хороший.
Джошуа Пинтер,
23

У меня недостаточно репутации, чтобы опубликовать комментарий к ответу Ромена Гая, но именно там должен быть этот ответ (я создал учетную запись, чтобы поделиться своим изменением).

В любом случае, я вижу, что другие люди обнаружили, что у его довольно крутого решения FlowLayout есть некоторые проблемы. Я сам мог найти одного и, как и другие, видел, что некоторых детей подрезали. При детальном рассмотрении алгоритма кажется, что при расчете высоты произошла очень простая ошибка. Когда самый последний дочерний элемент помещается на новую строку, значит, высота не была правильно вычислена. Я немного очистил вычисления (было странное использование "высоты" по сравнению с currentHeight).

Следующее изменение устраняет проблему «последний дочерний элемент обрезается, если находится на новой строке»:

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

    boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED;

    int width = 0;

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

    int maxChildHeight = 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()) > widthLimit)))
        {               
            newLine = true;
            currentHeight += maxChildHeight + mVerticalSpacing;

            width = Math.max(width, currentWidth - spacing);

            currentWidth = getPaddingLeft();
            maxChildHeight = 0;
        }
        else
        {
            newLine = false;
        }

        maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());

        lp.x = currentWidth;
        lp.y = currentHeight;

        currentWidth += child.getMeasuredWidth() + spacing;

        breakLine = lp.breakLine;
    }

    if (newLine == false)
    {
        width = Math.max(width, currentWidth - spacing);
    }

    width += getPaddingRight();
    int height = currentHeight + maxChildHeight + getPaddingBottom();

    setMeasuredDimension(resolveSize(width, widthMeasureSpec),
            resolveSize(height, heightMeasureSpec));
}
Кевин Фругье
источник
9

Есть библиотека от Google, которая называется «flexbox-layout» . Вам стоит это увидеть.

Чтобы использовать его в RecyclerView, вы можете использовать что-то вроде этого:

val layoutManager = FlexboxLayoutManager(activity)
layoutManager.flexDirection = FlexDirection.ROW
layoutManager.flexWrap = FlexWrap.WRAP
layoutManager.justifyContent = JustifyContent.FLEX_START
layoutManager.alignItems = AlignItems.FLEX_START 
recyclerView.layoutManager=layoutManager
разработчик Android
источник
6

Как и один из предыдущих ответов, я начал с решения здесь: http://hzqtc.github.io/2013/12/android-custom-layout-flowlayout.html

Я расширил его, чтобы учесть разный рост детей, как показано ниже.

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

// Custom layout that wraps child views to a new line
public class FlowLayout extends ViewGroup {

    private int marginHorizontal;
    private int marginVertical;

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

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

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

    private void init() { // Specify the margins for the children
        marginHorizontal = getResources().getDimensionPixelSize(R.dimen.activity_half_horizontal_margin);
        marginVertical = getResources().getDimensionPixelSize(R.dimen.activity_half_vertical_margin);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int childLeft = getPaddingLeft();
        int childTop = getPaddingTop();
        int lowestBottom = 0;
        int lineHeight = 0;
        int myWidth = resolveSize(100, widthMeasureSpec);
        int wantedHeight = 0;

        for (int i = 0; i < getChildCount(); i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }

            child.measure(getChildMeasureSpec(widthMeasureSpec, 0, child.getLayoutParams().width),
                    getChildMeasureSpec(heightMeasureSpec, 0, child.getLayoutParams().height));
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            lineHeight = Math.max(childHeight, lineHeight);

            if (childWidth + childLeft + getPaddingRight() > myWidth) { // Wrap this line
                childLeft = getPaddingLeft();
                childTop = marginVertical + lowestBottom; // Spaced below the previous lowest point
                lineHeight = childHeight;
            }
            childLeft += childWidth + marginHorizontal;

            if (childHeight + childTop > lowestBottom) { // New lowest point
                lowestBottom = childHeight + childTop;
            }
        }

        wantedHeight += childTop + lineHeight + getPaddingBottom();
        setMeasuredDimension(myWidth, resolveSize(wantedHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int childLeft = getPaddingLeft();
        int childTop = getPaddingTop();
        int lowestBottom = 0;
        int myWidth = right - left;
        for (int i = 0; i < getChildCount(); i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            if (childWidth + childLeft + getPaddingRight() > myWidth) { // Wrap this line
                childLeft = getPaddingLeft();
                childTop = marginVertical + lowestBottom; // Spaced below the previous lowest point
            }
            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
            childLeft += childWidth + marginHorizontal;

            if (childHeight + childTop > lowestBottom) { // New lowest point
                lowestBottom = childHeight + childTop;
            }
        }
    }
}

Я использовал это как решение для упаковки многострочного TextEdits. Надеюсь, поможет!

mp501
источник
Очень полезно в некоторых случаях. Вот версия, которая поддерживает MarginLayoutParams.
jk7
Хорошее решение! маржа работает только после установки внутри метода инициализации, но не из макета.
Pratik Saluja
6

Вот пользовательский класс, в котором вы можете создать макет, как показано ниже, с добавлением динамического представления (также называемого FlowLayout).

введите описание изображения здесь

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

/*
Created By Dhavalkumar Solanki
* */
public class FlowLayout extends ViewGroup {

    private int line_height_space;

    public static class LayoutParams extends ViewGroup.LayoutParams {

        public int horizontal_spacing;
        public 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_space = 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_space = Math.max(line_height_space, child.getMeasuredHeight() + lp.vertical_spacing);

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

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

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

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

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

    @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_space;
                }
                child.layout(xpos, ypos, xpos + childw, ypos + childh);
                xpos += childw + lp.horizontal_spacing;
            }
        }
    }
}

Пример :

text_view.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tool="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="5dp">

    <TextView
        android:id="@+id/tvText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="19sp"
        android:background="@drawable/unselected_tag"
        android:textColor="@color/colorPrimary"
        tool:text="Temp" />
</RelativeLayout>

activity_flow_layou_demo.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    >
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/tvTitleBusiness"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Business Interest "
                    android:textColor="@color/colorPrimary"
                    android:textSize="25sp" />

                <com.example.tristateandroid2.radardemo.FlowLayout
                    android:id="@+id/flowBusiness"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                </com.example.tristateandroid2.radardemo.FlowLayout>
            </LinearLayout>

            <LinearLayout
                android:layout_marginTop="@dimen/activity_horizontal_margin"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/tvTitlePrivate"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Private Interest "
                    android:textColor="@color/colorPrimary"
                    android:textSize="25sp" />

                <com.example.tristateandroid2.radardemo.FlowLayout
                    android:id="@+id/flowPrivate"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                </com.example.tristateandroid2.radardemo.FlowLayout>
            </LinearLayout>
        </LinearLayout>
    </ScrollView>
</RelativeLayout>

FlowLayouDemo.java

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.ArrayList;

public class FlowLayouDemo extends AppCompatActivity {
    private TextView tvTitleBusiness;
    private FlowLayout flowBusiness;
    private TextView tvTitlePrivate;
    private FlowLayout flowPrivate;
    private ArrayList<TagModel> arrayList;

    private void findViews() {
        tvTitleBusiness = (TextView) findViewById(R.id.tvTitleBusiness);
        flowBusiness = (FlowLayout) findViewById(R.id.flowBusiness);
        tvTitlePrivate = (TextView) findViewById(R.id.tvTitlePrivate);
        flowPrivate = (FlowLayout) findViewById(R.id.flowPrivate);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flow_layou_demo);
        findViews();
        addLayouts();
    }

    private void addLayouts() {
        if (arrayList == null) {
            arrayList = new ArrayList<>();
        }
        flowBusiness.removeAllViews();
        flowPrivate.removeAllViews();
        for (int i = 0; i < 75; i++) {

            final boolean[] selected = {false};
            View view = this.getLayoutInflater().inflate(R.layout.text_view, null);
            final TextView textView = (TextView) view.findViewById(R.id.tvText);
            if (i % 5 == 0) {
                arrayList.add(new TagModel(i, false, "Business VIEW : " + i));
                textView.setText("Busi VIEW To  IS : " + i);
            } else {
                arrayList.add(new TagModel(i, false, "TEXT IS : " + i));
                textView.setText("Busi IS : " + i);
            }
            textView.setBackgroundResource(R.drawable.unselected_tag);
            textView.setTextColor(Color.parseColor("#3F51B5"));
            textView.setTag(i);
            if(i<=50){
                flowBusiness.addView(view);
            }else {
                textView.setText("Priv View : "+i);
                flowPrivate.addView(view);
            }

            textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (selected[0]) {
                        selected[0] = false;
                        textView.setBackgroundResource(R.drawable.unselected_tag);
                        textView.setTextColor(Color.parseColor("#3F51B5"));
                    } else {
                        selected[0] = true;
                        textView.setBackgroundResource(R.drawable.selected_tag);
                        textView.setTextColor(Color.parseColor("#FFFFFF"));
                    }
                }
            });

        }
    }
}
Дхавал Соланки
источник
1
Хороший класс, но потребуется некоторая работа для поддержки направления макета справа налево.
Тед Хопп
Как разместить предметы в центре.
Abhishek c
java.lang.ClassCastException: android.widget.LinearLayout $ LayoutParams не может быть преобразован в com.nuveda.fillintheblank.FlowLayout $ LayoutParams
Naveen Kumar M
Я думаю, вы пытаетесь установить динамические параметры для своего макета, поэтому возникают проблемы, я предлагаю использовать RelativeLayout.LayoutParams.
Дхавал Соланки
3

Версия @MattNotEquals () FlowLayout, которая поддерживает MarginLayoutParams.

Это просто минималистичная реализация MarginLayoutParms для поддержки левого, правого, верхнего и нижнего полей.

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
 *  Original version courtesy of MattNotEquals() at http://stackoverflow.com/a/34169798/4515489 - 4/13/17.
 *  7/15/17 Revised to support MarginLayoutParams.
 */
public class FlowLayout extends ViewGroup {
    // Custom layout that wraps child views to a new line.

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int childLeft = getPaddingLeft();
        int childTop = getPaddingTop();
        int lowestBottom = 0;
        int lineHeight = 0;
        int myWidth = resolveSize(100, widthMeasureSpec);
        int wantedHeight = 0;
        for (int i = 0; i < getChildCount(); i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }
            child.measure(getChildMeasureSpec(widthMeasureSpec, 0, child.getLayoutParams().width),
                          getChildMeasureSpec(heightMeasureSpec, 0, child.getLayoutParams().height));
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            lineHeight = Math.max(childHeight, lineHeight);

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            childLeft += lp.leftMargin;
            childTop += lp.topMargin;
            if (childLeft + childWidth + lp.rightMargin + getPaddingRight() > myWidth) { // Wrap this line
                childLeft = getPaddingLeft() + lp.leftMargin;
                childTop = lowestBottom + lp.topMargin; // Spaced below the previous lowest point
                lineHeight = childHeight;
            }
            childLeft += childWidth + lp.rightMargin;

            if (childTop + childHeight + lp.bottomMargin > lowestBottom) { // New lowest point
                lowestBottom = childTop + childHeight + lp.bottomMargin;
            }
        }
        wantedHeight += lowestBottom + getPaddingBottom(); // childTop + lineHeight + getPaddingBottom();
        setMeasuredDimension(myWidth, resolveSize(wantedHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int childLeft = getPaddingLeft();
        int childTop = getPaddingTop();
        int lowestBottom = 0;
        int myWidth = right - left;
        for (int i = 0; i < getChildCount(); i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            childLeft += lp.leftMargin;
            childTop += lp.topMargin;
            if (childLeft + childWidth + lp.rightMargin + getPaddingRight() > myWidth) { // Wrap this line
                childLeft = getPaddingLeft() + lp.leftMargin;
                childTop = lowestBottom + lp.topMargin; // Spaced below the previous lowest point
            }
            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
            childLeft += childWidth + lp.rightMargin;

            if (childTop + childHeight + lp.bottomMargin > lowestBottom) { // New lowest point
                lowestBottom = childTop + childHeight + lp.bottomMargin;
            }
        }
    }

    @Override
    public boolean shouldDelayChildPressedState() {
        return false;
    }

    @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 FlowLayout.LayoutParams(getContext(), attrs);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
        if (lp instanceof LayoutParams) {
            return new LayoutParams((LayoutParams) lp);
        }
        else if (lp instanceof MarginLayoutParams) {
            return new LayoutParams((MarginLayoutParams) lp);
        }
        else
            return super.generateLayoutParams(lp);
    }

    /**
     * Per-child layout information for layouts that support margins.
     */
    public static class LayoutParams extends MarginLayoutParams {
        public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
            super(c, attrs);
        }
        public LayoutParams(int width, int height) {
            super(width, height);
        }
        public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
            super(source);
        }
        public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
            super(source);
        }
        public LayoutParams(@NonNull LayoutParams source) {
            super(source);
        }
    }
}
jk7
источник
Я чесал голову последние 2 часа, и только этот фрагмент кода работал у меня !! Не могли бы вы предоставить схему потока для RadioGroup?
Pratik Saluja
Кроме того, я хочу знать, как использовать маржу? margin = 10dp поступает правильно из макета.
Pratik Saluja 03
1
@Pratik, если у вас небольшая RadioGroup и вы хотите, чтобы рядом с ней отображались другие представления, если есть место, и переход к следующей строке, если места нет, FlowLayout должен работать нормально. Большой горизонтальный RadioGroup, который заставляет его элементы RadioButton переходить к следующей строке, когда первая строка выходит за пределы места, потребует записи настраиваемого класса типа RadioGroup, поскольку RadioGroup расширяет LinearLayout. Вы можете написать собственный класс RadioGroup, используя идеи этого класса FlowLayout. Пока у меня не было необходимости использовать радиокнопки. Обычно мы используем спиннеры или другие элементы управления.
jk7 05
@Pratik, вы можете установить все поля с android:layout_margin="10dp"в файле макета, или задать индивидуальные поля с атрибутами , такими как android:layout_marginLeft="10dp", android:layout_marginTop="4dp", ..etc.
jk7 05
0

Хороший простой автономный код FlowLayout здесь (всего несколько сжатых файлов gist.github) :

http://hzqtc.github.io/2013/12/android-custom-layout-flowlayout.html

Однако действия там из коробки не помогли мне загрузить настраиваемый макет.

Я нашел этот обходной путь [используя вызов 2-param .inflate () из этого примера ] :

@Override
protected void onCreate(Bundle savedInstanceState)
{
    // ..

    setContentView(R.layout.main_res_layout_activity_main);

    ViewGroup flowContainer = getFlowLayoutView(); 

    // ..
}

ViewGroup getFlowLayoutView()
{
    LayoutInflater inflater = getLayoutInflater();

    ViewGroup flowLayout = 
        (ViewGroup)
            inflater.inflate(
                    R.layout.main_res_layout_activity_main,
                    (FlowLayout) findViewById(R.id.flow_container)
            );

    return flowLayout;
}
Джин Бо
источник
0

Теперь в ConstraintLayout встроена поддержка с использованием виджета Flow. Он имеет множество опций, которые можно использовать для создания различных типов потоков.

Пример:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.constraintlayout.helper.widget.Flow
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="item_1,item_2,item_3"
    app:flow_horizontalBias="0"
    app:flow_horizontalGap="10dp"
    app:flow_horizontalStyle="packed"
    app:flow_verticalGap="8dp"
    app:flow_wrapMode="aligned"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<View
    android:id="@+id/item_1"
    android:layout_width="50dp"
    android:layout_height="50dp" />

<View
    android:id="@+id/item_2"
    android:layout_width="50dp"
    android:layout_height="50dp" />

<View
    android:id="@+id/item_3"
    android:layout_width="50dp"
    android:layout_height="50dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

Взгляните на этот пост: https://medium.com/@tapanrgohil/constraintlayout-flow-bye-bye-to-linerlayout-78fd7fa9b679

И здесь: https://www.bignerdranch.com/blog/constraintlayout-flow-simple-grid-building-without-nested-layouts/

Диди
источник