ConstraintLayout, when constraint dependent view is gone, the layout view behave weirdly

AndroidAndroid LayoutAndroid Constraintlayout

Android Problem Overview


I'm using ConstraintLayout where I will show as below

enter image description here

I would like to hide First (using gone), and which the view I expect to be as below (where ElasticBody will stretch over to use up the original First view space as well.

enter image description here

However, when I actual set First to gone, my view turn out to be as below (all image as from Android Studio Design view). My Elastic Body is missing as well, and the height expanded weirdly.

enter image description here

My layout code as below

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp">

    <TextView
        android:id="@+id/txt_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0ff"
        android:text="First"
        android:visibility="gone"
        android:textSize="26sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/txt_body"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/txt_body"
        android:layout_width="0dp"
        android:background="#f0f"
        android:layout_height="wrap_content"
        android:text="Elastic Body"
        android:textSize="26sp"

        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/txt_tail"
        app:layout_constraintStart_toEndOf="@+id/txt_first"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/txt_tail"
        android:background="#ff0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tail"
        android:textSize="26sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/txt_body"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

(Note, if you remove the gone, you'll get the first image view). Why is this so? How could I fix it where when my First is gone, I could the Elastic Body stretch out correctly?

p/s: I know how to do it in LinearLayout and RelativeLayout... but wonder if this is a limitation on ConstraintLayout?

Android Solutions


Solution 1 - Android

One more thing you can use for Visibility.GONE in ConstraintLayout: Barriers.

If you are not aware of Barriers, then please check this : Barriers

Solution 2 - Android

See Output :

Try following.

Set the first view's left and top constraints to "parent". After that:

  • set the txt_body textview width to "0dp"
  • set the left constraint to the first view's right side
  • set the right constraint to the tail view's left side.

So, whenever you set the first view's visibility to "gone", the body view will be stretched like how you want it.

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp">

    <TextView
        android:id="@+id/txt_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0ff"
        android:text="First"
        android:textSize="26sp"
        android:visibility="gone"
        app:layout_constraintEnd_toStartOf="@+id/txt_body"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent" />

    <TextView
        android:id="@+id/txt_body"
        android:layout_width="0dp"
        android:background="#f0f"
        android:layout_height="wrap_content"
        android:text="Elastic Body"
        android:textSize="26sp"
        app:layout_constraintRight_toLeftOf="@+id/txt_tail"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/txt_first"
        />

    <TextView
        android:id="@+id/txt_tail"
        android:background="#ff0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tail"
        android:textSize="26sp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

</android.support.constraint.ConstraintLayout>

Update

> If you want to do using barrier then also you can do it.

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp">

    <TextView
        android:id="@+id/txt_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0ff"
        android:text="First"
        android:textSize="26sp"
        android:visibility="gone"
        app:layout_constraintEnd_toStartOf="@+id/barrier"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/txt_body"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="#f0f"
        android:text="Elastic Body"
        android:textSize="26sp"
        app:layout_constraintStart_toEndOf="@+id/barrier"
        app:layout_constraintEnd_toStartOf="@+id/txt_tail"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/txt_tail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff0"
        android:text="Tail"
        android:textSize="26sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="start"
        app:constraint_referenced_ids="txt_body,txt_first" />

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="txt_body,txt_tail" />

</androidx.constraintlayout.widget.ConstraintLayout>

Solution 3 - Android

Say, you want to have a picture like this:

enter image description here

Here you have indents between the title and "Nice work", between "Nice work" and time, also horizontal indent to "Opinions". They are centered vertically.

enter image description here

"Opinions" is attached to the star, so that can be multilined and stay centered. I show results for 2 variants: in the first row opinions are multilined, while in the next row it is a single line. In columns you can see 4 variants of showing/hiding 2 labels.

enter image description here

  1. A more simple and preferrable way is to wrap both labels into LinearLayout and insert it into parent ConstraintLayout. Then you can set vertical gravity, show or hide labels, hide the LinearLayout itself.

  2. If you don't want to have nested layouts, use Barriers and Groups. It is a difficult task that can waste many hours. A key is having additional Views for aligning. Here I have 2 hiding labels ("Nice work" and "Opinions"), and I have to add 2 views (spaces).

enter image description here

The height of the right space is equal to the height of the star (14dp).

To simplify hiding several views, I joined them into groups.

enter image description here

You can see horizontal dotted lines - they are Barriers. I align them on tops and bottoms of the most big views (barrier_2 is similar):

<androidx.constraintlayout.widget.Barrier
    android:id="@+id/barrier_1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:barrierDirection="top"
    app:constraint_referenced_ids="left_text,opinion" />

enter image description here

Vertical aligning is based on these 2 additional Spaces (see marginTop="10dp"):

<Space
    android:id="@+id/left_text_space"
    android:layout_width="25dp"
    android:layout_height="10dp"
    android:layout_marginTop="10dp"
    app:layout_constraintStart_toEndOf="@id/left_text"
    app:layout_constraintTop_toBottomOf="@id/title" />

It is difficult to cover all situations, so see the following layout:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="15dp"
    android:paddingTop="5dp"
    android:paddingRight="15dp"
    android:paddingBottom="5dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:lineSpacingExtra="4sp"
        android:lines="1"
        android:paddingBottom="5dp"
        android:text="«Title text»"
        android:textColor="#333333"
        android:textSize="15sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="top"
        app:constraint_referenced_ids="left_text,opinion" />

    <TextView
        android:id="@+id/left_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#112233"
        android:paddingLeft="5dp"
        android:paddingTop="4dp"
        android:paddingRight="5dp"
        android:paddingBottom="4dp"
        android:text="Nice work"
        android:textColor="#ffffff"
        android:textSize="13sp"
        app:layout_constraintBottom_toBottomOf="@id/barrier_2"
        app:layout_constraintStart_toStartOf="@id/title"
        app:layout_constraintTop_toTopOf="@id/left_text_space" />

    <Space
        android:id="@+id/left_text_space"
        android:layout_width="25dp"
        android:layout_height="10dp"
        android:layout_marginTop="10dp"
        app:layout_constraintStart_toEndOf="@id/left_text"
        app:layout_constraintTop_toBottomOf="@id/title" />

    <androidx.constraintlayout.widget.Group
        android:id="@+id/left_text_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="visible"
        app:constraint_referenced_ids="left_text,left_text_space" />

    <Space
        android:id="@+id/opinion_space"
        android:layout_width="1dp"
        android:layout_height="14dp"
        android:layout_marginTop="10dp"
        app:layout_constraintStart_toStartOf="@id/left_text_space"
        app:layout_constraintTop_toBottomOf="@id/title" />

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="bottom"
        app:constraint_referenced_ids="left_text,opinion" />

    <ImageView
        android:id="@+id/opinion_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:contentDescription="@null"
        app:layout_constraintBottom_toBottomOf="@id/barrier_2"
        app:layout_constraintStart_toEndOf="@id/left_text_space"
        app:layout_constraintTop_toTopOf="@id/opinion_space"
        app:srcCompat="@drawable/ic_filled_rate_star" />

    <TextView
        android:id="@+id/opinion"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="5dp"
        android:layout_marginLeft="5dp"
        android:lineSpacingExtra="1sp"
        android:text="1. Opinion 1.\n2. Opinion 2.\n3. Opinion 3.\n4. Opinion 4."
        android:textColor="#1122aa"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="@id/opinion_icon"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toEndOf="@id/opinion_icon"
        app:layout_constraintTop_toTopOf="@id/opinion_icon" />

    <androidx.constraintlayout.widget.Group
        android:id="@+id/opinion_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="visible"
        app:constraint_referenced_ids="opinion_icon,opinion,opinion_space" />

    <ImageView
        android:id="@+id/time_icon"
        android:layout_width="15dp"
        android:layout_height="15dp"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="8dp"
        android:contentDescription="@null"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/barrier_2"
        app:srcCompat="@drawable/ic_time" />

    <TextView
        android:id="@+id/time"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="7dp"
        android:layout_marginLeft="7dp"
        android:ellipsize="end"
        android:lineSpacingExtra="1sp"
        android:lines="2"
        android:paddingBottom="7dp"
        android:text="17:00"
        android:textColor="#9e9e9e"
        android:textSize="11sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toEndOf="@id/time_icon"
        app:layout_constraintTop_toTopOf="@id/time_icon" />

</androidx.constraintlayout.widget.ConstraintLayout>

Then in your activity you can show/hide labels. Hide Groups, not views inside, because strangely inside a Group views are always visible.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_activity)

    left_text_group.visibility = View.GONE
    opinion_group.visibility = View.VISIBLE
}

