Як я можу зробити щось на зразок FlowLayout в Android?


Відповіді:


91

Якщо ви подивитеся розмову, яку я провела в день університету в Девокс (доступний на parleys.com ), ви навчитеся робити це самостійно. Під час бесіди я написав FlowLayoutреалізацію на сцені, щоб показати, як просто писати власні макети.

Впровадження розміщено тут .


18
спасибі роменському хлопцеві, це дійсно допомагає. як щодо просто дати рішення? або посилання? ... Я не розумію, як прийняли вашу відповідь.
Йоганн Хільбольд,

4
@JohannHilbold: Я щойно додав посилання
Macarse

7
Читачі повинні знати, що у коді за посиланням є кілька серйозних проблем, і він далеко не вирішується. У мене вже було дві основні проблеми у цій невеликій базі коду. Я не буду публікувати виправлення, тому що це рішення з клейкої стрічки для моїх особливих потреб ...
Неманя Ковачевич

6
Я ніколи не стверджував, що це ідеально :)
Ромен Гай

19
@RomainGuy справа в тому, що, можливо, це не так просто, зрештою.
Джеффрі Блаттман,

71

Ви повинні використовувати 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-flex-layouts-with.html


7
Це останнє і поки що найкраще рішення.
Тухід

3
Це слід позначити як рішення! Наразі найкраща відповідь на цю проблему
x10sion

1
Щоб додати до цієї відповіді, цей lib також має LayoutManager, якщо ви хочете мати цей ефект у RecyclerView
Арі

1
Бінго, банго, бонго. Чудова бібліотека для використання Flexbox в Android. Хороший.
Джошуа Пінтер,

23

У мене недостатньо репутації, щоб залишити коментар до відповіді Ромена Гая, але саме тут ця відповідь повинна бути (я створив обліковий запис лише для того, щоб поділитися своїм редагуванням).

У будь-якому випадку, я бачу, що інші люди виявили, що його досить круте рішення FlowLayout має деякі проблеми. Я міг знайти одного сам, і я бачив, як і інші, що деяких дітей обрізали. Розглядаючи деталі алгоритму, здається, це дуже проста помилка в обчисленні висоти. Коли сама остання дитина є тією, що ставиться на нову лінію, то висота не була обчислена належним чином. Я трохи очистив обчислення (там було дивне використання "висота" проти поточної висоти).

Наступна зміна вирішує проблему "остання дочірня особа відсічена, якщо на новому рядку":

@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

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. Сподіваюся, це допомагає!


Дуже корисно в певних випадках. Ось версія, яка підтримує MarginLayoutParams.
jk7,

Приємне рішення! margin працює лише після встановлення його всередині методу init, але не з макета.
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
Гарний клас, але йому знадобиться трохи роботи, щоб підтримати напрямок макета справа наліво.
Тед Хопп

Як робити предмети в центрі.
Абхішек, 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);
        }
    }
}

Я чухав голову протягом останніх 2 годин, і тільки цей фрагмент коду працював на мене !! Чи можете ви надати схему потоку для RadioGroup?
Пратік Салуя

Крім того, я хочу знати, як я можу використовувати маржу? margin = 10dp надходить правильно від макета.
Пратік Салуя

1
@Pratik, якщо у вас є невелика RadioGroup і ви хочете, щоб поряд з нею відображалися інші види, якщо є місце і переходили до наступного рядка, якщо місця немає, FlowLayout повинен працювати нормально. Велика горизонтальна RadioGroup, яка змушує елементи RadioButton переходити до наступного рядка, коли перший рядок поза приміщенням, вимагатиме написання власного класу типу RadioGroup, оскільки RadioGroup розширює LinearLayout. Ви можете написати власний клас RadioGroup, використовуючи ідеї цього класу FlowLayout. Дотепер у мене не було необхідності користуватися кнопками радіо. Зазвичай ми використовуємо Spinners або інші елементи керування.
jk7

@Pratik, ви можете встановити всі поля з android:layout_margin="10dp"в файлі макета, або задати індивідуальні поля з атрибутами , такими як android:layout_marginLeft="10dp", android:layout_marginTop="4dp", ..etc.
jk7

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/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.