Constraint Layout with two chained TextViews doesn't render properly - android

I'm trying to create an layout which renders two TextViews packed together as in picture #1. Now, when first (red) TextView has more text than the allowed space it expands itself to fit it's constraints - which is an expected behaviour, see picture #2.
Now when i reverse the situation - make the second (violet) TextView expand I'm not getting similar result - picture #3. As we can see the second TextView doesn't respect the first TextView constraints.
The other thing that I've also found is that if we replace the order of TextViews in our *.xml file as "TextView red -> TextView violet" to "TextView violet <- TextView red" while keeping the constraints it works properly - picture 4 (but the previous case ie fails again).
I've used the latest constraint layout libraries:
// implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0-alpha2'
And the 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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:background="#f00"
android:id="#+id/a"
android:layout_width="0dp"
android:layout_height="0dp"
android:textSize="50sp"
android:text="a"
app:layout_constraintHeight_default="wrap"
app:layout_constraintBottom_toTopOf="#id/b"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
/>
<TextView
android:background="#f0f"
android:id="#+id/b"
android:layout_width="0dp"
android:layout_height="0dp"
android:textSize="50sp"
android:text="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
app:layout_constraintHeight_default="wrap"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="#+id/a"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintVertical_chainStyle="packed"/
</androidx.constraintlayout.widget.ConstraintLayout>
If you do have an idea how to make two TextViews behave as in picture #1, #2 and #4 i would be very grateful.

You need to specify a minimum height constraint app:layout_constraintHeight_min="wrap" to both TextViews in order to make sure that both will at least wrap their content, in other words this will make any of the TextViews not to be greedy on one another.
Also this will keep both TextViews stretchable while maintaining the minimum height.
The only downside of this that both TextViews don't obey to the height constraints, you can see in below pic dashed line of height limits, also the top TextView text is cut. This is because both TextViews tends to wrap their content while keeping constraints at the same time.
In my trial, the padding is proportional to the size of the TextView and how big is the content of both of them.
A possible workaround to add padding at the top of top TextView, and at the Bottom of bottom Textivew
Another way to work on is to be strict to height constraint, and don't wrap height content by removing app:layout_constraintHeight_default="wrap" from both.
<?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=".MainActivity">
<TextView
android:id="#+id/a"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#f00"
android:paddingTop="0dp"
android:text="a"
android:textSize="50sp"
app:layout_constraintBottom_toTopOf="#+id/b"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/b"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#f0f"
android:paddingTop="0dp"
android:text="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbzzzzzzzzzzzzzzz"
android:textSize="50sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/a" />
</androidx.constraintlayout.widget.ConstraintLayout>

Edit: misunderstood wanted behavior.
You have 2 options, use
app:layout_constraintHeight_min="minimumHeight"
or
app:layout_constraintHeight_percent="percentage/100"

Related

Position button on top-end of CardView

