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>
Here's the representation of the layout above in design mode:
After running the application in my physical device I got the desired result:
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:
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>
The source code is available on GitHub.
Top comments (0)