Search code examples
androidandroid-constraintlayout

How best to use ConstraintLayout when you want alignment but are unsure of the child widget sizes


I'm trying to create this layout:

ConstraintLayout with two TextView and two EditText children

My criteria are:

  • I want the labels right-justified relative to each other, but otherwise flush left (with margin) within the parent.
  • I want the EditText items to fill the remaining space
  • Text aligned at the baseline

Knowing that the EditText widgets are taller than the TextView widgets, and that "Password" is wider than "Login", I was able to come up with this layout:

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/login"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="8dp"
            android:autofillHints="login"
            android:hint="@string/user_name_hint"
            android:inputType="textPersonName"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/textView"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="48dp"
            android:text="@string/login"
            app:layout_constraintBaseline_toBaselineOf="@+id/login"
            app:layout_constraintStart_toStartOf="parent" />

        <EditText
            android:id="@+id/password"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="16dp"
            android:ems="10"
            android:hint="@string/password_hint"
            android:inputType="textPassword"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/login"
            app:layout_constraintTop_toBottomOf="@+id/login"
            android:autofillHints="password" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="24dp"
            android:text="@string/password"
            app:layout_constraintBaseline_toBaselineOf="@+id/password"
            app:layout_constraintStart_toStartOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

But I realize that this depends on invalid assumptions. What would be the "right" way to achieve this layout?

(I realize that this would have been near-trivial using a GridLayout, but I'm trying to learn to use ConstraintLayout.


Solution

  • Try the following layout. I created a barrier toward the end of the two TextViews and constrained their ends to the barrier. The starts are constrained to the parent start. The biases are set to 1.0 to move both to the right. This should give you the right-justified text. (You may need a later version of ConstraintLayout for this to work; I used version 2.1.3)

    The layout for the EditTexts is straightforward.

    <androidx.constraintlayout.widget.ConstraintLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TextView
            android:id="@+id/loginLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:text="@string/login"
            app:layout_constraintBaseline_toBaselineOf="@+id/login"
            app:layout_constraintEnd_toStartOf="@id/barrier"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintStart_toStartOf="parent" />
    
        <TextView
            android:id="@+id/passwordLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:text="@string/password"
            app:layout_constraintBaseline_toBaselineOf="@+id/password"
            app:layout_constraintEnd_toStartOf="@id/barrier"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintStart_toStartOf="parent" />
    
        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/barrier"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="end"
            app:constraint_referenced_ids="loginLabel,passwordLabel" />
    
        <EditText
            android:id="@+id/login"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="8dp"
            android:autofillHints="login"
            android:inputType="textPersonName"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/barrier"
            app:layout_constraintTop_toTopOf="parent" />
    
        <EditText
            android:id="@+id/password"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="16dp"
            android:autofillHints="password"
            android:ems="10"
            android:inputType="textPassword"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/barrier"
            app:layout_constraintTop_toBottomOf="@+id/login" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    enter image description here

    There may be an easier way to do all this, but it is not occurring to me at this time.