How can I add a button on top-end of CardView?
I have a solution but I don't like set a fixed height (ie 50dp) of button and than set margin_top (ie 25dp) of card_view.
Do you have other solutions?
<?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="wrap_content">
<androidx.cardview.widget.CardView
android:id="#+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="#dimen/corner_radius"
app:cardElevation="2dp"
app:cardUseCompatPadding="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#id/btn">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="#+id/view_cover"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#ddd"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<com.google.android.material.button.MaterialButton
android:id="#+id/btn"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginEnd="#dimen/dimen_16"
android:elevation="#dimen/dimen_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
You can do it like following
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragments.LibraryFragment">
<androidx.cardview.widget.CardView
android:id="#+id/my_card"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:src="#android:drawable/ic_delete"
app:layout_constraintWidth_percent="0.08"
app:layout_constraintDimensionRatio="1:1"
app:tint="#color/redColor"
android:elevation="50dp"
app:layout_constraintTop_toTopOf="#id/my_card"
app:layout_constraintBottom_toTopOf="#id/my_card"
app:layout_constraintStart_toStartOf="#id/my_card"
app:layout_constraintEnd_toEndOf="#id/my_card"
app:layout_constraintHorizontal_bias="0.98"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
This will generate output like this
the image size will be based on the width of the screen which will be differ from device to device and it will resize according to it. and also it will be square due to we provided the ratio as 1:1
Looks like constraint layout doesn't allow negative margin(offset), so all you can do is workarounds.
There is another workaround, which i think is better than your example in terms of "intention". It goes like this:
Assuming your button is 50dp width/height.
Place Space element inside the constraint layout. Let's say width/height is 25dp(half of 50dp).
Align the Space element to the corner of the constraint layout. In case you want to put the button top end, then top of Space to top of constraint layout, and end of Space to end of constraint layout.
At this point, you virtually created "anchor" points where you can align other elements to, that is, the bottom and start of Space.
Align the start of the button to the start of Space, and the bottom of the button to the bottom of Space.
The button is now placed as if you would do layout_marginEnd/layout_marginBottom of -25dp.
The reason I think this is better is that the overlapping detail is no longer the constraint layout's concern, instead, it's the button and its neighboring element's.

Android sticky-footer: Align footer view to table, until it reaches screen size, then become fixed.at the bottom

This should be similar to an iOS tableview footer, also seen in various websites (sticky-footer).
I want to achieve the following:
A is a RecyclerView with variable number of rows.
When A is smaller than screen (or parent) size, B (footer) should be placed bellow the last row.
When A + B are bigger than screen size, then B becomes fixed at the bottom and A content is scrollable.
We are currently performing this with onMeasure functions that calculate all components heights in order to resize A accordingly.
I was wondering if there is an easier way to do it, maybe with ConstraintLayout.
Put A and B in a vertical packed chain with a bias of 0 to align it to the top. You also need to set app:layout_constrainedHeight="true" for the RecyclerView so that its constraints are taken into account when it gets too big to fit them (parent's height remains match_parent in this case):
<?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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/A"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintVertical_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/B" />
<TextView
android:id="#+id/B"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Footer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/A"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The above solution does not seem to work on Constraintlayout:2.0.0-beta2, looks like a bug introduced in that version. Works on 2.0.0-beta1 and 1.1.3.
Another way would be to set parent's height to wrap_content and then you can use the default chainstyle and remove the bias:
<?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="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/A"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/B" />
<TextView
android:id="#+id/B"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Footer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/A"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
This solution works on all versions.
Just try ConstraintLayout, this should very well be possible, just fix Bs height and constrain it to the bottom and A and turn the bias up to top.

RecyclerView doesn't properly wrap its content when it has a max height of WRAP_CONSTRAINT

I'm trying to have a RecylerView with a varying item count wrap its height to its items, but to never have it exceed its constraints. I believe I've set this up properly. Here's a simple XML for such a layout:
<!-- container.xml -->
<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="600dp"
android:background="#colors/blue">
<View
android:id="#+id/bottomGreenView"
android:layout_width="0dp"
android:layout_height="100dp"
android:background="#colors/green"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#colors/red"
app:layout_constrainedHeight="true"
app:layout_constraintHeight_max="0dp"
app:layout_constraintVertical_bias="0"
app:layout_constraintBottom_toTopOf="#id/bottomGreenView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:itemCount="1"
tools:listitem="#layout/test_list_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- test_list_item.xml -->
<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:background="#colors/white">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:text="List item here"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Note that the container is blue, the RecyclerView is red and the bottom view that should not be exceeded is green. And since the list items are white, there is no spacing between the items and the RecyclerView should wrap around the list items, the red background of the RecyclerView should never be seen. However, you can clearly see here that the RecyclerView is in fact seen and is much taller than it should be:
What I'm trying to achieve is this, where the RecyclerView properly wraps its content:
Interestingly, this does actually work as expected if I have the TextView's width wrap_content and remove its end-to-end constraint (the latter only so that it's not being centered horizontally). However, in my actual app I need these constraints as I also have content on the right side that I don't want to potentially overlap with long texts.
To me, this feels like an issue in either ConstraintLayout or RecyclerView (or both in tandem), so I've also filed an issue in the bug tracker. But if it is not, then I'd be happy to hear what you all have to say

