Guide to ConstraintLayout

Loutry
16 min readNov 24, 2016

The other day I used the latest and still pretty new ConstraintLayout and wanted to share with you what I learn.

Here, I won’t walk you through the editor, blueprint and stuff like this, talk about performance or why you should make this shift. Instead I will guide you on how to use ConstraintLayout in comparison of a RelativeLayout or LinearLayout, what you should look for and what are the new awesome features, with a lot of examples.

I will also add that from my experience, ConstraintLayout worked perfectly fine and as intended. However I experienced some troubles with the editor so no big deal, it’s already all set for production.

Artwork inspired by Jory Raphael work: https://dribbble.com/sensibleworld

I don’t know for you, but when I see an app design, to convert it into an Android layout, I start by identifying where I’m going to put some RelativeLayout and what should be align to what.

The good news is that ConstraintLayout is all about constraining your view to stuff and a big part of the job of a RelativeLayout is to constraint views to other views.

Here I listed all the RelativeLayout attributes that you may use in your everyday life, and that I’m going to show you how to transpose:

android:layout_alignStart="@id/view"
android:layout_alignLeft="@id/view"
android:layout_alignEnd="@id/view"
android:layout_alignRight="@id/view"
android:layout_alignTop="@id/view"
android:layout_alignBaseline="@id/view"
android:layout_alignBottom="@id/view"

android:layout_toStartOf="@id/view"
android:layout_toLeftOf="@id/view"
android:layout_toEndOf="@id/view"
android:layout_toRightOf="@id/view"
android:layout_above="@id/view"
android:layout_below="@id/view"

android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"

android:layout_centerInParent="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"

First thing first, if you have a min SDK lower than 17, you can now shift to using only start and end attribute and don’t repeat yourself for right and left \o/

The first layout_align* attributes are used to align the same side of two views to each other, in ConstraintLayout you can replace them with:

app:layout_constraintStart_toStartOf="@id/view"
app:layout_constraintLeft_toLeftOf="@id/view"
app:layout_constraintEnd_toEndOf="@id/view"
app:layout_constraintRight_toRightOf="@id/view"
app:layout_constraintTop_toTopOf="@id/view"
app:layout_constraintBaseline_toBaselineOf="@id/view"
app:layout_constraintBottom_toBottomOf="@id/view"

The layout_to* family attributes are use to put two views side by side. Again, we can easily get this result by constraining a side of our view to the opposite side of another one:

app:layout_constraintStart_toEndOf="@id/view"
app:layout_constraintLeft_toRightOf="@id/view"
app:layout_constraintEnd_toStartOf="@id/view"
app:layout_constraintRight_toLeftOf="@id/view"
app:layout_constraintTop_toBottomOf="@id/view"
app:layout_constraintBottom_toTopOf="@id/view"

Before continuing to the next attributes pack, I didn’t tell you a HUGE thing about ConstraintLayout: match_parent is no more. Instead we have match_constraint that you use by using 0 dp — a little bit as what we were doing when we were assigning a size of 0 dp to a LinearLayout and let him handle the size. You can still use wrap_content or give a dimension to your view.

That being said, You can still get the match_parent result by constraining your view to…it’s parent :) Yes, they provide us with a nice keyword:

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
… />

I’m sure now you can guess how to mimic the layout_alignParent* attributes; simply by using the transposed layout_alignTo* with a value to parent:

app:layout_constraintStart_toStartOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"

Before moving on the last layout_center* attributes, I wanted to stop and remind you that the goal of ConstraintLayout is actually to place some views on an x and y axis and distribute the available space between them. Until now I only talk about a constraint for a specific side. In real life, chance is that you may need to constraint your view on both sides of a given axis and will combine those attributes.

So when the layout will be ready to do its job, it will compute all the constraint attributes you gave him and try his best to place your view. That means that if your view has a fixed size or is defined as wrap_content and you provide two constraints for the same axis, they would be used as endpoints and the layout will by default center the view on the available space. Still lost? Don’t worry, here is an example of how we center a view in its parent by constraining every view’s side to it:

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
… />
Centered view in parent

So yeah, ConstraintLayout has centered the view in its parent, the constraint endpoints, as we wanted.

And here is the best, ConstraintLayout lets you go further by providing a new attribute: bias. A bias is a percent (a float value between 0 and 1) and lets you choose how the view will be placed between its constraints on an axis. As I said, by default the view is centered which corresponds to a 0.5 bias.

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="1"
… />

