Some Common ConstraintLayout Pitfalls and Mistakes

|

Recently I have been working a lot with converting nested layouts, into flattened layouts using ConstraintLayout as their root element.

If you do not know what ConstraintLayout is all about, in short terms, it is a layout that allows to position and size views in a flexible manner. It is in some ways similar to RelativeLayout. However, it gives you more power by allowing you to use percentages for sizes, chaining views together and much more.

Since I have been working with it quite a bit, I would try to highlight some common mistakes that people make when using ConstraintLayout. Also, help you avoid some pitfalls you can encounter on your. Hopefully, this will help you improve your own layouts.

Do not use match_parent for widths and heights

When you use ConstraintLayout, you are constraining your view dimensions using rules. These rules are a family of attributes that you apply on your view, starting with layout_constraint. There are various constraints, such as:

layout_constraintTop_toTopOf
layout_constraintStart_toStartOf

A common mistake is to use match_parent for layout_width and layout_height. However, what you instead should use is 0dp which means *match contstraints*, because ConstraintLayout the entire idea about this layout is that you apply constraints to size and position your views.

Instead of match_parent for width, you would instead use the following two rules:

app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"

Where parent is the encapsulating view, in this case it would be the ConstraintLayout. However, instead of writing parent it could be the ID of another view in the layout.

Now, match_parent will work and do what it does in other layouts. However, on some older versions of ConstraintsLayout it is not recommended to use it, since it is not entirely supported. Make sure you are using version 1.1 or newer.

What about wrap_content?

wrap_content is still valid for layout_width and layout_height and will be respected by ConstraintLayout.

A thing to note though. If you use wrap_content and for instance the two rules, from the match_parent example above like:

<View
    android:layout_width="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    ...

The view will not match the entire width of the parent. What you instead are describing here is that the view wraps its width, but should be positioned between the start and end of the parent. This is useful with couple of other attribute called layout_constraintHorizontal_bias and layout_constraintVertical_bias.

Let us say you want to postion a view at a third of the screens width. You would apply the following constraints like so:

<View
    android:layout_width="wrap_content"
    app:layout_constraintHorizontal_bias="0.3"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    ...

Pretty neat!

Broken chains

ConstraintLayout allows you to create a chain between multiple views. This can help you with things like:

  • Equally spreading chained views
+                                                      +
|    +----------+     +----------+     +----------+    |
|    |          |     |          |     |          |    |
|    |          +----->          +----->          |    |
<----+  View A  |     |  View B  |     |  View C  +---->
|    |          <-----+          <-----+          |    |
|    |          |     |          |     |          |    |
|    +----------+     +----------+     +----------+    |
+                                                      +

For this effect you need to apply layout_constraintHorizontal_chainStyle="spread" or layout_constraintHorizontal_chainStyle="spread_inside" on View A. The latter will not add spacing at the start of View A and at the end of View C. There is also a Vertical variant of this chain style.

  • Weigh views similarly to what LinearLayout can do
+                                   +
|+----------++---------------------+|
||          ||                     ||
||  Weight  +>        Weight       ||
<+   0.25   ||         0.75        +>
||          <+                     ||
||          ||                     ||
|+----------++---------------------+|
+                                   +

Apply layout_constraintHorizontal_weight or layout_constraintVertical_weight on all elements in the chain to add weights.

  • Packing together views
+                                                    +
|        +----------++----------++----------+        |
|        |          ||          ||          |        |
|        |          +>          +>          |        |
<--------+  View A  ||  View B  ||  View C  +-------->
|        |          <+          <+          |        |
|        |          ||          ||          |        |
|        +----------++----------++----------+        |
+                                                    +

Some of the variants can have bias applied to them to increase or decrease the effects on elements. Say you want to spread equally, except for the center view, which needs more space.

If you notice the small ascii drawings above, in between all of the views there is a directional arrow. In order to create a chain, you need to create constraints that position your views bi-directional. So if you have views A, B and C. A must have a constraint to a parent or another view and to B. In turn B must a constraint to A and to C. At last C must have a constraint to B and to another view or parent. If you forget to constraint bi-directinally, your chain is broken and the effect you want to achieve will not work.

So compared to RelativeLayout which does not allow to add circular positioning between views ConstraintLayout actually allows this in order to create these chains.

Setting Guideline percentages based on screen size and orientation

ConstraintLayout has a couple of virtual layouts, which are not visible on the screen at all. Guideline being one of the. Guidelines are useful for positioning start, end, top and bottom constraints on a view against it.

A guideline can be set at a fixed position using layout_constraintGuide_begin="100dp" which will position it at 100dp at the left or top of a view. Or you can alternatively use layout_constraintGuide_end="100dp", which will postion the Guideline at 100dp to the right or bottom of a view.

A guideline can also be positioned at a percentage of the width or height of the view with layout_constraintGuide_percent="0..1" where 0 is left, 0.5 is middle and 1 is right.

One cool thing is that you can use Android’s values folder structure to provide dimensions for different screen sizes for these guidelines. For instance on a phone you want to have a view fill most of the screen, while on a table you may want to to only fill half of the screen.

This is easy to set up for dimensions specified in dp. However, for the percentages, it is slightly different. Plain dimens on Android do not allow you to use floating point values.

Imagine you have a folder structure as follows:

Resources
    values
        dimens.xml
    values-sw600dp
        dimens.xml

You would put your normal phone resources into values/dimens.xml while you would put your tablet resources into values-sw600dp/dimens.xml.

Regular dimens for typed values like sp, dp, px can be added to a dimens.xml file like so:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <dimen name="guidelineBegin">16dp</dimen>
</resources>

This can be consumed for your Guideline like so:

<android.support.constraint.Guideline
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_begin="@dimen/guidelineBegin" />

The dimension added in values/dimens.xml will be substituted by the value in values-sw600dp/dimens.xml if the sw, smallest width is 600dp or more. There are other values folders you can use, which you can read more about in the Android App resources overview.

Dimensions which are float values, normally need to be followed by a typed value sp, dp, etc. In order to add percentages we can use for Guidelines, these need to be specified slightly differently like so:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <item name="fiftyPercent" type="dimen">0.5</item>
</resources>

These can still be consumed as normal dimensions like so:

<android.support.constraint.Guideline
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="@dimen/fiftyPercent" />

Should you use ConstraintLayout for all your layouts?

ConstraintLayout is very powerful and allows you to create complex layout, without a nested hell of views. Often it can be much more performant than a deeply nested layout. However, should you use it even for the simplest layouts? Obviously it really depends on the use case. However, it should be noted that all this power does not come for free. There is some overhead in calculating positions and sizes for views in your layout when using ConstraintLayout. Hence, in many cases using a flattened layout with RelativeLayout as root, may outperform ConstraintLayout. So, in the end it is a question of the complexity of the layout and how much of the power ConstraintLayout provides you want to utilize.

There are various benchmarks out there. Make sure the benchmark you read include both inflation-, layout- and measure-time. Also you may find that performance differs significantly between versions benchmarked as improvements find their way into ConstraintLayout over time. You can read more about the performance benefits on the Android Developers Blog.

Hopefully you found some of this information useful.