ConstraintLayout
Why ConstraintLayout?
ConstraintLayout positions every view with constraints — connections to the parent or to other views. You can build complex screens with a flat hierarchy (no nesting), which is faster and adapts to every screen size. It is the default layout in new Android Studio projects.
Nested LinearLayouts
- LinearLayout
- └ LinearLayout
- └ LinearLayout
- slow measure passes
One ConstraintLayout
- ConstraintLayout
- ├ ImageView
- ├ TextView ×2
- └ Button — flat & fast
The 2 rules of constraints
- Every view needs at least one horizontal (start/end) and one vertical (top/bottom) constraint.
- A constraint connects a side of this view to a side of the parent or of another view:
app:layout_constraintStart_toEndOf="@id/ivAvatar"= "my start sits at the avatar's end".
Example: centre a view
<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/tvHello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Centred!"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>Constraints on both opposite sides pull like springs — equal pull keeps the view in the centre.
Example: profile card
<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="wrap_content"
android:padding="16dp">
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/ic_person"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="Riya Sharma"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/ivAvatar"
app:layout_constraintEnd_toStartOf="@id/btnFollow"
app:layout_constraintTop_toTopOf="@id/ivAvatar" />
<TextView
android:id="@+id/tvClass"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Class 12 · Science · Aligarh"
app:layout_constraintStart_toStartOf="@id/tvName"
app:layout_constraintEnd_toEndOf="@id/tvName"
app:layout_constraintTop_toBottomOf="@id/tvName" />
<Button
android:id="@+id/btnFollow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Follow"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/ivAvatar"
app:layout_constraintBottom_toBottomOf="@id/ivAvatar" />
</androidx.constraintlayout.widget.ConstraintLayout>0dp = match constraints
Width 0dp means "stretch between my start and end constraints". That is why tvName fills the space between the avatar and the button, and long names wrap instead of overlapping the button.
Bias
<!-- constrained to both sides, but placed at 30% from the left -->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="30%"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintTop_toTopOf="parent" />Chains
<!-- three tabs spread evenly: each view is linked to its neighbours -->
<Button
android:id="@+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Day"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/b2"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Week"
app:layout_constraintStart_toEndOf="@id/b1"
app:layout_constraintEnd_toStartOf="@id/b3"
app:layout_constraintTop_toTopOf="@id/b1" />
<Button
android:id="@+id/b3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Month"
app:layout_constraintStart_toEndOf="@id/b2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/b1" />| chainStyle | Result |
|---|---|
| spread (default) | | A B C | equal space around |
| spread_inside | |A B C| ends stick to edges |
| packed | | ABC | grouped in the middle |
Guidelines, barriers and ratio
<!-- a vertical guideline at 40% of the width -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guide40"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.4" />
<!-- 16:9 banner that always fills the width -->
<ImageView
android:id="@+id/ivBanner"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- barrier: sits after whichever label is wider -->
<androidx.constraintlayout.widget.Barrier
android:id="@+id/labelBarrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="tvNameLabel,tvEmailLabel" />Using the Layout Editor
- 1Open the layout → Design view
- 2Drag a Button from the Palette onto the screen
- 3Drag the small circles on its sides to the parent edges or other views
- 4Set width to "match constraint" (0dp) in the Attributes panel when it should stretch
- 5Click the magic wand "Infer constraints" only as a start — then check every view
Common mistakes
- View without vertical constraint → jumps to the top at runtime.
match_parentinside ConstraintLayout → use0dp+ both side constraints.- Constraining to a view that is
gone→ uselayout_goneMarginStartor a barrier. - Circular constraints (A depends on B, B on A) without a chain → unpredictable layout.
Summary
- Each view: one horizontal + one vertical constraint (minimum).
0dpstretches between constraints; bias moves it along.- Chains share space; guidelines, barriers and ratios handle advanced cases.
Build a product card with ConstraintLayout only: a 16:9 image on top, title and price under it, an "Add to cart" button on the right of the price, and a "★ 4.5" text aligned to the title's baseline.
