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:
Related
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"
Consider a trivial ConstraintLayout with a 20% vertical GuideLine and a thick ugly vertical bar left of the invisible line (see XML).
I can manipulate the percentile programmatically by using ConstraintSet.setGuidelinePercent() as per tynn's example, but I need a more dynamic implementation (eg: a draggable GuideLine or Barrier).
Must be a better way than attaching a drag listener to the vertical bar and using setGuidelinePercent()?
It seems incomprehensible that ConstraintLayout would not provide a native solution - perhaps I just couldn't find it?
Sample XML:
<?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">
<TextView
android:id="#+id/leftTV"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="#AAA"
android:text="LEFT: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/verticalBar"
/>
<View android:id="#+id/verticalBar"
android:layout_width="8dp"
android:layout_height="match_parent"
android:background="#000000"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="#id/guideLine" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideLine"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".20" />
<TextView
android:id="#+id/rightTV"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="#DDD"
android:text="RIGHT: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="#+id/guideLine"
app:layout_constraintEnd_toEndOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Take a look at MotionLayout. (Emphasis is mine.)
MotionLayout is a layout type that helps you manage motion and widget animation in your app. MotionLayout is a subclass of ConstraintLayout and builds upon its rich layout capabilities. As part of the ConstraintLayout library, MotionLayout is available as a support library and is backwards-compatible to API level 14.
In addition to describing transitions between layouts, MotionLayout lets you animate any layout properties, as well. Moreover, it inherently supports seekable transitions. This means that you can instantly show any point within the transition based on some condition, such as touch input.
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.
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
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.