Cool, no? Well it gets better, you can not also constraint the position of your view but also its width or height with a ratio. This means that if one side of your view has a fixed size or a wrap_content, the other size will respect the ratio that you would have specified as width:height. Here is an example of a 4:3 TextView.

<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="4:3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
… />
4:3 view

You can also use it when both sides of your view are set to match_constraint, but then you must specify which side will be computed as a ratio of the other one by adding W or H before the ratio. Let me do a demo of this by displaying a square as big as possible that fits its parent in portrait mode:

<View
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H, 1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
… />

What happens here is that to get a view to fit its parent, logically, the dimension of that square will be the smaller side of its parent, aka the width in portrait mode. We did that by constraining our view on its x axis to be aligned to the start and end of its parent in match_constraint mode. The goal is to get a square, a 1:1 ratio, we did this by specifying that the ratio is actually a constraint for the height “H, 1:1”.

In the previous case ConstraintLayout could deduce which side to constraint as one side wasn’t set to match_constraint and had a fixed / wrapped content thus the H or W value in front of the ratio wasn’t needed.

Biggest square possible in portrait mode

The Android developer team has also added Guidelines to your arsenal. Those “virtual helpers” are anchors that won’t be displayed in your app, they are like one line of a grid above your layout and can be used to attach or constraint your widgets to it. They are only visible on your blueprint or preview editor.

Now those freebies are designed for a very specific task so you have only two possibilities for their attributes other than their size (wrap_content). The first one is their orientation, horizontal or vertical, and the second one is the position where they will be anchored, you can either provide a dimension or a percent.

<android.support.constraint.Guideline
android:id="@+id/guideline1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintGuide_begin="16dp"
android:orientation="vertical" />
<android.support.constraint.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintGuide_percent="0.75"
android:orientation="horizontal" />

You actually can’t constraint a Guideline in any other way, thus the impossibility to put a Guideline to a delta of X dp to the bottom of a view other than its parent.

̶A̶ ̶n̶i̶c̶e̶ ̶t̶r̶i̶c̶k̶ ̶i̶s̶ ̶h̶o̶w̶e̶v̶e̶r̶ ̶t̶o̶ ̶u̶s̶e̶ ̶i̶t̶ ̶t̶o̶ ̶e̶m̶u̶l̶a̶t̶e̶ ̶a̶ ̶m̶a̶x̶ ̶o̶r̶ ̶m̶i̶n̶ ̶s̶i̶z̶e̶ ̶f̶o̶r̶ ̶a̶ ̶w̶i̶d̶g̶e̶t̶.̶

<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintGuide_begin="192dp"
android:orientation="vertical"/>

<TextView
android:id="@+id/max_width_demo_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintRight_toLeftOf="@id/guideline"
app:layout_constraintTop_toTopOf="parent"
android:padding="8dp"
android:text="Text emulating maxWidth of 192dp" />


<TextView
android:id="@+id/min_width_demo_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toRightOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/max_width_demo_view"
android:padding="8dp"
android:text="Text emulating minWidth of 192dp" />
Trick to get a max and min size

