How to achieve overlap/negative margin on Constraint Layout? - android

Is it possible to achieve negative margin on constraint layout to achieve overlap?
I am trying to have a image centered on the layout and have a Text view such that it overlaps a by x dp. I tried setting negative margin value but no luck.
It would be great if there is a way to achieve this.

Update
ConstraintLayout now supports negative margins with version 2.1.0-alpha2. Simply state
android:layout_marginTop="-25dp"
for a negative 25dp margin. (This will only work if the top of the view is constrained. A margin has no effect in ConstraintLayout if the margin's side is not constrained.)
Clarification: The answer below remains valid, but I want to clarify a couple of things. The original solution will place a view with a de facto negative offset with respect to another view as stated and will appear in the layout as shown.
Another solution is to use the translationY property as suggested by Amir Khorsandi here. I prefer that solution as simpler with one caveat: The translation occurs post-layout, so views that are constrained to the displaced view will not follow the translation.
For example, the following XML displays two TextViews immediately below the image. Each view is constrained top-to-bottom with the view that appears immediately above it.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView"
android:layout_width="150dp"
android:layout_height="150dp"
android:tint="#388E3C"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_action_droid" />
<TextView
android:id="#+id/sayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Say my name."
android:textAppearance="#style/TextAppearance.AppCompat.Large"
app:layout_constraintTop_toBottomOf="#+id/imageView"
app:layout_constraintEnd_toEndOf="#+id/imageView"
app:layout_constraintStart_toStartOf="#+id/imageView" />
<TextView
android:id="#+id/sayIt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Say it."
android:textAppearance="#style/TextAppearance.AppCompat.Large"
app:layout_constraintEnd_toEndOf="#+id/sayName"
app:layout_constraintStart_toStartOf="#+id/sayName"
app:layout_constraintTop_toBottomOf="#id/sayName" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now, let's translate the "Say my name" TextView up by 50dp by specifying
android:translationY="-50dp"
This produces the following:
The "Say my name" TextView has shifted up as expected, but the "Say it" TextView has not followed it up as we might expect. This is because the translation occurs post-layout. Although the view moves post-layout, it can still be made clickable in the new position.
So, IMO, go with translationX and translationY for negative margins in ConstraintLayout if the caveat above doesn't affect your layout; otherwise, go with the space widget as outlined below.
Another caveat: As stated by Salam El-Banna in a comment to another answer, translationX will not be a good solution for RTL layouts since the sign of the translation will dictate the direction of the shift (left/right) regardless of the RTL or LTR nature of the layout.
Original answer
Although it doesn't appear that negative margins will be supported in ConstraintLayout, there is a way to accomplish the effect using the tools that are available and supported. Here is an image where the image title is overlapped 22dp from the bottom of the image - effectively a -22dp margin:
This was accomplished by using a Space widget with a bottom margin equal to the offset that you want. The Space widget then has its bottom constrained to the bottom of the ImageView. Now all you need to do is to constrain the top of the TextView with the image title to the bottom of the Space widget. The TextView will be positioned at the bottom of the Space view ignoring the margin that was set.
The following is the XML that accomplishes this effect. I will note that I use Space because it is lightweight and intended for this type of use, but I could have used another type of View and made it invisible. (You will probably need to make adjustments, though.) You could also define a View with zero margins and the height of the inset margin you want, and constrain the top of the TextView to the top of the inset View.
Yet another approach would be to overlay the TextView on top of the ImageView by aligning tops/bottoms/lefts/right and make suitable adjustments to margins/padding. The benefit of the approach demonstrated below is that a negative margin can be created without a lot of computation. That is all to say that there are several ways to approach this.
Update: For a quick discussion and demo of this technique, see the Google Developers Medium blog post.
Negative Margin for ConstraintLayout XML
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#mipmap/ic_launcher" />
<android.support.v4.widget.Space
android:id="#+id/marginSpacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="22dp"
app:layout_constraintBottom_toBottomOf="#+id/imageView"
app:layout_constraintLeft_toLeftOf="#id/imageView"
app:layout_constraintRight_toRightOf="#id/imageView" />
<TextView
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Say my name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/marginSpacer" />
</android.support.constraint.ConstraintLayout>

Another way is using translationX or translationY like this:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:translationX="25dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
it will work like android:layout_marginRight="-25dp"

Negative margins have never been officially supported in RelativeLayout. Negative margins will not be supported in ConstraintLayout. [...]
-- Romain Guy on Jun 8, 2016
Follow these two issues:
https://code.google.com/p/android/issues/detail?id=212499
https://code.google.com/p/android/issues/detail?id=234866

This is what I figured out after hours of trying to find a solution.
Let us consider two images, image1 and image2. Image2 is to be placed on top of image1 positioned to the bottom-right side.
Overlapping Views Example
We can use Space widget for overlapping views.
Constraint the Space widget's four sides with the four sides of the image1 respectively. For this example, constraint the image2's left side with the Space widget's right side and the image2's top side with the Space widget's bottom side. This will tie image2 with the Space widget and since the Space widget is constrained from all the sides, we can define required horizontal or vertical bias which will move image2 as required.
<?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=".Player">
<ImageView
android:id="#+id/image1"
android:layout_width="250dp"
android:layout_height="167dp"
android:src="#android:color/holo_green_dark"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Space
android:id="#+id/space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#+id/image1"
app:layout_constraintEnd_toEndOf="#+id/image1"
app:layout_constraintHorizontal_bias="0.82"
app:layout_constraintStart_toStartOf="#+id/image1"
app:layout_constraintTop_toTopOf="#+id/image1"
app:layout_constraintVertical_bias="0.62" />
<ImageView
android:id="#+id/image2"
android:layout_width="82dp"
android:layout_height="108dp"
android:src="#android:color/holo_green_light"
app:layout_constraintStart_toEndOf="#+id/space"
app:layout_constraintTop_toBottomOf="#+id/space" />
</android.support.constraint.ConstraintLayout>
Additionally, to position image2 on the center-bottom of image1, we can constraint image2's left and right sides with Space widget's left and right sides respectively. Similarly, we can place image2 anywhere by changing image2's constraints with Space widget.

I found a way to do it much simpler.
Basically have the ImageView, then on the Text View add top constraint to match the top constraint of the image and just add the margin top of the TextView to match to achieve the -ve margin type behavior.

This will help many
In my case i want my design like this:
Means i want my image is display half of their width so the basically i need negative margin of half of the actual image width but my whole layout in constraint layout and constraint layout does not allowed negative margin so i achieved this with below code
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="#drawable/ic_launcher_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="50dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
So that ImageView will end at the starting of the guideline. and the effect is same as like negative margin at the start of 50dp.
And also if your view's width is not fixed and it's in percentage so that you can place guideline with percentage and achieve whatever effect you want
Happy Coding:)

