]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
Related
I have a View in a Constraint Layout and I would like that at the very beginning it should be outside of the screen (and then later slowly move into the scree from right to left). Now, I kind of need something like negative bias or margins.
I had a look at this question How to achieve overlap/negative margin on Constraint Layout?. The accepted answer using android:layout_marginTop="-25dp" does not have any effect (altough the top of the view is constrained and I use"androidx.constraintlayout:constraintlayout:2.1.3").
I tried the second most upvoted answer and used the code:
view.setTranslationX(view.getWidth() - 20);
This actually works. However, the problem is that when the Fragment is created you first see that the view is not on the left for a short period of time. This is not what I want. I would like to have the view beyond the right rim of the layout at the very very beginning such that it can later move into the layout.
Do you have any idea how I can do that? Ideally I would like to do this programmatically.
Update: Here is the code of the XML layout where a negative margin does not have any effect:
<?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"
android:background="#drawable/game_test_background"
tools:context=".MainActivity"
android:id="#+id/constraintLayout">
<ImageView
android:id="#+id/imageView_RedRectange_Test"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_marginTop="-1250dp"
app:layout_constraintWidth_percent="0.25"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.048"
app:srcCompat="#drawable/red_rectangle" />
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.102"
app:layout_constraintHorizontal_bias="0.373"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.745"
app:layout_constraintWidth_percent="0.12" />
</androidx.constraintlayout.widget.ConstraintLayout>
Okay so to have a negative margin you can use translateX, translateY or TranslationZ.
in xml like so:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World!"
android:translationX="-60dp"
android:translationY="-90dp"
android:translationZ="-420dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
or programmatically like so:
View view = ...;
view.setTranslationX(-60);
view.setTranslationY(-90);
view.setTranslationZ(-420);
Then in order to slowly bring it in from right to left you can use the animate() method like so:
View view = ...;
view.animate().setDuration(1000).translationX(-600).start();
There is a problem with setting the width of the button using app:layout_constraintWidth_percent when the ImageView has a negative margin. The problem should go away if you can set a definite width to the button (instead of 0dp).
The problem should also resolve if you set app:layout_constraintWidth_percent to a value such that the text of the button shows completely on one line.
Here is a simplified layout to demonstrate this issue. The ConstraintLayout has two views that are simply constrained to the parent to appear in vertical center of the layout. These two views have no dependencies on each other. In addition, the ImageView has a top margin of -250dp, so it should appear above the layout's vertical center.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/holo_green_light">
<ImageView
android:id="#+id/redRectangle"
android:layout_width="100dp"
android:layout_height="30dp"
android:layout_marginTop="-250dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/red_rectangle" />
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.12" />
</androidx.constraintlayout.widget.ConstraintLayout>
Here is what happens then the width of the ImageView is changed from 0dp to a non-zero value of 1dp. When the width is set to 0dp, the negative margin seems to be ignored. It is only when the width is set to a non-zero value that the ImageView is correctly placed.
Changing the width of the button should have no effect on the placement of the ImageView; however, the button only appears in the proper position when the button has a non-zero width.
Here is what happens when the app:layout_constraintWidth_percent is increased so that the word "Button" is not cutoff.
Again, the placement of the ImageView should be independent of the width of the button. Instead, the button only appears in the correct position when the app:layout_constraintWidth_percent is set such that the word "Button" is not cutoff.
This is only an issue with negative margins. Positive margins work as expected.
This is a strange problem, so you may want to use one of the other solutions mentioned.
(ConstraintLayout version 2.1.3)
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.
I'm migrating some projects to AndroidX with Kotlin. I'm having some issues with ConstraintLayout, I already know that, according to the documentation:
Negative margins will not be supported in ConstraintLayout.
[...]
But I have the following situation:
I need to move up in 5dp the LinearLayout, however I need the height to continue to match the lower limit of the screen. That is, I move up 5dp and increment the height by 5dp.
As it's in the image, I've already tried translateY, but it just moves the entire view (not what I need). Also I can not create a view inside the id#top with height of 5dp and align with constraintTop_toTopOf, since they are part of different groups.
Is there any solution for this case?
It's tricky to get views to overlap in ConstraintLayout, but you can do it by adding an invisible view and constraining the overlapping view to the invisible view.
In this case the invisible view's bottom could be constrained to the bottom of the green LinearLayout, with a bottom margin of 5dp. The red LinearLayout can then have its top constrained to the bottom of the invisible view. This should give you 5dp of overlap.
Try copy-pasting the following into your constraint layout
<LinearLayout
android:id="#+id/green"
android:layout_width="0dp"
android:layout_height="100dp"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="#android:color/holo_green_light" />
<View
android:id="#+id/dummyView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="5dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="#id/green"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<LinearLayout
android:id="#+id/red"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="#id/dummyView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="#android:color/holo_red_light" />
Note that a dimension of "0dp" means "match constraints" when set on a child view of a ConstraintLayout. This is not obvious, but is in fact documented here https://developer.android.com/reference/android/support/constraint/ConstraintLayout
The whole purpose of ConstraintLayout is to have a flat view hierarchy. Therefore, having LinearLayouts nested in defeats the purpose.
I suggest you get rid of the nested LinearLayouts and do everything using constraints.
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:
I want to position views outside of a ConstraintLayout to animate them with a sliding animation. I've tried setting contraints like constraintBottom_toTopOf="parent" but the View stays inside the container.
Note that I want to achieve this with constraints to use built-in animations, not with in-code animations.
Any idea how I could do this ?
I'm using compile 'com.android.support.constraint:constraint-layout:1.1.0-beta1'
with Android Studio 3.0 Beta 7
This is a simple xml file that should place the view outside of the container :
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#color/colorAccent">
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#color/colorPrimary"
app:layout_constraintBottom_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
But this is the result
This appears to be an issue with ConstraintLayout 1.1.0-beta1; It works as expected in ConstraintLayout 1.1.0-beta3.
Update to ConstraintLayout 1.1.0-beta3. I will also note that you need to constrain your view horizontally by doing something like the following.
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#color/colorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toTopOf="parent" />
On a side note, negative margins are not accepted in ConstraintLayout. See this Stack Overflow question regarding negative margins and ConstraintLayout.
In every view you can use negative margin, which will put the view outside of the parent view, and then set the clipping parameters.
android:clipChildren="false"
android:clipToPadding="false"
this will make the view not to clip.
I got another way to solve the problem:
1.Add a anchor(anchor_left) layout_constraintStart_toStartOf="parent".
2.Add YourView layout_constraintEnd_toStartOf="#+id/anchor_left"
That's it!
code:
<android.support.constraint.ConstraintLayout>
<View
android:id="#+id/anchor_left"
app:layout_constraintStart_toStartOf="parent"/>
<YourView
android:id="#+id/ll_left"
app:layout_constraintEnd_toStartOf="#+id/anchor_left"/>
</android.support.constraint.ConstraintLayout>
What I did is:
created a view of 0dp height inside the ConstraintLayout, e.g. "fakeView"
placed the new fakeView anchored at Top of the ConstraintLayout
when I need to hide a View, translate it outside the constraint..
change the constraint of the view you want to hide, in order to have BOTTOM connected to the Top of the FakeView.
I think you can use same technique to move object on the left of the fakeview or on the right.
One trick would be to set negative margin for the side you want, in the ConstraintLayout itself. This requires that other views that have constraint to that side be offset:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_marginBottom="-48dp">
<ImageButton
android:id="#+id/leftButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="72dp"
android:background="#drawable/shape_next_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageButton
android:id="#+id/rightButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="24dp"
android:background="#drawable/shape_previous_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>