̶A̶s̶ ̶y̶o̶u̶ ̶c̶a̶n̶ ̶s̶e̶e̶,̶ ̶i̶f̶ ̶a̶ ̶v̶i̶e̶w̶ ̶h̶a̶s̶ ̶t̶w̶o̶ ̶c̶o̶n̶s̶t̶r̶a̶i̶n̶t̶s̶ ̶o̶n̶ ̶a̶ ̶s̶a̶m̶e̶ ̶g̶i̶v̶e̶n̶ ̶s̶i̶d̶e̶,̶ ̶i̶t̶ ̶w̶o̶u̶l̶d̶ ̶k̶e̶e̶p̶ ̶t̶h̶e̶ ̶s̶m̶a̶l̶l̶e̶r̶ ̶o̶n̶e̶ ̶a̶k̶a̶ ̶t̶h̶e̶ ̶“̶l̶e̶f̶t̶e̶s̶t̶”̶ ̶o̶r̶ ̶“̶t̶o̶p̶e̶s̶t̶”̶.̶ ̶S̶o̶ ̶t̶o̶ ̶g̶e̶t̶ ̶a̶ ̶m̶a̶x̶W̶i̶d̶t̶h̶ ̶b̶e̶h̶a̶v̶i̶o̶r̶,̶ ̶a̶ ̶v̶i̶e̶w̶ ̶n̶o̶t̶ ̶b̶i̶g̶g̶e̶r̶ ̶t̶h̶a̶n̶ ̶a̶ ̶g̶i̶v̶e̶n̶ ̶v̶a̶l̶u̶e̶,̶ ̶w̶e̶ ̶d̶o̶u̶b̶l̶e̶ ̶c̶o̶n̶s̶t̶r̶a̶i̶n̶t̶ ̶i̶t̶ ̶o̶n̶ ̶i̶t̶s̶ ̶r̶i̶g̶h̶t̶ ̶s̶i̶d̶e̶.̶ ̶T̶h̶e̶ ̶l̶a̶y̶o̶u̶t̶ ̶t̶h̶e̶n̶ ̶k̶e̶e̶p̶s̶ ̶t̶h̶e̶ ̶s̶m̶a̶l̶l̶e̶s̶t̶ ̶v̶a̶l̶u̶e̶ ̶f̶o̶r̶ ̶t̶h̶e̶ ̶r̶i̶g̶h̶t̶ ̶c̶o̶n̶s̶t̶r̶a̶i̶n̶t̶ ̶a̶n̶d̶ ̶w̶e̶ ̶g̶e̶t̶ ̶a̶ ̶v̶i̶e̶w̶ ̶t̶h̶a̶t̶ ̶d̶o̶e̶s̶n̶’̶t̶ ̶t̶a̶k̶e̶ ̶a̶ ̶b̶i̶g̶g̶e̶r̶ ̶s̶i̶z̶e̶ ̶t̶h̶a̶n̶ ̶t̶h̶e̶ ̶v̶a̶l̶u̶e̶ ̶p̶r̶o̶v̶i̶d̶e̶d̶ ̶b̶y̶ ̶t̶h̶e̶ ̶g̶u̶i̶d̶e̶l̶i̶n̶e̶.̶ ̶F̶o̶r̶ ̶t̶h̶e̶ ̶m̶i̶n̶W̶i̶d̶t̶h̶ ̶b̶e̶h̶a̶v̶i̶o̶r̶ ̶w̶e̶ ̶c̶o̶n̶s̶t̶r̶a̶i̶n̶t̶ ̶t̶h̶e̶ ̶v̶i̶e̶w̶ ̶w̶i̶t̶h̶ ̶t̶h̶e̶ ̶g̶u̶i̶d̶e̶l̶i̶n̶e̶ ̶o̶n̶ ̶t̶h̶e̶ ̶o̶t̶h̶e̶r̶ ̶s̶i̶d̶e̶ ̶t̶h̶u̶s̶ ̶g̶e̶t̶t̶i̶n̶g̶ ̶a̶ ̶b̶i̶g̶g̶e̶r̶ ̶v̶i̶e̶w̶.̶

Edit: As Nicolas Roard brought forward in his comment, this behavior was only the result of the constraint order in which they were checked at computation. So bottom line, don’t use two constraints for the same side of your view, it’s not ok :p

I will also add that if you want a min or max size, no need to use any trick; min size is actually an attribute of View and max size is available on a lot of widget such as ImageView, TextView, Button, CheckBox, WebView, etc. so simply use them without any constraint.

To replace the previous example here is what Nicolas explained in its comment: a view which is anchored on both sides to the same anchor, thus being aligned to this anchor.

<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3"
tools:layout_editor_absoluteY="0dp"
tools:layout_editor_absoluteX="106dp" />

<TextView
android:id="@+id/label_abcde"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintRight_toLeftOf="@+id/guideline"
app:layout_constraintLeft_toLeftOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent"
android:padding="16dp"
android:text="ABCDE" />

<TextView
android:id="@+id/label_f"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/label_abcde"
app:layout_constraintRight_toRightOf="@+id/label_abcde"
app:layout_constraintLeft_toRightOf="@+id/label_abcde"
android:padding="16dp"
android:text="F" />
ABCDE is aligned to a 0.3 vertical guideline and F is aligned to ABCDE’s right anchor

We anchored ABCDE left and right side to a 0.3 guideline. The ConstraintLayout then centered the middle of the view to the anchor point: the guideline. On F view we proceed again to anchor the left and right side but to the right side of our previous view this time and get a view centered to the right side of ABCDE.

We are now entering the second part of this article: how to use your ConstraintLayout as a LinearLayout and beyond.

The simplest and the most basic use of a LinearLayout is to put view with a fixed size side by side on an x or y axis. Let’s say you want to achieve a “LinearLayout like” in horizontal mode, this is how you will set your views on the x axis:

<TextView
android:id="@+id/label_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="A" />

