4

I'm trying to take multiple horizontally-chained Buttons and TextViews and chain them vertically as sets of views, but still maintain a flat view hierarchy. Here is my initial layout and code:

Initial Layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btnTopLeft"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#eeeeee"
        app:layout_constraintBottom_toTopOf="@+id/lblTopLeft"
        app:layout_constraintEnd_toStartOf="@+id/btnTopRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="1"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblTopLeft"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toTopOf="@+id/btnMiddleLeft"
        app:layout_constraintEnd_toEndOf="@id/btnTopLeft"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnTopLeft"
        app:layout_constraintTop_toBottomOf="@+id/btnTopLeft"
        tools:text="Button 1" />

    <Button
        android:id="@+id/btnTopRight"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#dddddd"
        app:layout_constraintBottom_toTopOf="@+id/lblTopRight"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnTopLeft"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="2"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblTopRight"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toTopOf="@+id/btnMiddleRight"
        app:layout_constraintEnd_toEndOf="@id/btnTopRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnTopRight"
        app:layout_constraintTop_toBottomOf="@+id/btnTopRight"
        tools:text="Button 2" />

    <Button
        android:id="@+id/btnMiddleLeft"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#cccccc"
        app:layout_constraintBottom_toTopOf="@+id/lblMiddleLeft"
        app:layout_constraintEnd_toStartOf="@+id/btnMiddleRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/lblTopLeft"
        tools:text="3"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblMiddleLeft"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toTopOf="@+id/btnBottomLeft"
        app:layout_constraintEnd_toEndOf="@id/btnMiddleLeft"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnMiddleLeft"
        app:layout_constraintTop_toBottomOf="@+id/btnMiddleLeft"
        tools:text="Button 3" />

    <Button
        android:id="@+id/btnMiddleRight"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#bbbbbb"
        app:layout_constraintBottom_toTopOf="@+id/lblMiddleRight"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnMiddleLeft"
        app:layout_constraintTop_toBottomOf="@+id/lblTopRight"
        tools:text="4"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblMiddleRight"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toTopOf="@+id/btnBottomRight"
        app:layout_constraintEnd_toEndOf="@id/btnMiddleRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnMiddleRight"
        app:layout_constraintTop_toBottomOf="@+id/btnMiddleRight"
        tools:text="Button 4" />

    <Button
        android:id="@+id/btnBottomLeft"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#aaaaaa"
        app:layout_constraintBottom_toTopOf="@+id/lblBottomLeft"
        app:layout_constraintEnd_toStartOf="@+id/btnBottomRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/lblMiddleLeft"
        tools:text="5"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblBottomLeft"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@id/btnBottomLeft"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnBottomLeft"
        app:layout_constraintTop_toBottomOf="@+id/btnBottomLeft"
        tools:text="Button 5" />

    <Button
        android:id="@+id/btnBottomRight"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#999999"
        app:layout_constraintBottom_toTopOf="@+id/lblBottomRight"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnBottomLeft"
        app:layout_constraintTop_toBottomOf="@+id/lblMiddleRight"
        tools:text="6"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblBottomRight"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@id/btnBottomRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnBottomRight"
        app:layout_constraintTop_toBottomOf="@+id/btnBottomRight"
        tools:text="Button 6" />

</android.support.constraint.ConstraintLayout>

When I set the visibility of a Button and its TextView, I would like for the button on the same row to fill the remaining space, but I want the buttons below or above it to remain in the same place until both buttons on the row have been hidden. Once both buttons on the same row have been hidden, then I would like for the remaining buttons to fill the remaining space. For example, here is what I want the layout to look like when I hide Button and TextView 3:

Correct Layout

But currently if I hide Button and TextView 3, here is what currently happens:

Incorrect Layout

This is expected behavior based on my current code, but I'm looking for a way to prevent the vertical chain from collapsing down until button 4 has also been hidden. Once button 4 is hidden, this is the desired result:

Correct Layout 2

I've played around with Barriers, but placing a barrier at any point seems to cause complications with the dynamic heights of the buttons because it breaks the vertical chain. Can anyone provide some insight on how to achieve this with a flat view hierarchy?

David
  • 834
  • 1
  • 10
  • 27

1 Answers1

4

Break up the screen into three vertically chained Views as follows:

enter image description here

I have set background colors to make the views more prominent, but they would probably be transparent or set to invisible in the actual implementation.

Now, add the buttons and labels so they are vertically constrained to the interior of these views. This does break the vertical chains, but the vertical sizing is now controlled by the vertical chains of the underlying views. The buttons and labels are still chained to the sides of the ConstraintLayout.

enter image description here

Buttons and labels still resize to the left and right as they did before. Now, however, the button underneath will not expand up until all views in a row are set to "gone."

enter image description here

You will need to set the underlying view to "gone" when all buttons and labels in a row are also set to "gone", so you will need to keep track of the status of the buttons and labels in a row.

