ConstraintLayout hides the last line of TextView with app:layout_constrainedHeight="true" - android

I have noticed weird behavior of ConstraintLayout (version 1.1.3) that hides the last line of TextView whenever I try to use height with wrap_content property and layout_constrainedHeight is set to true.
With layout_constrainedHeight:
With no layout_constrainedHeight:
Source code:
<?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:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="Lorem..."
app:layout_constrainedHeight="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I thought that whenever I want to use wrap_content with ConstraintLayout I have to set layout_constrainedHeight to true, but this sometimes gives me weird bugs. Am I missing something?
EDIT
If I remove the margin around TextView, it works fine. It seems that ConstraintLayout does something wrong with wrap_content and margins.

According to documentation:
WRAP_CONTENT : enforcing constraints (Added in 1.1)
If a dimension is set to WRAP_CONTENT, in versions before 1.1 they will be treated as a literal dimension -- meaning, constraints will not limit the resulting dimension.
While in general this is enough (and faster), in some situations, you might want to use WRAP_CONTENT, yet keep enforcing constraints to limit the resulting dimension. In that case, you can add one of the corresponding attribute:
app:layout_constrainedWidth=”true|false”
app:layout_constrainedHeight=”true|false”
I'll emphasise a piece of this quote:
keep enforcing constraints to limit the resulting dimension
So basically, setting layout_constrainedHeight or layout_constrainedWidth to true will preserve constraints and reduce view size by the specified margin instead of pushing all the other views around and increasing current view height/width to fit the content.
Here is an example with app:layout_constrainedHeight="true" and app:layout_constrainedWidth=”true” and different margins. Red TextView wrapped it's content and after that was reduced in size. Green TextView has no app:layout_constrained...="true" attributes and margins set. Their height is equal, but the width is different in the end.
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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/top_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="86dp"
android:layout_marginEnd="26dp"
android:background="#ff2356"
android:text="#string/lorem_kind_of"
android:textColor="#android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedHeight="true"/> <!-- This line is the only difference -->
<TextView
android:id="#+id/bottom_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="86dp"
android:layout_marginEnd="26dp"
android:background="#009900"
android:text="#string/lorem_kind_of"
android:textColor="#android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/top_text_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
My guess is that you probably do not need to use app:layout_constrainedHeight attribute. You are welcome to leave a comment and we will elaborate on this further if my answer doesn't solve your problem.
UPDATE (22 May 2020)
Looks like the behaviour that you want is achievable only without the app:layout_constrainedHeight="true". I may be wrong, it depends on the final desired result, but according to my "experiments" looks like app:layout_constrainedHeight constraints a view from growing further it's minimum size.
I've updated the XML code and recorded a little video to see the difference.

Try adding one more constraint for bottom of your TextView - app:layout_constraintBottom_toBottomOf="parent" or app:layout_constraintBottom_toTopOf="#id/someOtherView" and this someOtherView should have bottom constraint to the parent. There should be a vertical chain in order app:layout_constrainedHeight to work.
Hope it helps.

Related

How can I force an empty ConstraintLayout to fill its parent container?

I am using a ConstraintLayout as an overlay. It should be as wide and as tall as its parent container. However, for some strange reason it does not fill its parent vertically (but does horizontally). Here is the relevant 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="match_parent"
tools:context=".ui.home.HomeFragment">
<androidx.constraintlayout.widget.ConstraintLayout <-- This is the overlay
android:id="#+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
With the above code the overlay is not visible either (its height is 0dp). When I place a TextView inside, it expands to fit the TextView. In short, it is behaving as if its height was set to wrap_content.
How do I force it to fill its container while leaving it empty?
The problem is most likely your size specifications. From the documentation for ConstraintLayout:
Important: MATCH_PARENT is not recommended for widgets contained in a ConstraintLayout. Similar behavior can be defined by using MATCH_CONSTRAINT with the corresponding left/right or top/bottom constraints being set to "parent".
There really should be a lint rule for this or ConstraintLayout should reject match_parent.

Android textview: text flowing over RHS of screen