<TextView
android:id="@+id/label_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_a"
app:layout_constraintBaseline_toBaselineOf="@id/label_a"
android:text="B" />

<TextView
android:id="@+id/label_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_b"
app:layout_constraintBaseline_toBaselineOf="@id/label_b"
android:text="C" />

We simply align our first view to the start of its parent and only constraint the start and not its end. This is really important, if you add an end constraint linked to your next view you will do something else. We however could have constrained it to another view that we wouldn’t want in our “wrap”. We then put the next view to the end of our first one by constraining its start to the end of the first one. You can go on if you want more views in your LinearLayout.

Be aware that this solution isn’t like wrapping widgets inside a LinearLayout and you won’t have all the handling to prevent views to be displayed outside the “wrapper” limits. Keep reading for a foolproof basic LinearLayout behavior like. ;)

For the more “advanced” use of LinearLayout, we’re gonna need the wonderful chain mechanism of ConstraintLayout.

What the documentation says about chain:
‣ Chains provide group-like behavior in a single axis (horizontally or vertically). The other axis can be constrained independently.
‣ A set of widgets are considered a chain if they are linked together via a bi-directional connection.
‣ Chains are controlled by attributes set on the first element of the chain (the “head” of the chain) which is the left-most widget for horizontal chains, and the top-most widget for vertical chains.

So in other words, a chain is a wagon of widgets referring each other as constraints. This mechanism will assist you to distribute those widgets in different fashion between two points defined by the left or top constraint of the head of the chain and the right or bottom constraint of the last element.

Note that it’s really important that you set those two constraints on the extremity of the chain, otherwise those widgets will be interpreted as a chain by the ConstraintLayout but the layout won’t know how he should distribute them. So it will either approximately display all the widgets of the chain as a single stack on the same point or you could also see only one big widget depending on what you provided. In any case it won’t do anything that you want.

That being said, chances are that chains will soon become your next best friend ♥ Here is an illustration from the documentation of what you can achieve with them:

So I’m gonna start from top to bottom and this time, we will talk about how we used to achieve this effect with a LinearLayout and how you can now do it with ConstraintLayout like a boss.

I don’t know for you, but the Spread and Spread Inside Chain style are a miracle, like if you wanted to achieve this result with a LinearLayout you would need to first wrap all those widgets in one LinearLayout that would be in charge of evenly distributing its child. Then you would need to wrap again each of your widgets in a FrameLayout with a 0 dp size because you wanted to keep your widget’s size to wrap_content or to a fixed size and couldn’t enjoy the evenly distribution mechanism of the LinearLayout on them. Two layouts wrapper to achieve it… Crazy.

Now, all you need to do is refer all your widgets to each other and set their extremity constraint. And. You’re done! The spread chain is the default one so you don’t need to specify anything, but if you want the spread inside chain style, you will set it on the head chain.

<TextView
android:id="@+id/label_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/label_b"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_chainStyle="spread_inside"
android:text="A" />

<TextView
android:id="@+id/label_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_a"
app:layout_constraintEnd_toStartOf="@+id/label_c"
app:layout_constraintTop_toTopOf="parent"
android:text="B" />

<TextView
android:id="@+id/label_cde"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_b"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="CDE" />

Note that a bias attribute on the same axis that a spread* chain is irrelevant, you will need to correctly set your start and end constraints.

Isn’t it super nice? :)

The first time I read about Weighted Chain I actually wanted to know where this style was hidden. Well, it turns out that a Weighted Chain is a spread* chain with at least one widget set to 0 dp/match_constraint.

Which retrospectively seems logical, if your widgets take all the space they won’t be “spread” anymore. And yeah, I didn’t read the one line that document it just above the picture, my bad.

If there is only one widget with a 0 dp size, it will take as much space as possible of the space distribution with no regards about the weight attribute if it has one.

If there is more than one widget with a 0 dp size but no weight attribute, the layout will evenly distribute all the remaining space to those widgets-same as a LinearLayout.

However if their is more than one widget with 0 dp size in the chain and at least one of them have a weight constraint, then all of them should have one too, otherwise 0 dp will be the actual size of the widget and it won’t be displayed.

<TextView
android:id="@+id/label_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/label_2"
app:layout_constraintTop_toTopOf="parent"
android:text="1" />

<TextView
android:id="@+id/label_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_1"
app:layout_constraintEnd_toStartOf="@+id/label_3"
app:layout_constraintBaseline_toBaselineOf="@id/label_1"
android:text="2" />