Here is the XML:

activity_main.xml

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:id="@+id/topGroup"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_green_light"
        app:layout_constraintBottom_toTopOf="@id/centerGroup"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/centerGroup"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_light"
        app:layout_constraintBottom_toTopOf="@id/bottomGroup"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/topGroup" />

    <View
        android:id="@+id/bottomGroup"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_orange_light"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/centerGroup" />

    <Button
        android:id="@+id/btnTopLeft"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#eeeeee"
        app:layout_constraintBottom_toTopOf="@+id/lblTopLeft"
        app:layout_constraintEnd_toStartOf="@+id/btnTopRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/topGroup"
        tools:text="1"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblTopLeft"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="@+id/topGroup"
        app:layout_constraintEnd_toEndOf="@id/btnTopLeft"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnTopLeft"
        app:layout_constraintTop_toBottomOf="@+id/btnTopLeft"
        tools:text="Button 1" />

    <Button
        android:id="@+id/btnTopRight"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#dddddd"
        app:layout_constraintBottom_toTopOf="@+id/lblTopRight"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnTopLeft"
        app:layout_constraintTop_toTopOf="@id/topGroup"
        tools:text="2"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblTopRight"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="@+id/topGroup"
        app:layout_constraintEnd_toEndOf="@id/btnTopRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnTopRight"
        app:layout_constraintTop_toBottomOf="@+id/btnTopRight"
        tools:text="Button 2" />

    <Button
        android:id="@+id/btnMiddleLeft"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#cccccc"
        app:layout_constraintBottom_toTopOf="@+id/lblMiddleLeft"
        app:layout_constraintEnd_toStartOf="@+id/btnMiddleRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/centerGroup"
        tools:text="3"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblMiddleLeft"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="@+id/centerGroup"
        app:layout_constraintEnd_toEndOf="@id/btnMiddleLeft"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnMiddleLeft"
        app:layout_constraintTop_toBottomOf="@+id/btnMiddleLeft"
        tools:text="Button 3" />

    <Button
        android:id="@+id/btnMiddleRight"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#bbbbbb"
        app:layout_constraintBottom_toTopOf="@+id/lblMiddleRight"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnMiddleLeft"
        app:layout_constraintTop_toTopOf="@+id/centerGroup"
        tools:text="4"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblMiddleRight"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="@+id/centerGroup"
        app:layout_constraintEnd_toEndOf="@id/btnMiddleRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnMiddleRight"
        app:layout_constraintTop_toBottomOf="@+id/btnMiddleRight"
        tools:text="Button 4" />

    <Button
        android:id="@+id/btnBottomLeft"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#aaaaaa"
        app:layout_constraintBottom_toTopOf="@+id/lblBottomLeft"
        app:layout_constraintEnd_toStartOf="@+id/btnBottomRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/bottomGroup"
        tools:text="5"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblBottomLeft"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="@id/bottomGroup"
        app:layout_constraintEnd_toEndOf="@id/btnBottomLeft"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnBottomLeft"
        app:layout_constraintTop_toBottomOf="@+id/btnBottomLeft"
        tools:text="Button 5" />

    <Button
        android:id="@+id/btnBottomRight"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#999999"
        app:layout_constraintBottom_toTopOf="@+id/lblBottomRight"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnBottomLeft"
        app:layout_constraintTop_toTopOf="@+id/bottomGroup"
        tools:text="6"
        tools:textSize="42sp" />

    <TextView
        android:id="@+id/lblBottomRight"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="@id/bottomGroup"
        app:layout_constraintEnd_toEndOf="@id/btnBottomRight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/btnBottomRight"
        app:layout_constraintTop_toBottomOf="@+id/btnBottomRight"
        tools:text="Button 6" />

</android.support.constraint.ConstraintLayout>
Cheticamp
  • 61,413
  • 10
  • 78
  • 131
  • I had actually considered this, but it felt a little hacky. I had hoped ConstraintLayout had a better way of doing this than having to manage underlying invisible views and the count of visible buttons in each row, but maybe ConstraintLayout just isn't quite there yet. I'll mark this as the answer if a more elegant solution is not posted within a day or two. – David May 21 '19 at 13:17
  • @David Barriers seem to be an answer, but they don't play nicely with chains. There are some new capabilities in _ConstraintLayout 2.0_ such as a linear helper that may be of some use here. Unfortunately, there is little documentation of these features as of today. – Cheticamp May 21 '19 at 13:33
  • I'm going to go ahead and accept your answer as correct. I keep finding that if I want to do any design beyond the basics, I have to create an invisible View and setup constraints as though the View is a container for its "sub-views" (i.e. the solution you've provided). My view hierarchy remains flattened, but its much more cumbersome to understand. I'm a little disappointed ConstraintLayout isn't powerful enough to handle these scenarios better, but I'm sure it will become more robust over time. – David May 21 '19 at 19:39