1

I am attempting to set up what I thought should be a simple layout with a ConstraintLayout I have a series of text entries, with labels whose size can change from language to language. There is a barrier to the right side of the labels so the text entries stay aligned even when the label lengths change.

I want the text entries to be a nominal size (200dp for the top one and 150dp for the others in this example) but to be able to shrink as needed to fit the screen if the labels are long and the screen is small. In landscape mode, or on a larger screen, the entries should be their max size and aligned left against the barrier.

After a lot of trial and error, I got the layout below to work, by setting two of the chains to "spread_inside" and the last one to "packed". Setting them all to spread_inside messes up the layout (entry_2 shrinks to its minimum size).

Working version - last chain packed

All chains set to spread_inside

This smells like something that will stop working this way in a future ConstraintLayout update (this is using com.android.support.constraint:constraint-layout:1.1.0-beta5). The behavior of this layout is different using beta4 (also wrong, but differently wrong, beta4 aligns them all on the right side of the screen).

Am I missing something here? Does anyone else have similar chain patterns that have worked? Is there a better way of doing this?

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
    android:padding="16dp"
    tools:context="com.mypackage.testapp.MainActivity">

    <TextView
        android:text="Label 1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="@+id/entry_1"
        app:layout_constraintBottom_toBottomOf="@+id/entry_1"
        android:id="@+id/label_1" />

    <EditText
        android:id="@+id/entry_1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:inputType="text"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintHorizontal_weight="100.0"
        app:layout_constraintLeft_toRightOf="@+id/guideline"
        app:layout_constraintRight_toLeftOf="@+id/space_1"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_max="200sp"
        app:layout_constraintWidth_min="100sp" />

    <Space
        android:id="@+id/space_1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/entry_1"
        app:layout_constraintHorizontal_weight="0.01"
        app:layout_constraintTop_toTopOf="@+id/entry_1"
        app:layout_constraintBottom_toBottomOf="@+id/entry_1"/>

    <TextView
        android:text="Label 2 Is very very long"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="@+id/entry_2"
        app:layout_constraintBottom_toBottomOf="@+id/entry_2"
        android:id="@+id/label_2" />

    <EditText
        android:id="@+id/entry_2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:inputType="text"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintHorizontal_weight="100.0"
        app:layout_constraintLeft_toRightOf="@+id/guideline"
        app:layout_constraintRight_toLeftOf="@+id/space_2"
        app:layout_constraintTop_toBottomOf="@+id/entry_1"
        app:layout_constraintWidth_max="150sp"
        app:layout_constraintWidth_min="100sp" />

    <Space
        android:id="@+id/space_2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/entry_2"
        app:layout_constraintHorizontal_weight="0.01"
        app:layout_constraintTop_toTopOf="@+id/entry_2"
        app:layout_constraintBottom_toBottomOf="@+id/entry_2"/>

    <TextView
        android:text="Label Three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="@+id/entry_3"
        app:layout_constraintBottom_toBottomOf="@+id/entry_3"
        android:id="@+id/label_3" />

    <EditText
        android:id="@+id/entry_3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:inputType="text"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintHorizontal_weight="100.0"
        app:layout_constraintLeft_toRightOf="@+id/guideline"
        app:layout_constraintRight_toLeftOf="@+id/space_3"
        app:layout_constraintTop_toBottomOf="@+id/entry_2"
        app:layout_constraintWidth_max="150sp"
        app:layout_constraintWidth_min="100sp" />

    <Space
        android:id="@+id/space_3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/entry_3"
        app:layout_constraintHorizontal_weight="0.01"
        app:layout_constraintTop_toTopOf="@+id/entry_3"
        app:layout_constraintBottom_toBottomOf="@+id/entry_3"/>

    <android.support.constraint.Barrier
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:barrierDirection="right"
        app:constraint_referenced_ids="label_1,label_2,label_3" />

</android.support.constraint.ConstraintLayout>

Edit:

I reported this issue to the ConstraintLayout team at Google and they confirmed that it is a bug. Once fixed, I should be able to do this without the complicated chains.

Tyler V
  • 9,694
  • 3
  • 26
  • 52

3 Answers3

1

The Android dev team confirmed this is a bug, and it has been fixed in the beta6 release. The layout now works without the need for any chains or spaces (the solution below is what they suggested in the ticket).

https://issuetracker.google.com/issues/74469361

<TextView
    android:id="@+id/label_1"
    android:tag="48,103,136,57"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Label 1"
    app:layout_constraintBottom_toBottomOf="@+id/entry_1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="@+id/entry_1" />

<EditText
    android:id="@+id/entry_1"
    android:tag="505,63,512,136"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:layout_marginStart="8dp"
    android:inputType="text"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintStart_toStartOf="@+id/guideline"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintWidth_max="200sp"
    app:layout_constraintWidth_min="100sp" />

<TextView
    android:id="@+id/label_2"
    android:tag="48,254,442,57"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Label 2 Is very very long"
    app:layout_constraintBottom_toBottomOf="@+id/entry_2"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="@+id/entry_2" />

<EditText
    android:id="@+id/entry_2"
    android:tag="505,214,450,136"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:layout_marginStart="8dp"
    android:inputType="text"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintStart_toStartOf="@+id/guideline"
    app:layout_constraintTop_toBottomOf="@+id/entry_1"
    app:layout_constraintWidth_max="150sp"
    app:layout_constraintWidth_min="100sp" />