Solution 4 - Android

An answer has already been given linking to Barriers. I will provide an example of how I've actually implemented it:

<TextView
    android:id="@+id/textView1"
    app:layout_constraintTop_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    android:text="Some text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<TextView
    android:id="@+id/textView2"
    app:layout_constraintTop_toBottomOf="@id/textView1"
    app:layout_constraintLeft_toLeftOf="parent"
    android:text="Some other text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
<androidx.constraintlayout.widget.Barrier
    android:id="@+id/barrier1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:barrierDirection="bottom"
    app:constraint_referenced_ids="textView1,textView2" />

This example shows 2 TextViews either of which can be gone. The Views are on stacked from top to bottom, so barrierDirection is set to bottom. Should you need another direction, just change that line accordingly.

Setting any of the 2 TextViews to gone, will result in the Barrier shifting to the bottom of the other, and if we set both to gone, it'll just shift up to the element that textView1's top constraint was referencing, in this case, the parent.

> Note: If your textView1's top constraint is something else, i.e. it's below another element, the barrier will end up there if both views are set to gone.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionElyeView Question on Stackoverflow
Solution 1 - AndroidMehul KabariaView Answer on Stackoverflow
Solution 2 - AndroidShweta ChauhanView Answer on Stackoverflow
Solution 3 - AndroidCoolMindView Answer on Stackoverflow
Solution 4 - AndroidVuckoView Answer on Stackoverflow