DEV Community

Filipe Bezerra
Filipe Bezerra

Posted on

How I solved layout issues in dynamically gone widgets using ConstraintLayout

This post is about how to deal with 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:

...
<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>
Enter fullscreen mode Exit fullscreen mode

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

Alt Text

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

Alt Text

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

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:

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.

I'm not going to go deeper about Barrier, you can check out this awesome explanation with good examples.

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

...
<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>
Enter fullscreen mode Exit fullscreen mode

The source code is available on GitHub.

Top comments (0)