<TextView
    android:id="@+id/label_3"
    android:tag="48,405,218,57"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Label Three"
    app:layout_constraintBottom_toBottomOf="@+id/entry_3"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="@+id/entry_3" />

<EditText
    android:id="@+id/entry_3"
    android:tag="505,365,450,136"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:layout_marginStart="8dp"
    android:inputType="text"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintStart_toStartOf="@+id/guideline"
    app:layout_constraintTop_toBottomOf="@+id/entry_2"
    app:layout_constraintWidth_max="150sp"
    app:layout_constraintWidth_min="100sp" />

<android.support.constraint.Barrier
    android:id="@+id/guideline"
    android:tag="490,48,0,0"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:barrierDirection="right"
    app:constraint_referenced_ids="label_1,label_2,label_3" />
Tyler V
  • 9,694
  • 3
  • 26
  • 52
0

My guess would be the fact that you're not really setting up a chain.

https://developer.android.com/training/constraint-layout/index.html

A chain works properly only if each end of the chain is constrained to another object on the same axis

For a proper chain, your labels would need to be part of it as well. You can probably just ditch the chain attributes and constrain the edit texts to the barrier and the parent rights.

Hope that helps.

dominicoder
  • 9,338
  • 1
  • 26
  • 32
  • The chain is between the barrier and parent right. Is a vertical barrier not considered in the proper axis for a horizontal chain? I tried just tying the EditTexts to the right as you suggested but that just moves the barrier over and aligns things to the right. – Tyler V Mar 11 '18 at 03:13
  • What I mean is I think a chain is supposed to run from the parent left / start to the parent right / end. Since yours starts with the barrier, it might not be a true chain so isn't being calculated correctly. You might try setting the `layout_width` to `wrap_content`, and setting `minWidth`, and `maxWidth` attributes instead of the constraint equivalents. – dominicoder Mar 11 '18 at 04:37
  • A chain doesn't have to run the full width/height of the parent. You could have 3 views arranged horizontally but only have the right two be a chain (having "bi-directional constraints", from the link you posted). Example: View A tied left to left of parent, view B tied left to right of A, right to left of C, view C tied left to right of B, right to right of parent. Then B and C form a chain from the right of A to the right of parent. Setting min/max width and wrap_content starts it at min width and lets it expand off the screen as you type if the screen is small. – Tyler V Mar 11 '18 at 04:55
0

Try it with Relative Layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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"
    android:padding="16dp">

    <TextView
        android:id="@+id/label_1"
        android:text="Label 1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"/>
    <EditText
        android:id="@+id/entry_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:layout_alignBaseline="@+id/label_1"
        android:layout_toEndOf="@+id/label_1"
        android:maxLength="20"
        android:textSize="20sp"
        android:inputType="text" />
    <TextView
        android:id="@+id/label_2"
        android:text="Label 2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:textSize="20sp"
        android:layout_below="@+id/label_1"/>
    <EditText
        android:id="@+id/entry_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:layout_alignBaseline="@+id/label_2"
        android:layout_toEndOf="@+id/label_2"
        android:maxLength="20"
        android:textSize="20sp"
        android:inputType="text" />
    <TextView
        android:id="@+id/label_3"
        android:text="Label 3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:textSize="20sp"
        android:layout_below="@+id/label_2"/>
    <MultiAutoCompleteTextView
        android:id="@+id/entry_3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:layout_alignBaseline="@+id/label_3"
        android:layout_toEndOf="@+id/label_3"
        android:maxLength="200"
        android:textSize="20sp"/>
</RelativeLayout>

Edit: This should be your basic structure for form, provided if ConstraintLayout is not the only option.

  • Just use Relative Layout, Table Layout is not necessary here
  • Set value for android:maxLength
  • For including more text-characters use MultiAutoCompleteTextView
Rohit Sharma
  • 1,271
  • 5
  • 19
  • 37
  • This is what I had originally, but it doesn't work as presented (EditText width does not expand when set to 0dp in a table, and setting the width of the EditText manually doesn't adapt to small screens). – Tyler V Mar 11 '18 at 15:56
  • Okay. Have you tried it with layout_width = match_parent and setting the layout_marginEnd as per your need for EditText – Rohit Sharma Mar 11 '18 at 16:13
  • 1
    Yes, I have. Using match_parent for all of them creates EditTexts that are about 1em wide. If I set the width of one of them, then they all have matching size. Setting the end/right margin doesn't stop it from going off screen, just adds a margin to the right. It may look like it stops at the edge of the screen, but if you keep typing the cursor just moves to the hidden section. – Tyler V Mar 11 '18 at 16:33
  • Yes you are right. I haven't tested it earlier. Thanks @TylerV Now, I have updated my answer. :) – Rohit Sharma Mar 11 '18 at 18:34
  • This version works, but still misses a couple of the behaviors I was trying to get with the ConstraintLayout. 1) the EditTexts are no longer vertically aligned when labels are different lengths, and 2) the EditTexts span the entire screen width, which makes them too long on a large screen or in landscape mode. Setting maxLength just limits the number of characters that can be typed into the box, not the box size itself. I want the EditText width to be "as wide as possible but not wider than X" – Tyler V Mar 11 '18 at 19:19