You only need to use Space widget in your layout
<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">
<Space
android:id="#+id/negative_margin"
android:layout_width="16dp"
android:layout_height="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toLeftOf="parent"/>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Widget who needs negative margin"
app:layout_constraintTop_toBottomOf="#+id/negative_margin"
app:layout_constraintLeft_toLeftOf="#+id/negative_margin" />

Place Background View Behind Subject View
I wanted to use negative margin to add a view behind a subject view that is proportionally bigger than the subject view. The solution I found was to scale android:scaleX="1.2" and android:scaleY="1.2" the background view while constraining it to all sides of the subject.
<View
android:id="#+id/subjectBackground"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleY="1.2"
android:scaleX="1.2"
app:layout_constraintBottom_toBottomOf="#+id/subjectView"
app:layout_constraintEnd_toEndOf="#+id/subjectView"
app:layout_constraintStart_toStartOf="#+id/subjectView"
app:layout_constraintTop_toTopOf="#+id/subjectView" />

This is an old question yet very much asked, the fastest way to achieve this is by constraining the top and bottom to the side of the view you want to anchor to, like so:
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="55dp"
android:layout_height="55dp"
app:layout_constraintBottom_toBottomOf="#+id/parent_view_id"
app:layout_constraintTop_toBottomOf="#+id/parent_view_id"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
This will center it in the bottom line of the view, centered horizontally.

