Achieving Negative Margin in ConstraintLayout
Mar 8, 2021
Historically, negative margins were not supported in ConstraintLayout. It is changing though—this feature has been added to ContraintLayout 2.1.0-alpha2 but not available in stable version yet at the time of this writing.
In the meantime, we can use a Space widget to accomplish the same task.
Space
The idea is to define a View of type Space with 0 height and/or width with a margin set to achieve the negative margin effect. For instance, see this UI:
Notice how the blue background below overlaps with the CardView above it. Note that this particular layout could have been implemented as a LinearLayout with negative margins but let’s learn how to do it with ConstraintLayout.
Below is the layout code—pay attention to the Space View, its margin and how the blue background below is anchored to that spacer.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainFragment">
<androidx.cardview.widget.CardView
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginEnd="48dp"
app:cardCornerRadius="8dp"
app:cardElevation="10dp"
app:cardUseCompatPadding="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="@+id/cardTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_title_text"
android:textSize="32sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/cardDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/card_description_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cardTitle" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<Space
android:id="@+id/bgSpacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="60dp"
app:layout_constraintBottom_toBottomOf="@id/card"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#EFF7F8"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bgSpacer">
<TextView
android:id="@+id/otherText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="60dp"
android:layout_marginEnd="16dp"
android:text="@string/other_text1"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/otherText2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/other_text2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/otherText1" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>Bonus: Adding an Arc
A quick way to take this layout to the next level visually is to add an arc shape to the blue background. This can be done by using a shape or an image to render an arc anchored to the top of the Space View.
Looks better, doesn’t it? And all we had to do to achieve this is this:
<View
android:layout_width="0dp"
android:layout_height="40dp"
android:background="@drawable/bg_arc"
app:layout_constraintBottom_toTopOf="@id/bgSpacer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Here is a full revised 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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainFragment">
<androidx.cardview.widget.CardView
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginEnd="48dp"
app:cardCornerRadius="8dp"
app:cardElevation="10dp"
app:cardUseCompatPadding="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="@+id/cardTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_title_text"
android:textSize="32sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/cardDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/card_description_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cardTitle" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<View
android:layout_width="0dp"
android:layout_height="40dp"
android:background="@drawable/bg_arc"
app:layout_constraintBottom_toTopOf="@id/bgSpacer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Space
android:id="@+id/bgSpacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="60dp"
app:layout_constraintBottom_toBottomOf="@id/card"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#EFF7F8"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bgSpacer">
<TextView
android:id="@+id/otherText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="60dp"
android:layout_marginEnd="16dp"
android:text="@string/other_text1"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/otherText2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/other_text2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/otherText1" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>Hope this example was helpful. Full source code is available in this repo https://github.com/jshvarts/ConstraintLayoutOverlappingViewsDemo