Reputation: 60081
I'm using ConstraintLayout where I will show as below
I would like to hide First
(using gone), and which the view I expect to be as below (where ElasticBody will stretch over to use up the original First
view space as well.
However, when I actual set First
to gone
, my view turn out to be as below (all image as from Android Studio Design view). My Elastic Body
is missing as well, and the height expanded weirdly.
My layout code as below
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/txt_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0ff"
android:text="First"
android:visibility="gone"
android:textSize="26sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/txt_body"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txt_body"
android:layout_width="0dp"
android:background="#f0f"
android:layout_height="wrap_content"
android:text="Elastic Body"
android:textSize="26sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/txt_tail"
app:layout_constraintStart_toEndOf="@+id/txt_first"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txt_tail"
android:background="#ff0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tail"
android:textSize="26sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/txt_body"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
(Note, if you remove the gone
, you'll get the first image view).
Why is this so? How could I fix it where when my First
is gone, I could the Elastic Body
stretch out correctly?
p/s: I know how to do it in LinearLayout and RelativeLayout... but wonder if this is a limitation on ConstraintLayout?
Upvotes: 77
Views: 95158
Reputation: 6981
Try following.
Set the first view's left and top constraints to "parent". After that:
So, whenever you set the first view's visibility to "gone", the body view will be stretched like how you want it.
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/txt_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0ff"
android:text="First"
android:textSize="26sp"
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@+id/txt_body"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/txt_body"
android:layout_width="0dp"
android:background="#f0f"
android:layout_height="wrap_content"
android:text="Elastic Body"
android:textSize="26sp"
app:layout_constraintRight_toLeftOf="@+id/txt_tail"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="@+id/txt_first"
/>
<TextView
android:id="@+id/txt_tail"
android:background="#ff0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tail"
android:textSize="26sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
If you want to do using barrier then also you can do it.
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/txt_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0ff"
android:text="First"
android:textSize="26sp"
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@+id/barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txt_body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#f0f"
android:text="Elastic Body"
android:textSize="26sp"
app:layout_constraintStart_toEndOf="@+id/barrier"
app:layout_constraintEnd_toStartOf="@+id/txt_tail"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txt_tail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff0"
android:text="Tail"
android:textSize="26sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="txt_body,txt_first" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="txt_body,txt_tail" />
</androidx.constraintlayout.widget.ConstraintLayout>
Upvotes: 33
Reputation: 7479
An answer has already been given linking to Barriers. I will provide an example of how I've actually implemented it:
<TextView
android:id="@+id/textView1"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:text="Some text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textView2"
app:layout_constraintTop_toBottomOf="@id/textView1"
app:layout_constraintLeft_toLeftOf="parent"
android:text="Some other text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:barrierDirection="bottom"
app:constraint_referenced_ids="textView1,textView2" />
This example shows 2 TextViews
either of which can be gone
. The Views are on stacked from top to bottom, so barrierDirection
is set to bottom
. Should you need another direction, just change that line accordingly.
Setting any of the 2 TextViews
to gone
, will result in the Barrier
shifting to the bottom of the other, and if we set both to gone
, it'll just shift up to the element that textView1
's top constraint was referencing, in this case, the parent.
Note: If your
textView1
's top constraint is something else, i.e. it's below another element, the barrier will end up there if both views are set togone
.
Upvotes: 11
Reputation: 28793
Say, you want to have a picture like this:
Here you have indents between the title and "Nice work", between "Nice work" and time, also horizontal indent to "Opinions". They are centered vertically.
"Opinions" is attached to the star, so that can be multilined and stay centered. I show results for 2 variants: in the first row opinions are multilined, while in the next row it is a single line. In columns you can see 4 variants of showing/hiding 2 labels.
A more simple and preferrable way is to wrap both labels into LinearLayout
and insert it into parent ConstraintLayout
. Then you can set vertical gravity, show or hide labels, hide the LinearLayout
itself.
If you don't want to have nested layouts, use Barrier
s and Group
s. It is a difficult task that can waste many hours. A key is having additional View
s for aligning. Here I have 2 hiding labels ("Nice work" and "Opinions"), and I have to add 2 views (spaces).
The height of the right space is equal to the height of the star (14dp).
To simplify hiding several views, I joined them into groups.
You can see horizontal dotted lines - they are Barrier
s. I align them on tops and bottoms of the most big views (barrier_2 is similar):
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="left_text,opinion" />
Vertical aligning is based on these 2 additional Space
s (see marginTop="10dp"):
<Space
android:id="@+id/left_text_space"
android:layout_width="25dp"
android:layout_height="10dp"
android:layout_marginTop="10dp"
app:layout_constraintStart_toEndOf="@id/left_text"
app:layout_constraintTop_toBottomOf="@id/title" />
It is difficult to cover all situations, so see the following layout:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="15dp"
android:paddingTop="5dp"
android:paddingRight="15dp"
android:paddingBottom="5dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lineSpacingExtra="4sp"
android:lines="1"
android:paddingBottom="5dp"
android:text="«Title text»"
android:textColor="#333333"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="left_text,opinion" />
<TextView
android:id="@+id/left_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#112233"
android:paddingLeft="5dp"
android:paddingTop="4dp"
android:paddingRight="5dp"
android:paddingBottom="4dp"
android:text="Nice work"
android:textColor="#ffffff"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="@id/barrier_2"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toTopOf="@id/left_text_space" />
<Space
android:id="@+id/left_text_space"
android:layout_width="25dp"
android:layout_height="10dp"
android:layout_marginTop="10dp"
app:layout_constraintStart_toEndOf="@id/left_text"
app:layout_constraintTop_toBottomOf="@id/title" />
<androidx.constraintlayout.widget.Group
android:id="@+id/left_text_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:constraint_referenced_ids="left_text,left_text_space" />
<Space
android:id="@+id/opinion_space"
android:layout_width="1dp"
android:layout_height="14dp"
android:layout_marginTop="10dp"
app:layout_constraintStart_toStartOf="@id/left_text_space"
app:layout_constraintTop_toBottomOf="@id/title" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="left_text,opinion" />
<ImageView
android:id="@+id/opinion_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@null"
app:layout_constraintBottom_toBottomOf="@id/barrier_2"
app:layout_constraintStart_toEndOf="@id/left_text_space"
app:layout_constraintTop_toTopOf="@id/opinion_space"
app:srcCompat="@drawable/ic_filled_rate_star" />
<TextView
android:id="@+id/opinion"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:lineSpacingExtra="1sp"
android:text="1. Opinion 1.\n2. Opinion 2.\n3. Opinion 3.\n4. Opinion 4."
android:textColor="#1122aa"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@id/opinion_icon"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/opinion_icon"
app:layout_constraintTop_toTopOf="@id/opinion_icon" />
<androidx.constraintlayout.widget.Group
android:id="@+id/opinion_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:constraint_referenced_ids="opinion_icon,opinion,opinion_space" />
<ImageView
android:id="@+id/time_icon"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_gravity="center_vertical"
android:layout_marginTop="8dp"
android:contentDescription="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier_2"
app:srcCompat="@drawable/ic_time" />
<TextView
android:id="@+id/time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginLeft="7dp"
android:ellipsize="end"
android:lineSpacingExtra="1sp"
android:lines="2"
android:paddingBottom="7dp"
android:text="17:00"
android:textColor="#9e9e9e"
android:textSize="11sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/time_icon"
app:layout_constraintTop_toTopOf="@id/time_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
Then in your activity you can show/hide labels. Hide Group
s, not views inside, because strangely inside a Group
views are always visible.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
left_text_group.visibility = View.GONE
opinion_group.visibility = View.VISIBLE
}
Upvotes: 12