Reputation: 175
It has been quite some time since I used XML layout for Android. I am struggling with what seems to be very simple thing, yet I cannot find a quick solution.
I have a constraintLayout with an image, title, description and a button:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<ImageView
android:id="@+id/image_cover"
android:layout_width="200dp"
android:layout_height="300dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:background="@android:color/holo_red_dark"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:textColor="@color/black"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/image_cover"
app:layout_constraintTop_toTopOf="@id/image_cover"
tools:text="Title" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintBottom_toTopOf="@id/button"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="spread_inside"
android:text="@string/some_very_long_text"/>
<Button
android:id="@+id/button"
app:layout_constraintTop_toBottomOf="@id/description"
app:layout_constraintBottom_toBottomOf="@id/image_cover"
app:layout_constraintStart_toStartOf="@id/title"
android:layout_width="wrap_content"
android:padding="0dp"
android:layout_margin="0dp"
android:background="@color/black"
android:text="Button"
android:layout_height="36dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
I tried to create a chain with either packed (and vertical bias) or spread-inside, however I am only able to achieve either result 1 or 2 and not both.
The idea to do it in XML only, not in Java/Kotlin code.
Upvotes: 0
Views: 522
Reputation: 62831
Since you specify that the button is 39dp
in height, you can set a Space widget 39dp
from the bottom of the description using a bottom margin. Now set a barrier with a direction = bottom
to the Space widget and the description. Now the barrier will float between the bottom of the Space widget and the bottom of the description and will alight on whichever is lower.
Now you can set the top of the button to the bottom of the Barrier and the button will float. The XML is below.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<ImageView
android:id="@+id/image_cover"
android:layout_width="200dp"
android:layout_height="300dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:background="@android:color/holo_red_dark"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:textColor="@color/black"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/image_cover"
app:layout_constraintTop_toTopOf="@id/image_cover"
tools:text="Title" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/some_very_long_text"
app:layout_constraintBottom_toTopOf="@id/button"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="spread_inside" />
<Space
android:id="@+id/space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="36dp"
android:padding="0dp"
app:layout_constraintBottom_toBottomOf="@id/image_cover"
app:layout_constraintStart_toStartOf="@id/title" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_margin="0dp"
android:background="@color/black"
android:padding="0dp"
android:text="Button"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/barrier" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="description,space" />
</androidx.constraintlayout.widget.ConstraintLayout>
If you don't explicitly know the height of the button, you can create an invisible clone of the button with the same constraints and use the invisible button for the barrier.
Here is another version that also works, but I am not sure why. It doesn't use a Space.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<ImageView
android:id="@+id/image_cover"
android:layout_width="200dp"
android:layout_height="300dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:background="@android:color/holo_red_dark"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:text="Title"
android:textColor="@color/black"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/image_cover"
app:layout_constraintTop_toTopOf="@id/image_cover" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/some_very_long_text1"
app:layout_constraintBottom_toTopOf="@id/button"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintVertical_bias="0.0"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_margin="0dp"
android:background="@color/black"
android:padding="0dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="@id/barrier"
app:layout_constraintStart_toStartOf="@id/title" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="image_cover,description" />
</androidx.constraintlayout.widget.ConstraintLayout>
Update: I was curious about the last layout above. Although it works, I wouldn't use it and I have come to the conclusion that it is a bug since the barrier is located below the button but should be immediately below either the description or the image cover. I am using "androidx.constraintlayout:constraintlayout:2.1.4".
More: I no longer think that this is a bug but a reasonable resolution to a, seemingly, impossible condition: The barrier is positioned to the bottom of the TextView and ImageView. The button's bottom is constrained to the barrier while the TextView's bottom is constrained to top of the button. If the barrier is positioned immediately below its referenced views then the wrap_content height of the TextView cannot be honored since the button will intrude into the TextView height (For the long text.)
What is pictured above could be a compromise. The barrier is still below its referenced views, although much farther below, and all constraints and layout sizes can be honored.
It's hard to tell if this is intentional or not or if it will continue to be the case going forward. I believe that the Space solution is the better solution.
Upvotes: 1