Search code examples
androidandroid-layoutandroid-constraintlayout

ConstraintLayout - Align view to top or bottom of another view, depending on the visibility of the other view


I have the following simplified XML layout with one constraint layout, two textviews and one switch:

<?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">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Demo"
        app:layout_constraintEnd_toStartOf="@id/innerConstraintLayout"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.SwitchCompat
        android:id="@+id/switch1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="@id/textView1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@id/textView1" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/textView1"
        app:layout_constraintTop_toBottomOf="@id/switch1" />
</androidx.constraintlayout.widget.ConstraintLayout>

For switch1 I have to use layout_constraintTop_toTopOf and layout_constraintBottom_toBottomOf to center it horizontally with textView1. Otherwise switch1 would be too big.

textView2 should be visible below switch1 if switch1 is visible and otherwise aligned with the top of textView1.

enter image description here

How an I achieve this?

Currently textView2 is visible below switch1 if switch1 is visible but otherwise aligned with the bottom of textView1.

Thanks!


Solution

  • You are using the top and bottom constraints of the switch to center it on the left TextVIew. When the switch is visible, the bottom TextView is aligned to its bottom. When the switch is gone, you want the bottom TextView to be aligned to the top of the left TextView. (The switch actually extends below and above the left TextView but appears to shrink since it is centered on the left view.

    A Barrier positioned below the switch with the top of the bottom TextView attached to is will get you what you want when the switch is visible. But what happens when the switch is gone?

    When the switch is made gone, it, in essence' shrinks to a point. Since the top and bottom of the switch is still attached to the top and bottom of the left TextView, the point will be centered between the top and bottom of the left TextView. This is not what you want since you want the bottom view to be aligned with the top of the left view.

    The way around this is to set a zero width/height widget on top of the left TextView. A Space widget is the most appropriate.

    We can now set a barrier to the bottoms of the Space and the switch. We will also need to specify that the switch is to be ignored by the Barrier when the switch is made gone by specifying

    app:barrierAllowsGoneWidgets="false"
    

    Here is what the final layout looks like. I have made a few adjustments for this demo.

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="32dp">
    
        <TextView
            android:id="@+id/textView1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:text="This is the left TextView"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <androidx.appcompat.widget.SwitchCompat
            android:id="@+id/switch1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:visibility="visible"
            app:layout_constraintBottom_toBottomOf="@id/textView1"
            app:layout_constraintStart_toEndOf="@+id/textView1"
            app:layout_constraintTop_toTopOf="@id/textView1" />
    
        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:text="Bottom TextView"
            app:layout_constraintStart_toEndOf="@+id/textView1"
            app:layout_constraintTop_toBottomOf="@id/barrier" />
    
        <Space
            android:id="@+id/spacerTop"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintEnd_toEndOf="@id/textView1"
            app:layout_constraintTop_toTopOf="@id/textView1" />
    
        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/barrier"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierAllowsGoneWidgets="false"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="spacerTop,switch1" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    enter image description here

    I don't recall when some of the features I reference were introduced. You may need to move to a later version of ConstraintLayout to get this working.