How to achieve overlap/negative margin on Constraint Layout?
Asked Answered
M

13

159

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.

Medora answered 23/3, 2017 at 19:8 Comment(1)
Guideline may help, I haven't tried.Consistent
K
295

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.

enter image description here

<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:

enter image description here

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:

enter image description here

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>
Kelikeligot answered 25/3, 2017 at 12:48 Comment(9)
Not working in my case, ImageView is centered on the parent, TextView is overlap above ImageView. Then Space makes the error when App back from background. I think 0dp, 0dp makes some trouble. What about just use Guideline.Km
This way can't work for view has constraints with parent.Caterer
Why do I need a Space? I tried without space and I've the same result.Plosion
@Plosion the point here is that the bottom of the space is constrained at the bottom of the imageView, the margin moves the space up, and then the top of the text view is constrained at the bottom of the space which creates this "half overlaying" effect we are trying to achieve. Please post an answer here if you've managed to get it without a space so we can see the constraints you used on your two views.Besant
The Update does not work android:layout_marginTop="-25dp". It has not effectsEjaculation
@Ejaculation Are you using ConstraintLayout version 2.1.0-alpha2 or later?Kelikeligot
@Cheticamp: I use implementation 'androidx.constraintlayout:constraintlayout:1.1.3'. I tried to change it to the version number you suggested with implementation 'androidx.constraintlayout:constraintlayout:2.1.0' but all of my XML layout files showed many errors after that. This is why I kept my initial version and I'll try to find another way of using negative marginsEjaculation
Thanks, this is working for me. If top view is overlapping you just set android:layout_width="0dp" android:layout_height="0dp" For me its working find after spent half day on this issue.Luik
I now use "androidx.constraintlayout:constraintlayout:2.1.3" but still the suggested soluton does not have any effect.Ejaculation
B
88

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"

Byproduct answered 2/8, 2018 at 11:40 Comment(3)
Be aware of this, since the actual position of the view stays at the original position (before translation), it will be translated only visually. Thereby, onClick events will be only triggered from within the old position.Strainer
@goemic's statement seems to be wrong, since this is not a tween animation but a property animation. (please correct me if I am wrong)Bee
bad practice as it does not support RTL layoutsCanyon
C
28

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

Consistent answered 23/3, 2017 at 19:49 Comment(2)
their idea may be that with constraintlayout you don't need negative margin. That may be true if you have all elements on one layout. Sometimes though you want to group logically elements like in a command strip made of buttons, which has its own advantages for reusability and clarity and encapsulation. At that point the group will have a rectangular shape, and it's this limitation on that shape that makes negative margins useful and necessary again.Tila
Negative margins emulate exactly the same concept of the baseline on text, being text a complex shape within a rectangular containerTila
S
25

We can use Space widget for overlapping views.

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 image

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.

Spender answered 23/9, 2018 at 16:12 Comment(0)
M
12

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.

Medora answered 25/3, 2017 at 19:24 Comment(2)
unless image height is variableHighflier
It would still work. You'd just need to be careful about where you'd like your upper widget to overlay the image, setting the margin from the right or from the bottom could be a better option, or you could use bias, all of which answer the question.Gat
U
4

This will help many

In my case i want my design like this:

after

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:)

Ulibarri answered 15/11, 2018 at 7:50 Comment(0)
R
1

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" />

Rademacher answered 26/2, 2020 at 12:41 Comment(0)
E
1

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" />
Exogamy answered 3/1, 2021 at 21:42 Comment(0)
M
0

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.

Moureaux answered 11/4, 2020 at 14:56 Comment(0)
F
0

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.. enter image description here

Famed answered 25/1, 2021 at 6:3 Comment(0)
G
0

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"/>
Gd answered 24/5, 2021 at 9:30 Comment(0)
M
0

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" />
Magnetomotive answered 29/11, 2021 at 7:3 Comment(0)
P
-2

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>
Panache answered 14/6, 2018 at 2:51 Comment(2)
This adds a little more nesting, BUT it works for animating views out from the top, unlike the other solutions here, which assume that the View will be completely shown. Thanks!Crenulate
This answer needs a lot more information. Unclear how putting something in a LinearLayout can be used to accomplish overlap.Nicotinism

© 2022 - 2024 — McMap. All rights reserved.