How I solved layout issues in dynamically gone widgets using ConstraintLayout
Tags: android, constraintlayout
Published: 2020-10-31 21:02:33

This post is about how to deal with [GONE](https://developer.android.com/reference/android/view/View#GONE) widgets using ConstraintLayout in native Android development.

### The study case

I have a screen which displays details of each sandwich selected from the list, backed by a sandwich object in my `Activity`.

This screen is written with ConstraintLayout and shows informations like:
- Also known as
- Ingredients,
- Place of origin
- Description

Here's the layout with only the important parts:

```xml
...
<layout ...
    >
    <ScrollView ...
        >
        <androidx.constraintlayout.widget.ConstraintLayout ...
            >

            <ImageView
                ...
                android:id="@+id/sandwich_image"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                ...
                />

            <TextView
                ...
                android:id="@+id/also_know_as_label"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/sandwich_image"
                />

            <TextView
                ...
                android:id="@+id/also_know_as_text"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/also_know_as_label"
                app:layout_constraintStart_toStartOf="@+id/also_know_as_label"
                app:layout_constraintTop_toBottomOf="@+id/also_know_as_label"
                ...
                />

            <TextView
                ...
                android:id="@+id/ingredients_label"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/also_know_as_text"
                app:layout_constraintStart_toStartOf="@+id/also_know_as_text"
                app:layout_constraintTop_toBottomOf="@+id/also_know_as_text"
                />

            <TextView
                ...
                android:id="@+id/ingredients_text"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/also_know_as_label"
                app:layout_constraintStart_toStartOf="@+id/also_know_as_label"
                app:layout_constraintTop_toBottomOf="@+id/ingredients_label"
                ...
                />

            <TextView
                ...
                android:id="@+id/origin_label"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/also_know_as_label"
                app:layout_constraintStart_toStartOf="@+id/also_know_as_label"
                app:layout_constraintTop_toBottomOf="@+id/ingredients_text"
                />

            <TextView
                ...
                android:id="@+id/origin_text"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/also_know_as_label"
                app:layout_constraintStart_toStartOf="@+id/also_know_as_label"
                app:layout_constraintTop_toBottomOf="@+id/origin_label"
                ...
                />

            <TextView
                ...
                android:id="@+id/description_label"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/also_know_as_label"
                app:layout_constraintStart_toStartOf="@+id/also_know_as_label"
                app:layout_constraintTop_toBottomOf="@+id/origin_text"
                />

            <TextView
                ...
                android:id="@+id/description_text"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/also_know_as_label"
                app:layout_constraintStart_toStartOf="@+id/also_know_as_label"
                app:layout_constraintTop_toBottomOf="@+id/description_label"
                ...
                />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </ScrollView>
</layout>
```

Here's the representation of the layout above in design mode:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2xn77lxxabswk9ib3t2y.png)

After running the application in my physical device I got the desired result: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/m7aibg6la0k7pl79zh3i.png)

### The problem

Some sandwich objects lack data from some of their attributes, a common solution is to set View's visibility to View.GONE.

The result was unexpected:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/l2j9t7elk7ukp6vculth.png)

In ConstraintLayout we have View's constrained to each other, so I ended up with a problem.

### The solution

I looked for a straightforward solution and the answer was ConstraintLayout Barrier.

From the [docs](https://developer.android.com/reference/androidx/constraintlayout/widget/Barrier):

> A Barrier references multiple widgets as input, and creates a virtual guideline based on the most extreme widget on the specified side. For example, a left barrier will align to the left of all the referenced views.

In fact there's a recommendation on how to [handle GONE widgets](https://developer.android.com/reference/androidx/constraintlayout/widget/Barrier#gone-widgets-handling).

I'm not going to go deeper about Barrier, you can check out [this awesome explanation](https://constraintlayout.com/basics/barriers.html) with good examples.

Here's the final layout with only the important parts:

```xml
...
<layout ...
    >
    <ScrollView ...
        >
        <androidx.constraintlayout.widget.ConstraintLayout ...
            >
            <ImageView
                ...
                android:id="@+id/sandwich_image"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                ...
                />

            <androidx.constraintlayout.widget.Guideline
                android:id="@+id/start_guideline"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_constraintGuide_begin="@dimen/margin_normal"
                />

            <androidx.constraintlayout.widget.Guideline
                android:id="@+id/end_guideline"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_constraintGuide_end="@dimen/margin_normal"
                />

            <TextView
                ...
                android:id="@+id/also_known_as_label"
                android:layout_width="0dp"
                app:layout_constraintEnd_toStartOf="@+id/end_guideline"
                app:layout_constraintStart_toStartOf="@+id/start_guideline"
                app:layout_constraintTop_toBottomOf="@+id/sandwich_image"
                />

            <TextView
                ...
                android:id="@+id/also_known_as_text"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/also_known_as_label"
                app:layout_constraintStart_toStartOf="@+id/also_known_as_label"
                app:layout_constraintTop_toBottomOf="@+id/also_known_as_label"
                ...
                />

            <androidx.constraintlayout.widget.Barrier
                android:id="@+id/also_known_as_barrier"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:barrierDirection="bottom"
                app:constraint_referenced_ids="also_known_as_label,also_known_as_text"
                />

            <TextView
                ...
                android:id="@+id/ingredients_label"
                android:layout_width="0dp"
                app:layout_constraintEnd_toStartOf="@+id/end_guideline"
                app:layout_constraintStart_toStartOf="@+id/start_guideline"
                app:layout_constraintTop_toBottomOf="@+id/also_known_as_barrier"
                />

            <TextView
                ...
                android:id="@+id/ingredients_text"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/ingredients_label"
                app:layout_constraintStart_toStartOf="@+id/ingredients_label"
                app:layout_constraintTop_toBottomOf="@+id/ingredients_label"
                ...
                />

            <androidx.constraintlayout.widget.Barrier
                android:id="@+id/ingredients_barrier"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:barrierDirection="bottom"
                app:constraint_referenced_ids="ingredients_label,ingredients_text"
                />

            <TextView
                ...
                android:id="@+id/origin_label"
                android:layout_width="0dp"
                app:layout_constraintEnd_toStartOf="@+id/end_guideline"
                app:layout_constraintStart_toStartOf="@+id/start_guideline"
                app:layout_constraintTop_toBottomOf="@+id/ingredients_barrier"
                />

            <TextView
                ...
                android:id="@+id/origin_text"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/origin_label"
                app:layout_constraintStart_toStartOf="@+id/origin_label"
                app:layout_constraintTop_toBottomOf="@+id/origin_label"
                ...
                />

            <androidx.constraintlayout.widget.Barrier
                android:id="@+id/origin_barrier"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:barrierDirection="bottom"
                app:constraint_referenced_ids="origin_label,origin_text"
                />

            <TextView
                ...
                android:id="@+id/description_label"
                android:layout_width="0dp"
                app:layout_constraintEnd_toStartOf="@+id/end_guideline"
                app:layout_constraintStart_toStartOf="@+id/start_guideline"
                app:layout_constraintTop_toBottomOf="@+id/origin_barrier"
                />

            <TextView
                ...
                android:id="@+id/description_text"
                android:layout_width="0dp"
                app:layout_constraintEnd_toEndOf="@+id/description_label"
                app:layout_constraintStart_toStartOf="@+id/description_label"
                app:layout_constraintTop_toBottomOf="@+id/description_label"
                ...
                />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </ScrollView>
</layout>
```
The source code is available on [GitHub](https://github.com/filipebezerra/sandwich-club-app-android-java/blob/master/app/src/main/res/layout/activity_detail.xml).