如何在Android中执行类似FlowLayout的操作?

Adh*_*ham 91 android

我怎样才能FlowLayout在Android中做类似的事情?

Rom*_*Guy 87

如果您观看我在Devoxx大学日(在parleys.com上提供)的演讲,您将学习如何自己动手.在演讲中,我FlowLayout在舞台上现场编写了一个实现,以展示编写自定义布局是多么简单.

实施在这里托管.

  • @RomainGuy重点是,也许它毕竟不是那么简单. (17认同)
  • 谢谢罗曼家伙,真的很有帮助.如何给出解决方案?还是链接?...我不知道你的答案被接受了. (16认同)
  • 我不明白为什么谷歌不会向sdk添加这样的类... (13认同)
  • 读者应该知道链接上的代码存在一些严重的问题,并且远非直接的解决方案.我已经在这个小代码库中遇到了两个主要问题.我不会发布修复,因为它们是我的特殊需求的胶带解决方案...... (6认同)
  • 我从未声称它是完美的:) (6认同)
  • @JohannHilbold:我刚刚添加了链接 (4认同)

ars*_*ent 57

您应该使用FlexboxLayoutflexWrap="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>
Run Code Online (Sandbox Code Playgroud)

有关构建说明,请参阅github存储库.

FlexboxLayout的可视化表示 有关这方面的更多信息 - https://android-developers.googleblog.com/2017/02/build-flexible-layouts-with.html

  • 这是迄今为止最新的解决方案. (6认同)
  • This should be marked as the solution! Best answer for this problem so far (3认同)
  • 宾果,邦戈,邦戈。在 Android 中使用 Flexbox 的精彩库。好一个。 (2认同)

小智 23

我没有足够的声誉向Romain Guy的回答发表评论,但这就是答案所在(我创建了一个帐户只是为了分享我的编辑).

无论如何,我看到其他人发现他非常酷的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));
}
Run Code Online (Sandbox Code Playgroud)


Did*_*idi 8

现在,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>
Run Code Online (Sandbox Code Playgroud)

看看这篇文章: https://medium.com/@tapanrgohil/constraintlayout-flow-bye-bye-to-linerlayout-78fd7fa9b679

这里: https: //www.bignerdranch.com/blog/constraintlayout-flow-simple-grid-building-without-nested-layouts/


Dha*_*nki 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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

范例:

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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

        }
    }
}
Run Code Online (Sandbox Code Playgroud)


and*_*per 6

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
Run Code Online (Sandbox Code Playgroud)


mp5*_*501 5

像之前的答案之一一样,我在这里开始使用解决方案: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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我用它作为包装多行TextEdits的解决方案.希望能帮助到你!