<TextView
android:id="@+id/label_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBaseline_toBaselineOf="@id/label_1"
android:text="3" />
Three views in a weighted chain
<TextView
android:id="@+id/label_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/label_b"
app:layout_constraintTop_toTopOf="parent"
android:text="A" />

<TextView
android:id="@+id/label_b"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_a"
app:layout_constraintEnd_toStartOf="@+id/label_c"
app:layout_constraintBaseline_toBaselineOf="@id/label_a"
app:layout_constraintHorizontal_weight="2"
android:text="B" />

<TextView
android:id="@+id/label_c"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_b"
app:layout_constraintEnd_toStartOf="@+id/label_d"
app:layout_constraintBaseline_toBaselineOf="@id/label_a"
app:layout_constraintHorizontal_weight="4"
android:text="C" />

<TextView
android:id="@+id/label_d"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@+id/label_c"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBaseline_toBaselineOf="@id/label_a"
android:text="D" />
Weighted chain — here D view isn’t displayed because it is set to match_constraint and his buddies have a weight attribute and not him.

Another thing that we were used to do with a LinearLayout was to wrap some widgets with a fixed size to group them together and then center it in another view.

You guess it, this is what does the last chain style: packed.

This one can actually do even more than just center the chain block content, you can also add a bias constraint on the head chain that will shift the position of the the packed chain.

<TextView
android:id="@+id/label_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/label_b"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="0.3"
android:text="A" />

<TextView
android:id="@+id/label_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_a"
app:layout_constraintEnd_toStartOf="@+id/label_c"
app:layout_constraintTop_toTopOf="parent"
android:text="B" />

<TextView
android:id="@+id/label_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_b"
app:layout_constraintEnd_toStartOf="@+id/label_d"
app:layout_constraintTop_toTopOf="parent"
android:text="C" />

<TextView
android:id="@+id/label_d"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@+id/label_c"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="D" />
Packed chain with bias

Note that this chain style can’t achieve a weighted chain. You should also be vigilant not to switch those styles. You can’t, if when you need to place some widgets you have no way of defining it without considering the all widgets block, then sure, use a packed style, otherwise use spread* style with the last view set to match_constraint.

Here we’re gonna imitate a LinearLayout with this approach to display some widgets under another one. In a first time we could think about doing it with a packed chain style with a 0 bias instead of going with a spread* chain because all of our views was set to a fixed size or wrap_content and we didn’t want them to be spread but put next to each other.

However when you’re dealing with ConstraintLayout, you need to think about some extreme cases of your display and guess what is the constraint you need and how. So don’t forget, it’s not because everything looks fine that it’s actually doing what you wanted.

Here, let’s suppose we got some widgets that can’t fit the allocated space, how do you want them to behave? And how do our two approaches look like?

Packed and spread* chains aren’t that swappable

The left part is the result of the packed style, whereas the right one is the one with the spread style.

Hope you can more clearly distinguish the two chains concept now and use the right one when the time will come :)

This is the last main part of this article and what may be the most confusing if you’re trying to make the shift to ConstraintLayout: margins. Indeed, Margins are now interpreted as constraints on their own and can’t be negative.

A margin is a constraint used to shift the position of the view from its other constraints and it is taken into consideration as belonging to the view when the layout do his distribution job. Again, it can’t be negative.

Here I’m gonna quote the documentation:
“If side margins are set, they will be applied to the corresponding constraints (if they exist), enforcing the margin as a space between the target and the source side.” Emphasis to the “if they exist”.

Here are some examples.

If two views, not in chain, have margins set along one of their mutual side, then only the margin of the view (B), constraint to the other view (A), will be “used”. In fact B would have no idea that A has a margin and won’t be constrained by it, it will only be constrained by its margin attribute specified on its constrained side, thus the overlaps.

First column: A has a 16dp margin and B a 8dp margin, A is constrained to the parent top and B top to the bottom of A. The other columns are failed chains.

The layout has taken into consideration A’s margin when it aligned the TextView on the top start of its parent. However, as you can notice, B is stuck to A and doesn’t “respect” A margin but only its own margin. When we constraint a view to another one, we actually constraint it to its content and its margin isn’t really part of it anymore, it is an attribute dedicated to constraint information.

<TextView
android:id="@+id/label_a"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/label_c"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/label_b"
android:background="@color/colorAccent"
android:text="A" />

<TextView
android:id="@+id/label_b"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/label_d"
app:layout_constraintTop_toBottomOf="@id/label_a"
android:background="@color/colorAccent"
android:text="B" />

