Erigami
Erigami

Reputation: 854

How do I make children the same size in a Flow ConstraintLayout?

I have a bunch of tiles that I want to distribute horizontally across the screen. They'll be too wide show up on a single row for most devices, so I'd like to use a ConstraintLayout with a Flow helper. I can get that to work.

But I'd like to make sure each tile is the same width. Ideally each tile would be as wide as the required width of the widest tile. What's the right way to make sure that each element in my Flow gets the same width?

(I tried creating a chain and using layout_constraintHorizontal_weight but that just squeezes every tile into the same row and prevents the Flow from flowing.)

Upvotes: 3

Views: 1899

Answers (2)

Mahmudul Hasan Shohag
Mahmudul Hasan Shohag

Reputation: 3156

You can achieve the same width for the children just using Flow like this:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <View
        android:id="@+id/view1"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="#333" />

    <View
        android:id="@+id/view2"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="#333" />

    <View
        android:id="@+id/view3"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="#333" />

    <View
        android:id="@+id/view4"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="#333" />

    <androidx.constraintlayout.helper.widget.Flow
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        app:constraint_referenced_ids="view1,view2,view3,view4"
        app:flow_horizontalGap="8dp"
        app:flow_maxElementsWrap="2"
        app:flow_verticalGap="8dp"
        app:flow_wrapMode="chain"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Preview:

enter image description here

Note: One downside in this solution is, if you have an odd number of elements, then the last element will fill the full width. You can programatically set the last element width at runtime.

Upvotes: 2

Erigami
Erigami

Reputation: 854

I ended up adding a ViewTreeObserver that creates a ConstraintSet that sets the size of the children before layout occurs. It isn't pretty, but it does roughly what I want:

        final ConstraintLayout statContainer = findViewById(R.id.stat_container);

        statContainer.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                try {
                    ConstraintSet constraintSet = new ConstraintSet();
                    constraintSet.clone(statContainer);

                    // Determine a minimum width
                    int width = 0;
                    for (int i = 0; i < statContainer.getChildCount(); i++) {
                        View child = statContainer.getChildAt(i);
                        if (child instanceof Flow) {
                            continue;
                        }

                        int candidateWidth = child.getMeasuredWidth();
                        width = Math.max(width, candidateWidth);
                    }

                    // Don't allow any child to get too big
                    int max = statContainer.getWidth() / 3;

                    width = Math.min(width, max);

                    // Set that width
                    for (int i = 0; i < statContainer.getChildCount(); i++) {
                        View child = statContainer.getChildAt(i);
                        if (child instanceof Flow) {
                            continue;
                        }
                        constraintSet.constrainWidth(child.getId(), width);
                    }

                    constraintSet.applyTo(statContainer);
                    statContainer.invalidate();

                } finally {
                    statContainer.getViewTreeObserver().removeOnPreDrawListener(this);
                }

                return false;
            }
        });

Upvotes: 0

Related Questions