I have created a text view (mostly auto-created by Android Studio's 'new Tabbed Activity' helper) and populated it with some text stored as raw resources. I have allowed the view to scroll, and it's all working fine except that the last letter or two of many lines disappears off the edge of the screen. It's the same with two devices, with different screen sizes.
here's my resource file:
<?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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.PlaceholderFragment">
<TextView
android:id="#+id/section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/activity_horizontal_margin"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:layout_marginEnd="#dimen/activity_horizontal_margin"
android:layout_marginBottom="#dimen/activity_vertical_margin"
android:textSize="20sp"
android:scrollbars = "vertical"
android:justificationMode="inter_word"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="#+id/constraintLayout"
tools:layout_constraintLeft_creator="1"
tools:layout_constraintTop_creator="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
I have tried enlarging the marginEnd (from 20 to 50dp) - no effect, and also tried android:gravity="center", but that made it worse!
[Edit]
As a result of suggested answers, the view has changed a little; a margin has appeared down the RHS so the text no longer disappears 'off the edge' of the screen, but it disappears 'behind' the margin. Does this help? Also , does the fact that this is a tabbed activity (as I mentioned earlier) have any bearing? If I scroll sideways, to see the next tab, the missing parts of the words still don't appear.
This happens because your TextView is not constrained to the right edge of the parents.
You can fix this by adding the following to your TextView
app:layout_constraintRight_toRightOf="parent"
But you should avoid using left and right in your layouts. So the correct way would be to add
app:layout_constraintEnd_toEndOf="parent"
And also change width to 0dp so that the TextView can take up the space defined by the constraints.
Use app:layout_constrainedWidth="true" on your textView with start and end constraint set to parent.

Flat ConstraintLayout with layout_constraintHeight_min for each row doesn't work

I have what I think is a pretty common use case: I have multiple rows of information on the screen. I'm hoping to implement these views without having to use nested ViewGroups. Each row should have a minimum height, but expand if the contents are larger than the minimum height. The contents should be nested vertically.
It seems like this simplified example should work:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Row"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/view"
app:layout_constraintBottom_toBottomOf="#+id/view"/>
<View
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#8800ff00"
app:layout_constraintHeight_min="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="#id/textView"/>
</android.support.constraint.ConstraintLayout>
Instead of creating a ViewGroup for each row, I have a flat layout with constraints. A View for each row sets the row height and can be clickable. The views inside the row are siblings in the layout hierarchy, but center themselves vertically in the middle of the View for that row. Since I've specified app:layout_constraintHeight_min and anchor the bottom of the View to the bottom of the contents, it should grow with the contents.
But there's a problem:
ConstraintLayout adds undesired spacing above the row! Note that the unwanted spacing above the row is equal to the correct spacing between the bottom of the contents and the bottom of the row.
My theory is this: since the View's bottom is anchored to the bottom of the contents (the TextView) it wants to stick tightly to that and be right next to it. If I force it to move further away, it adds something like a bottom margin to accomplish that, it adds a similar top margin to be symmetrical.
How do I make it stop? If it wasn't for that unwanted spacing on the top, I'd have exactly what I need. Perhaps there's some special ConstraintLayout trick, some magical attribute to fix this behavior. Or maybe there's a completely different way to use ConstraintLayout to accomplish the UI I want.
I realize that using fixed-height rows would make this much simpler, but I don't like doing that if the contents can grow.
I could change my UI to have a nested ConstraintLayout for each row, but I'd rather not do that after working so hard to make a complex layout completely flat, without multiple layers of ViewGroups. But that's what I'll do if I can't find a better solution, which I hope to find here.
I think removing the min height and adding some margin in the TextView will do the thing and instead of using match_parent you can use constraints if possible
<?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/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="50dp"
android:text="Row is not column"
android:textColor="#283858"
app:layout_constraintBottom_toBottomOf="#+id/view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/view"/>
<View
android:id="#+id/view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#8800ff00"
app:layout_constraintBottom_toBottomOf="#id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Constraint layout: Cannot separate elements and keep them all on screen

I have a simple constraint layout like this:
<?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">
<ListView
android:id="#+id/main_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/rescan"
/>
<android.support.v7.widget.AppCompatButton
android:id="#+id/rescan"
android:layout_width="wrap_content"
android:text="#string/rescan_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintBottom_toBottomOf="parent"
/>
</android.support.constraint.ConstraintLayout>
Despite the layout_constraintBottom_toTopOf="#id/rescan", the listview potentially extends halfway through the button.
To try and correct that, I added hardcoded dimensions (which I prefer not to do); to the listview:
android:layout_marginBottom="50sp"
And to the button:
android:layout_height="40sp"
android:textSize="20sp"
android:layout_margin="10sp"
However, I then got this (emulator pic, the design view also corresponds to this):
The button is halfway off the screen.
So I decided to remove the hardcoded dimensions and use a barrier. The developer page is ambiguous about how barrierDirection is supposed to work, but this
"constraintlayout.com" example makes it clear the direction should be the side you want the barrier on in relation to the elements listed in referenced_ids. Based on that, here's what I have inside the constraint layout:
<ListView
android:id="#+id/main_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/bottomBarrier"
/>
<android.support.constraint.Barrier
android:id="#+id/bottomBarrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="#id/rescan"
/>
<android.support.v7.widget.AppCompatButton
android:id="#+id/rescan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/rescan_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.5"
/>
But things are not really getting better...
It's impossible to tell, but here the barrier is at the very bottom. Which explains why the listview, with Bottom_toTopOf the barrier, also now extends all the way to the bottom.
However, that makes the whole barrier, who's direction is top and who's constrained id is the button, totally pointless. In no sense is it keeping the listview on one side and the button on the other. It's below both of them.
Even more berserk: If I change the direction to bottom and leave everything else as it, the barrier jumps to the very top, the listview jumps halfway off the screen upward, and the button stays in place.
I'm totally flummoxed. My two biggest questions are:
Why, in the first picture, does the listview extend halfway through the button, when it is set bottom-to-top of the button?
Why, in the last picture, is the barrier, with a direction of "top" and referencing the button id, below the button?
1.
Your ListView's height is set to wrap_content which means the view will compute its own size and constraints will not limit the dimension. ConstraintLayout-1.1.0 introduced new attributes that allow using wrap_content yet keep enforcing the constraints to limit the specified dimension. These attribues are:
app:layout_constrainedWidth="true"
app:layout_constrainedHeight="true"
2.
As for the Barrier, there's an error in the way you're referencing the view's id:
app:constraint_referenced_ids="#id/rescan"
This should be changed to:
app:constraint_referenced_ids="rescan"

ConstraintLayout - constraintHeight_percent gets ignored when view above another view?

]I'm using constraint layout in version 1.1.0 beta 6 (Which is way better than the stable 1.0.2 by the way...)
However, using constraintHeight_percent doesn't work for me in some case.
Here's what I want to achieve:
Place a button(bottom-button) with a fixed height at the bottom of the constraint layout
Place another button(top-button) between the top of the constraint layout, and above bottom-button.
Make the top-button final height to be half of its height.
Here's the 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"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#71B9DE">
<Button
android:id="#+id/bottomButton"
android:layout_width="0dp"
android:layout_height="150dp"
android:text="BOTTOM BUTTON"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:layout_width="0dp"
android:layout_height="0dp"
android:text="TOP BUTTON - The height should be 75dp!!"
app:layout_constraintBottom_toTopOf="#id/bottomButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
Here's the preview:
And here's how I expected it to be:
I don't understand why after using app:layout_constraintHeight_percent="0.5", the height of the top button is still 150DP and not 75DP.
It seems like app:layout_constraintHeight_percent="0.5" calculation is done related to the parent height, and not related to the button height itself, after it got constrained.
Btw, I'm not looking for other solutions (Using guidelines, barriers). I really try to figure out why it doesn't work.
Thanks for the help!
According to official reference document in the section 'MATCH_CONSTRAINT dimensions (Added in 1.1)':
layout_constraintWidth_percent and layout_constraintHeight_percent : will set the size of this dimension as a percentage of the parent
provided that the corresponding constraints are set to MATCH_CONSTRAINT (0dp)
You shoud use android:layout_width="match_parent" instead of 0dp

Categories

Resources