If the other three columns look funny to you and you can’t actually read the view on the “first line”, it’s because we actually didn’t set the top and/or bottom chain endpoints. Just don’t do it, you will end up with that kind of crazy results. Either remove a side of the cross constraint reference or set both endpoints and turn it into a “working” chain.

<TextView
android:id="@+id/label_c"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_a"
app:layout_constraintEnd_toStartOf="@+id/label_e"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/label_d"
android:background="@color/colorAccent"
android:text="C" />

<TextView
android:id="@+id/label_d"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@id/label_b"
app:layout_constraintEnd_toStartOf="@+id/label_f"
app:layout_constraintTop_toBottomOf="@id/label_c"
android:layout_margin="16dp"
android:background="@color/colorAccent"
android:text="D" />
<TextView
android:id="@+id/label_e"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_c"
app:layout_constraintEnd_toEndOf="@+id/label_g"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/label_f"
android:layout_margin="16dp"
android:background="@color/colorAccent"
android:text="E" />

<TextView
android:id="@+id/label_f"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@+id/label_d"
app:layout_constraintEnd_toStartOf="@+id/label_h"
app:layout_constraintTop_toBottomOf="@id/label_e"
android:background="@color/colorAccent"
android:text="F" />
<TextView
android:id="@+id/label_g"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/label_e"
app:layout_constraintEnd_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/label_h"
android:layout_margin="24dp"
android:background="@color/colorAccent"
android:text="G" />

<TextView
android:id="@+id/label_h"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@+id/label_f"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/label_g"
android:layout_margin="16dp"
android:background="@color/colorAccent"
android:text="H" />

Let’s now talk about margin in case of a chain with free space (no match_constraint and not enough elements to fill all the given space), the margin will then be taken into consideration as part of the view content when the layout will compute the view size and divide the remaining space between the various views of the chains.

Here are some examples.

Four spread chains with margins on some of their views. The blue line delimits the constraint layout padding. The other line of the same color has the same length.
Four spread inside chains with margins on some of their views.
Four packed chains with margins on some of their views.
Three views in a weighted chain: B has a 16dp margin and take with its margin the same amount of space on x than C. A is wrapped to its content.

Let’s now consider chains are overflowed.

First row: overflowed spread chain. Second row: overflowded packed chain. All views have the same fixed size and a margin of 16dp.

As you can see on the first row, when a spread* chain is too “full” then the widgets are gonna overlap each other to fit between the two endpoint constraints. Thus, the margin constraints can overlap.

On the second line, the packed chain has managed differently its excessive amount of views. It gave the priority to keep the views block intact, so the views are not overlapping, neither the margin. However the overflow is compensated by the end constraint of the chain.

Note that this behavior may actually highlight something wrong in your chains. If you visually have a suspicious margin overlap, maybe your chain wasn’t correctly constrained.

There is also some new attributes available to specify another margin when a targeted view constraint is gone.

android:layout_goneMarginStart="@dimen/my_other_margin"
android:layout_goneMarginEnd="@dimen/my_other_margin"
android:layout_goneMarginLeft="@dimen/my_other_margin"
android:layout_goneMarginTop="@dimen/my_other_margin"
android:layout_goneMarginRight="@dimen/my_other_margin"
android:layout_goneMarginBottom="@dimen/my_other_margin"

If you still think this new margin “behavior” will confuse you tomorrow, my advise to you is simply: don’t use them. You can probably replace your “old” margin by a padding and if you really needed the distinction, it may be for a background purpose, so just add another view with it and you’re done. Yes, it will be redundant and so error prone, but you won’t be confused and won’t be sad for not successfully using this magical layout :)

To conclude I want you to leave with a small cheat-sheet of what to do if something doesn’t work as you intended:
‣ check that you didn’t misspell the second part of your layout_constraint*_to*Of attributes, it can happen quite easily.
‣ check that you correctly constrained the head and the last element of your chains.
‣ chain style and bias are defined on the head chain, except weight constraint which is specific to a widget.
‣ don’t try to add bias constraint on a spread* chain.
‣ don’t try to add weight constraint on a packed chain.
‣ watch out for margin behavior.
‣ don’t try to add on a guideline another constraint than: layout_constraintGuide_* or its orientation, it won’t work.
‣ say out loud what you’re trying to achieve, putting the word “constraint” as often as possible, it may help you understand what you want to achieve :)

--

--

Loutry

Android Craftswoman ♥ You can meet me IRL at the Paris Android User Group :)