One can try this way, this is much simpler
<?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="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MyProfileFragment">
<ImageView
android:id="#+id/imageViewUserPic"
android:layout_width="#dimen/dp60"
android:src="#mipmap/ic_launcher"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_margin="20dp"
android:layout_height="wrap_content">
</ImageView>
<ImageView
android:id="#+id/imageViewEdit"
app:layout_constraintBottom_toBottomOf="#+id/imageViewUserPic"
android:src="#drawable/ic_edit_red_round"
app:layout_constraintEnd_toEndOf="#+id/imageViewUserPic"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ImageView>
</androidx.constraintlayout.widget.ConstraintLayout>
The layout will be like this..

this is my soluation
<com.oven.test.avatar
android:id="#+id/imageview_a"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginTop="28dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.oven.test.smallicon
android:id="#+id/small_icon_overlap_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="#+id/imageview_a"
app:layout_constraintTop_toTopOf="#+id/imageview_a"
app:layout_constraintVertical_bias="1"
android:layout_marginBottom="20dp"/>

Using translationX and translationY may works for your situation.
<TextView
android:id="#+id/tvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="text"
android:translationX="-15dp"
android:translationY="10dp"
app:layout_constraintEnd_toEndOf="#+id/imageView"
app:layout_constraintTop_toTopOf="#+id/imageView" />

A Simple Way.
I'm not sure best way.
Just wrap using LinearLayout
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<View
android:layout_width="wrap_content"
android:layout_marginLeft="-20dp"
android:layout_height="wrap_content"/>
</LinearLayout>

Related

How to prevent an imageview to be "pushed" outside the screen by a textview with variable dimensions?

In a constraint layout, I have a textview, with an imageview next to it:
But sometimes the text in the textview can be very long and sometimes span more than one line. In these cases, the imageview get pushed outside the view:
Even addind a constraint between the imageview and the view container, the imageview gets pushed outside the view.
The goal is to always have the image right next to the text and if it grows, the image starts getting pushed to the side as long is not going outside the view. When it touches the boundaries of the view, it should stay there while the text wraps to the next line.
This code block just shows the case of the second picture:
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView" android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
tools:text="This is a cat with a lot more text next to it so pay attention "/>
<ImageView
android:layout_width="30dp"
android:layout_height="30dp" tools:srcCompat="#tools:sample/avatars[3]"
android:id="#+id/imageView"
app:layout_constraintStart_toEndOf="#+id/textView" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0"/>
</androidx.constraintlayout.widget.ConstraintLayout>
I've tried usgin barriers and guidelines, but they really do not serve for this case. The textview needs to be wrap_content since its size is variable and it is preferable to use constraint layout, that's why I did not used another one. Chains also did not work here.
You can use a packed chain with a bias of 0 to make it start-aligned and then set app:layout_constrainedWidth="true" for both views so that their constraints are respected when wrapping content.
<?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">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="Text goes here"
android:textSize="24sp"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="#id/image"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/textView"
app:layout_constraintTop_toTopOf="parent"
tools:src="#tools:sample/avatars" />
</android.support.constraint.ConstraintLayout>
The problem is that you are setting textview's wrap_content. Doing so will let the textview decide its own width, ignoring layout constraints such as margins.
You need to set width to 0dp which is interpreted as match_constraint in constraint layout, it allows the layout to decide textview's width which means that textview will be given as much width as necessary after margins and other layout constraints are applied.
Now to your desired effect, first remove all constraints from both views. Select both of them, then right click and select chain option from the options menu. Then Pack them horizontally. This would form a chain.
Why do we need chain?
Because you want the imageview to right of textview and textview to left of image view, this kind of two way constraint cannot be added because TextView won't draw till imageview is drawn and imageview is waiting for textview to draw.
Chain will help you tackle this problem.

