ConstraintLayout

ConstraintLayout is the layout Android recommends as the default container for most screens, and it's what TrailLog's own screens are built on. Instead of nesting several LinearLayouts inside each other to line things up, you describe where each view sits relative to other views or to the parent, and the system solves the whole screen's positions in one flat pass. This lesson builds up that idea with a full worked TrailLog example.

Why ConstraintLayout Is the Recommended Default

Before ConstraintLayout, a moderately complex screen often meant a LinearLayout containing another LinearLayout containing a RelativeLayout, and so on. Each nested layout adds another pass of measuring and positioning that the system has to work through before it can draw the screen. ConstraintLayout's core promise is a single, flat view hierarchy: all of a screen's views are direct children of one ConstraintLayout, with no nesting needed to achieve complex arrangements. Fewer layout levels genuinely means less work for the system on every measure and layout pass, which is why it's the recommended choice even for fairly simple screens.

The Core Idea: Constraints

A constraint ties one edge of a view to an edge of either the parent or another view. Every view placed in a ConstraintLayout needs enough constraints to pin its position — at minimum one horizontal and one vertical constraint, or it can end up snapped to the top-left corner. The most common constraint attributes look like this:

  • app:layout_constraintTop_toTopOf="parent" — pin this view's top edge to the parent's top edge.
  • app:layout_constraintTop_toBottomOf="@id/otherView" — pin this view's top edge just below another view's bottom edge.
  • app:layout_constraintStart_toStartOf="parent" — pin this view's start edge to the parent's start edge (start/end instead of left/right so it works correctly in right-to-left languages).
  • app:layout_constraintEnd_toEndOf="parent" — pin this view's end edge to the parent's end edge.

Worked Example: TrailLog's Trail Detail Screen

Here's the layout behind TrailDetailActivity: a photo at the top, the trail's name constrained just below it, and the distance constrained below the name.

<!-- res/layout/activity_trail_detail.xml -->
<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">

    <ImageView
        android:id="@+id/trailPhoto"
        android:contentDescription="Trail photo"
        android:scaleType="centerCrop"
        android:layout_width="0dp"
        android:layout_height="220dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/trailNameText"
        android:textStyle="bold"
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintTop_toBottomOf="@id/trailPhoto"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/trailDistanceText"
        android:textColor="#666666"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:layout_marginStart="16dp"
        app:layout_constraintTop_toBottomOf="@id/trailNameText"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Notice the chain of relationships: the photo is pinned to the parent's top and both sides; the name is pinned below the photo (toBottomOf="@id/trailPhoto"); and the distance is pinned below the name. Three views, three positions, and not one of them is nested inside another — that's the flat hierarchy ConstraintLayout is built around.

0dp: "Match Constraints" Sizing

Both trailPhoto and trailNameText above use android:layout_width="0dp". Inside a ConstraintLayout, 0dp has a special meaning distinct from wrap_content or match_parent: it tells the view to stretch to fill whatever space its constraints leave available. trailPhoto is constrained to both the parent's start and end, so with a width of 0dp it stretches edge-to-edge; wrap_content there would just size the view to its natural content size and ignore the space between the constraints, while match_parent would ignore the constraints entirely and size itself to the parent's full width regardless of what it's constrained to. In practice: reach for 0dp whenever a view should stretch between two constraints.

Chains: Distributing Several Views Evenly

When a row of views are all constrained to each other in sequence — the first to the second, the second to the third, and so on — ConstraintLayout recognizes that as a chain, and lets you control how the leftover space is distributed with app:layout_constraintHorizontal_chainStyle (or the vertical equivalent) on the first view in the chain. If TrailLog's detail screen had three stat chips — distance, date, and elevation — laid out in a row, setting the chain style to "spread" would space them out evenly across the width, "packed" would pull them together in the middle, and "spread_inside" would push the first and last chips to the outer edges while spacing the rest evenly between them.

Guidelines: An Invisible Alignment Reference

A Guideline is a line that exists purely for other views to constrain against — it's never drawn on screen. It's useful when several views need to line up at a position that isn't tied to any particular view, like "20% in from the left edge":

<androidx.constraintlayout.widget.Guideline
    android:id="@+id/startGuideline"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.20" />

Any view can then constrain to @id/startGuideline exactly as it would to any other view's edge. If TrailLog's detail screen later added a column of labels (Distance, Date, Notes), a single guideline keeps all of them aligned without hardcoding the same margin on every one.

Note: This layout XML is correct and matches how a real TrailLog project would be structured, but this website's own project doesn't run a live Android Studio build, so nothing here renders on the page itself — try it in a real project to see it laid out.

The Layout Editor: A Visual Alternative

Android Studio ships a visual Layout Editor that lets you drag views onto a canvas and draw constraints by dragging from one edge to another, rather than typing every app:layout_constraint... attribute by hand. It's a genuinely productive way to build screens day to day. That said, everything the Layout Editor produces is exactly the XML shown in this lesson — understanding that XML directly, which is this lesson's focus, is what lets you read, review, and fix a layout confidently regardless of which tool wrote it.

Up next: With one screen positioned, RecyclerView Basics covers how TrailLog displays a whole scrolling list of trails efficiently.