Change vertical space between elements using guidelines in constraint layout, Android

I am new to constraints layout. I want to build a very basic UI in which there are four views with equal spaces between them. Now what I want is that when I run the code on smaller device the spaces between elements should be less and when I run it on a tab, the spaces increase.
Upon searching, i came across this:
How to make ConstraintLayout work with percentage values?
Now i know how to add guidelines with percentage, but I am not completely clear still.
Am I supposed to place a horizontal guideline after every view? And anchor the view with its top and bottom guideline? But isn't it two much of work? For 4 views do I need to put 8 guidelines?
If I place a horizontal guideline on 50% of screen, and want to use it as anchor, which constraints will I apply on other views on its top?
If anyone can clear my understanding, it would be highly appreciated.
You can implement via vertical chain and horizontal chain.
read ConstraintLayout Chain.
The approach with Guidelines you describe would probably be the way to do it using the older ConstraintLayout version as discussed in the topic you linked. Now that the ConstraintLayout-1.1.0 is out it is possible to set percentage based dimensions for views by using the app:layout_constraintHeight_percent and app:layout_constraintWidth_percent.
In your case the best approach in my opinion would be to create a vertical chain of the views and set the desired height as percentage of the parent for each view. Assuming the total height percentage of all views would be less than 100%, the remaining space would then be equally divided between the views by using chain_spread_inside or chain_spread (default) attribute.
Example XML:
<?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"
tools:context=".MainActivity">
<View
android:id="#+id/view1"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/holo_orange_dark"
app:layout_constraintVertical_chainStyle="spread_inside"
app:layout_constraintHeight_percent="0.2"
app:layout_constraintBottom_toTopOf="#id/view2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/view2"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/holo_red_dark"
app:layout_constraintHeight_percent="0.3"
app:layout_constraintBottom_toTopOf="#id/view3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/view1" />
<View
android:id="#+id/view3"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/holo_blue_dark"
app:layout_constraintHeight_percent="0.1"
app:layout_constraintBottom_toTopOf="#id/view4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/view2" />
<View
android:id="#+id/view4"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/holo_green_dark"
app:layout_constraintHeight_percent="0.2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/view3" />
</android.support.constraint.ConstraintLayout>
Result with spread_inside chain style:
and with default spread chain style:

Put 2 views in a ConstraintLayout that's itself in another ConstraintLayout

I am trying to understand how Android's ConstraintLayout works, and in order to do so, I want to create a layout that would take 1/4 of the view height with a left/right/bottom margin of 25dp, and in this layout, put two views, the first one would take 70% of its height and the second one the remaining 30%.
To sum up this:
I tried this so far:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout android:layout_height="match_parent"
android:layout_width="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.constraint.ConstraintLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
android:layout_marginBottom="25dp"
app:layout_constraintDimensionRatio="w,1:4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" >
<View
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintDimensionRatio="w,7:10"
android:background="#color/red" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintDimensionRatio="w,3:10"
android:background="#color/light_blue" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
But instead I'm only having the bottom view (that should take 30% of the height) taking the whole layout space. What am I doing wrong?
Thanks for your help.
What you have:
Property app:layout_constraintDimensionRatio is "self dependable" , that is it only adjust width-to-height ratio of itself.
What you require:
If you're using ConstrainLayout 1.1.x you can use property app:layout_constraintHeight_percent , which takes values from 0 to 1.
Also, you might have to adjust constrains of these views top and bottom to be relative to each other.

Categories

Resources