How do I center a text inside a chain relative to the whole screen?

I want to create a user name centered TextView with an edit button to the right of the screen. If I specify the TextView to match the start/end of the parent, it will center. However, the text will overflow and paint over the button. I can chain one side of the text to the button, but then the text will not be centered. Here is the example code I have where tvName is not centered due to the button in the horizontal chain:
<TextView
android:id="#+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/pt_name_font_size"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="#id/tvUserMsisdn"
app:layout_constraintEnd_toStartOf="#id/ibChangeName"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/aivProfile"
tools:text="B"/>
<TextView
android:id="#+id/tvName2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/pt_name_font_size"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="#id/tvUserMsisdn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/tvName"
tools:text="A"/>
<ImageButton
android:id="#+id/ibChangeName"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="#dimen/default_margin"
app:layout_constraintBottom_toBottomOf="#id/tvName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#id/tvName"
android:background="#android:color/black"/>
This is what it looks like:
I can't use a horizontal bias, since the button has to remain with a static size, and the width of the screen may vary, as well as the size of the font. Ideally, I would be able to specify two app:layout_constraintEnd_toStartOf attributes, one for the whole width of the screen, so the text remains centered, and another one for the button, so it won't overflow.
Ideally, I would be able to specify two app:layout_constraintEnd_toStartOf attributes
I'm sure you already know this, but that is impossible.
The only thing you can do that is like that is to use a Barrier, but that's not helpful here because (in this case) that would be functionally identical to simply constraining the end to the start of the ImageButton.
The best thing you can do here, in my opinion, is to constrain the TextView's start and end to the parent, and then use padding to make sure that long text doesn't overlap the ImageButton. The button has a fixed width (28dp), and a fixed margin (#dimen/default_margin), so you can give the TextView left/right padding equal to 28dp + 2x default_margin. This will make sure that text wraps at least default_margin away from the left edge of the ImageButton.
Put both the TextView and the ImageButton inside a RelativeLayout:
<RelativeLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvName2"
android:layout_alignParentStart="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="#+id/ibChangeName"
android:textSize="#dimen/pt_name_font_size"
android:textStyle="bold"
tools:text="A"/>
<ImageButton
android:id="#+id/ibChangeName"
android:layout_alignParentEnd="true"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="#dimen/default_margin"
android:background="#android:color/black"/>
</RelativeLayout>
This attribute for the TextView
android:layout_toStartOf="#+id/ibChangeName"
ensures that the TextView won't overlap with the ImageButton
Now you have to center the relative layout
If the ImageButton always has a fixed size (28dp in this case) you can use a Space widget with the same dimensions as the button and place it on the left edge of the parent. You can then chain the three Views together and set the chain's style to spread_inside. The TextView's width can be set to match_constraint and its gravity to center or use wrap_content and app:layout_constrainedWidth="true" so it doesn't overlap the other Views if it gets too big.
Example XML to get the general idea:
<?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">
<Space
android:id="#+id/space"
android:layout_width="28dp"
android:layout_height="28dp"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintEnd_toStartOf="#id/textview"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Textview"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="#id/button"
app:layout_constraintStart_toEndOf="#id/space" />
<ImageButton
android:id="#+id/button"
android:layout_width="28dp"
android:layout_height="28dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/textview" />
</android.support.constraint.ConstraintLayout>

Is it possible to control the bias of both sides of a chain in Androids ConstraintLayout?

I created two rows of ImageButtons through chains and used the packed style to close the gap between the two rows. However there is a lot of extra space surrounding the overall ImageButtons rows on the top and bottom.
When I try to use layout_constraintVertical_bias="0.1" on the top row buttons and layout_constraintVertical_bias="0.9" on the bottom row buttons to remove the gap it only shifts the rows up to the top of the layout. I know chain constraint details should be in the parent view but is there anyway to get the children of the chain to adapt a bias as well? I know this can be done by changing the width to 0dp but when I do that the images on the bottom become wider than the images at the top due to an uneven number of buttons.
<?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="wrap_content">
<ImageButton
android:id="#+id/button1"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/button4"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#id/button2"
app:layout_constraintVertical_chainStyle="packed"/>
<ImageButton
android:id="#+id/button2"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/button4"
app:layout_constraintLeft_toRightOf="#+id/button1"
app:layout_constraintRight_toLeftOf="#id/button3"
app:layout_constraintVertical_bias="1"/>
<ImageButton
android:id="#+id/button3"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/button5"
app:layout_constraintLeft_toRightOf="#id/button2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintVertical_chainStyle="packed"/>
<ImageButton
android:id="#+id/button4"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/button1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#id/button5"
app:layout_constraintBottom_toBottomOf="parent"/>
<ImageButton
android:id="#+id/button5"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/button3"
app:layout_constraintLeft_toRightOf="#id/button4"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
Edit:
This is what I'm trying to accomplish below. I basically want the arrows to push up to the red line while the ImageButtons stay in the place they are at right now so there is a gap between the buttons and the top and bottom edge of the layout. I attempted to shrink the gap in the second image using vertical bias but as you can see it only worked for the top row of buttons and not the buttons on the bottom row.
So, the question is really "why is my ConstraintLayout so tall?" If you can use a later release of ConstraintLayout (1.1.0-beta3 is the latest), this problem will go away. If you can't use a beta release, there is a work-around but upgrading is the best solution. Ping back if you can't use a beta release.

Using Constraint Layout to center two items together in the center

Here is my intended layout.
<- ActionBar with back button
[ Image1 ] [Image2][Image3] [Image4]
Doese anyone know how to support this with ConstraintLayout? Image2,Image3 are to be in the center and with little or no margin between them. Image1, and Image4 are to be near to left right edges respectively.
Is there anyway to achieve the same with LinearLayout or RelativeLayout for the row of images?
Does coordinatorlayout always have to be root layout? and if so does it support the ActionBar?
You can create two buttons centered by using chains (chainStyle="packed")
They will be such that the space on the left and right of them is the same. (ratio can be adjusted with bias)
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:text="Two"
app:layout_constraintEnd_toStartOf="#+id/button2"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="268dp" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="0dp"
android:text="Buttons centered"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/button"
tools:layout_editor_absoluteY="268dp" />
Using a guideline is an easy way to achieve what you are trying to do.
The key thing to notice is app:layout_constraintGuide_percent="0.5" which places the guide in the center of the view horizontally. From there you can attach your images to either side of it.
<?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="com.example.junkrmm.MainActivity">
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"
/>
<ImageView
android:id="#+id/A"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#android:drawable/a"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="0dp"
android:layout_marginTop="0dp" />
<ImageView
android:id="#+id/B"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:srcCompat="#android:drawable/b"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="0dp"
android:layout_marginTop="0dp" />
<ImageView
android:id="#+id/C"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#android:drawable/c"
app:layout_constraintEnd_toStartOf="#+id/guideline"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="0dp"
android:layout_marginTop="0dp" />
<ImageView
android:id="#+id/D"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#android:drawable/d"
app:layout_constraintStart_toEndOf="#+id/guideline"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="0dp"
android:layout_marginTop="0dp"/>
</android.support.constraint.ConstraintLayout>
You could achieve the same with LinearLayout if the sizes of your images are consistent, but ConstraintLayout is a better fit for this.
Off the top of my head I believe that CoordinatorLayout needs to be the root viewgroup, but I could be mistaken. It does support ActionBar in the form of Toolbar which is a newer more flexible variant of ActionBar.
By using Horizontal chain and adjusting the bias you can get the layout as above.
If you find it difficult to understand plz go through this video, here I have designed the layout using the layout editor. Youtube: Centring button in constraint layout using chains and bias
STEPS:
open layout editor and add two button
hold shift and select both button, then select Horizontal chain from chain option by right clicking
toggle the spacing option by clicking the chain option that appears on hovering over the linked buttons.
add two more buttons each to left and right edge
constraint left button to left edge and right to right edge
set bias of left to 10 and right to 90 using the layout editor.
Yes, this is possible (as long as you know your images aren't so wide that they will overlap, and as long as Image2 and Image3 are the same width).
Positioning Image1 and Image4 is easy; you can just constrain their outside edges to the outside edges of the parent.
Then use these constraints to position Image2's right-hand edge to the exact center of the parent:
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
and these constraints to position Image3's left-hand edge to the exact center:
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintLeft_toRightOf="parent"
Update
If you know in advance that Image2 and Image3 are not the same width, and you need the combination of the two to be centered, then (as far as I'm aware) you can only solve this problem by introducing an intermediate parent (e.g. a LinearLayout).
In this case, you'd position Image1 and Image4 to the parent edges as before, and use these constraints to "center" the LinearLayout inside the ConstraintLayout:
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
Then you simply place Image2 and Image3 inside this LinearLayout.

How to center multiple Views together using ConstraintLayout?

Background
Google has announced a new layout called "ConstraintLayout" that's supposed to be the ultimate layout, that could replace all of the layouts while staying flat (without nested layouts) and have better performance.
The problem
Thing is, I barely see any tutorials for it that could help me on this matter, other than the video presented on Google IO.
What I am trying to do is, given that I have a vertically-centered LinearLayout within another layout - convert them both into a single ConstraintLayout.
After all, this is the purpose of this new layout...
The layout I wish to deal with looks like this:
Notice that the views in the center are only centered vertically, and that the 2 textViews are to the right of the ImageView, which is also centered vertically.
This all works well with RelativeLayout, which has the LinearLayout of the 2 TextViews, but I wish to know how to convert them into a single ConstraintLayout.
Here's a sample XML of what I've shown:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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="wrap_content"
android:minHeight="?attr/listPreferredItemHeightSmall">
<ImageView
android:id="#+id/appIconImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginEnd="4dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="4dp"
android:layout_marginStart="2dp"
android:adjustViewBounds="true"
android:src="#android:drawable/sym_def_app_icon"
tools:ignore="ContentDescription"/>
<LinearLayout
android:id="#+id/appDetailsContainer"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="#+id/appIconImageView"
android:layout_toLeftOf="#+id/overflowView"
android:layout_toRightOf="#+id/appIconImageView"
android:layout_toStartOf="#+id/overflowView"
android:orientation="vertical">
<TextView
android:id="#+id/appLabelTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:text="label"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textDirection="locale"
tools:ignore="HardcodedText,UnusedAttribute"/>
<TextView
android:id="#+id/appDescriptionTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:minLines="3"
android:text="description"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textDirection="locale"
tools:ignore="HardcodedText,UnusedAttribute"/>
</LinearLayout>
<ImageView
android:id="#+id/overflowView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:padding="10dp"
app:srcCompat="#drawable/ic_more_vert_black_24dp"
tools:src="#drawable/ic_more_vert_black_24dp"
tools:ignore="ContentDescription"/>
<ImageView
android:id="#+id/isSystemAppImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="#+id/overflowView"
android:layout_alignLeft="#+id/overflowView"
android:layout_alignParentBottom="true"
android:layout_alignRight="#+id/overflowView"
android:layout_alignStart="#+id/overflowView"
android:adjustViewBounds="true"
android:scaleType="centerInside"
app:srcCompat="#drawable/ic_warning_black_24dp"
tools:ignore="ContentDescription"
tools:src="#drawable/ic_warning_black_24dp"/>
</RelativeLayout>
What I tried
I tried to read some articles and watch some videos of Google :
https://codelabs.developers.google.com/codelabs/constraint-layout/index.html#0
https://www.youtube.com/watch?v=sO9aX87hq9c
https://youtu.be/csaXml4xtN8?t=1693
That didn't help, so I tried to using it, hoping I will find out how to use it myself.
But I can't find out how to do it. I tried using the feature to convert the layouts, but this makes a huge mess of the views and puts additional margins that I don't want to have.
The question
How can I convert the 2 layouts into a single ConstraintLayout ?
Take a look at my answer here.
ContraintLayout contains a feature - Chains - that makes it possible to implement what you are asking:
Chains provide group-like behavior in a single axis (horizontally or
vertically).
A set of widgets are considered a chain if they a linked together via
a bi-directional connection
Once a chain is created, there are two possibilities:
Spread the elements in the available space
A chain can also be "packed", in that case the elements are grouped together
As for your case, you'll have to pack your label and description TextViews and center them in you parent vertically:
(make sure you use a version of ConstraintLayout that supports chains)
<?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:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:text="TextView"
app:layout_constraintBottom_toTopOf="#+id/button"
app:layout_constraintLeft_toRightOf="#+id/imageView2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"/>
<TextView
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Button\nMkay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="#+id/imageView2"
app:layout_constraintTop_toBottomOf="#+id/textView"/>
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#mipmap/ic_launcher"/>
<ImageView
android:id="#+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#mipmap/ic_launcher"/>
<ImageView
android:id="#+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:srcCompat="#mipmap/ic_launcher"/>
</android.support.constraint.ConstraintLayout>
Update 25-06-2019 (#Saeid Z):
Now in constraint layout 1.1.3 we must use app:layout_constraintHorizontal_chainStyle="packed" instead of app:layout_constraintVertical_chainPacked="true"
Set app:layout_constraintVertical_bias="0.5" to the views that need to be centered vertically, bias attribute only works if you specify the constraints for the boundaries (e.g. top and bottom for vertical bias, left and right for horizontal bias)
An example:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/constraintLayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="#id/constraintLayout"
app:layout_constraintBottom_toBottomOf="#id/constraintLayout"
app:layout_constraintVertical_bias="0.5" />
</android.support.constraint.ConstraintLayout>
Got it working in my layout here: https://github.com/hidroh/tldroid/blob/master/app/src/main/res/layout/activity_main.xml, pretty much similar layout, though I position things at 1/3rd of screen height.
EDIT: This answer had been written before chains became available. Please use chains now, see above answer: https://stackoverflow.com/a/40102296/1402641
For now I believe your only choice would be to wrap the two text views in another layout - probably linear layout best fits this situation.
But the team behind constraint layout said they want to introduce "virtual containers", which serve exactly this use case: you group two or more views together and set a constraint (like centring vertically) on the container. The idea is that it is not a full nested layout inside of constraint layout, but just something that constraint layout uses to position it's children - hence it should provide better performance than nesting.
They mention it in their I/O talk (linked to the exact time). So I guess stay tuned.
To center something vertically or horizontally, set an opposing constraint on the layout.
Centered Vertically
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
Centered Horizontally
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
Take a look at mine example (Center components in ConstraintLayout)
For now you could center "image" (constrain left-top-bottom), constrain "label" top to "image" top, constrain "description" top to "label" bottom.
In example below i have matched button height with two textviews to the right (which is your case). Other textviews are constrained as on the link above.
UPDATE: Answering to your comment below:
I have installed your app and this is only i can think of currently. Since it is a ViewHolder layout, layout_height can be set to wrap_content, or you can fix it if you'd like to. I can send you xml if you want, i dont want to flood the answer.
Top TextView left constraint is constrained to right constraint of ImageView. Also, top TextView -> top constraint is constrained to top of container, so is right. Bottom TextView is constrained to bottom of container. TextViews in the middle are constrained to match width of top TextView and got no connection with ImageView.

Categories

Resources