Is it a bad practice to use negative margins in Android?
In 2010, @RomainGuy (core Android engineer) stated that negative margins had unspecified behavior.
In 2011, @RomainGuy stated that you can use negative margins on LinearLayout
and RelativeLayout
.
In 2016, @RomainGuy stated that they have never been officially supported and won't be supported by ConstraintLayout
.
In December 2020(v2.1.0, official release June 2021), negative margin support for constraints has been added to ConstraintLayout.
It is easy to work around this limitation though.
Add a helper view (height 0dp, width constrained to parent) at the bottom of your base view, at the bottom add the margin you want.
Then position your view below this one, effectively allowing it to have a "negative" margin but without having to use any unsupported negative value.
android ConstraintLayout does not allow negative margins
Support for setting negative margin in a ConstraintLayout
has been added in ConstraintLayout 2.1.0 alpha 2 on the 17th of December 2020.
You can update to it by setting the dependency to:
implementation 'androidx.constraintlayout:constraintlayout:2.1.0-alpha2'
The full changelog is available here: https://androidstudio.googleblog.com/2020/12/constraintlayout-210-alpha-2.html which includes:
ConstraintLayout
- supports negative margins for constraints
This means that now you can do things such as android:layout_marginTop="-25dp"
, which you couldn't do before!
How to achieve overlap/negative margin on Constraint Layout?
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>
How to use same technique of css negative margin in a layout?
Here you go:
<?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"
tools:layout_editor_absoluteY="81dp">
<TextView
android:id="@+id/txt_home_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/box"
android:elegantTextHeight="true"
android:padding="30dp"
android:text="Welcome!"
android:textColor="#ffffff"
android:textSize="28sp"
tools:layout_editor_absoluteX="129dp"
tools:layout_editor_absoluteY="0dp" />
<android.support.v4.widget.Space
android:id="@+id/space"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toBottomOf="@+id/txt_home_title"
app:layout_constraintLeft_toLeftOf="@id/txt_home_title"
app:layout_constraintRight_toRightOf="@id/txt_home_title" />
<ImageView
android:id="@+id/home_logo"
android:layout_width="98dp"
android:layout_height="84dp"
android:scaleType="fitCenter"
android:src="@mipmap/ic_launcher"
app:layout_constraintHorizontal_bias="0.687"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/space" />
</android.support.constraint.ConstraintLayout>
Output:
Explanation:
We can not use negative margin in ConstraintLayout
as it is not officially supported so we need to use a workaround. Add an empty view i.e. Space
between ImageView
and TextView
and set android:layout_marginBottom="32dp"
to Space
view and you are done.
I followed this answer of CommonsWare.
Is it bad practice to use Negative Margins or Padding in CSS
No; it's not bad practice, so long as you're aware of the fact you're using negative margins, and that this necessarily 'pulls'/'moves' elements from their otherwise-'normal' position.
Why would you even worry about this?
Questions regarding margins in constraint layout
Because you have to set the android:layout_width
and android:layout_height
to match_constraint
or the special 0dp
value inside the layout.xml
file. Otherwise looks like you're using wrap_content
so it's respecting the constraints by adapting the view to its size.
How many different unit types can you use for setMargins()
most often case in Android when setting any dimensions: pixels are used. also setMargins
get px values, not dps or any other (according to doc). so if you have some number in different unit then you need to convert it to pixels
for re-calculating some value in given unit use TypedValue
class, e.g. like below:
int _10dpInPixelUnit = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10,
getResources().getDisplayMetrics());
if you want to use inches just use TypedValue.COMPLEX_UNIT_IN
(some doc and other units HERE)
why can't I take any object outside the screen
As stated in some answers under this question, firstly - using negative margin values is a bad practice and you should avoid this approach and secondly, you can set negative values to translation
with the same result
Related Topics
Android Facebook Sdk 4 in Eclipse
Android Sample Bluetooth Code to Send a Simple String via Bluetooth
Error Android Emulator Gets Killed in Android Studio
Best Method to Download Image from Url in Android
Android 6.0 Marshmallow. Cannot Write to Sd Card
Error Opening Supportmapfragment for Second Time
Android Studio 0.4 Duplicate Files Copied in APK Meta-Inf/License.Txt
How to Build an Executable for Android Shell
Which Is the Best Way to Add a Button
Web Colors in an Android Color Xml Resource File
How to Launch Activity Only Once When App Is Opened for First Time
Defining Z Order of Views of Relativelayout in Android
Execute Asynctask Several Times
How to Join Two SQLite Tables in My Android Application