2

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.)

Erigami
  • 804
  • 1
  • 8
  • 20
  • have you tried using guidelines ? – Umair Jul 01 '19 at 06:35
  • @Umair guidelines won't help, he needs to calculate each element's width dynamically according to the widest one – Khojiakbar Jul 01 '19 at 06:36
  • @Khojiakbar well he have to calculate width of any textview, for that it's best to use guideline otherwise a textview can take whole width horizontally then he won't be able to do what OP wants. – Umair Jul 01 '19 at 06:46
  • see this https://stackoverflow.com/questions/48843203/how-to-best-place-my-obscured-progressbar-on-a-constraintlayout-on-the-screen/48843312#48843312 – user311086 Jul 01 '19 at 08:36
  • @Khojiakbar are you saying I should calculate the child widths in code? – Erigami Jul 01 '19 at 22:14
  • 1
    @Erigami in fact I haven't used flow, but there's nothing in docs about solution of this kind of problems. I would try to implement width calculation by myself – Khojiakbar Jul 01 '19 at 22:35
  • 1
    If your elements contain text, then it's easy, thanks to `Paint` – Khojiakbar Jul 01 '19 at 22:37

2 Answers2

2

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.

Mahmudul Hasan Shohag
  • 2,203
  • 1
  • 22
  • 30
0

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;
            }
        });
Erigami
  • 804
  • 1
  • 8
  • 20