ConstraintLayout at Trainline

Greg McGowan
Trainline’s Blog
Published in
5 min readApr 26, 2019

At Trainline we have a large Android team and over the years have produced a large codebase. One of the biggest challenges in such an environment is maintaining a consistent approach to the various libraries and SDKs we use. There are several reasons for this

  1. The library may have scope for a variety of usage styles.
  2. The developer may not be completely familiar with the library.
  3. Developers enjoy playing with new tools. Usage patterns are often an afterthought.

One way we have attempted to improve the consistency was to create a series of best practice wiki guides. We decided to start with a relatively uncontroversial technology: ConstraintLayout. We wanted to establish a few basic usage patterns and build from there.

When to use ConstraintLayout

ConstraintLayout is effectively RelativeLayout on steroids so it can be used for any UI that requires a similar level of complexity.

The classic example of when to use it is when you have a layout with deep hierarchy, where child view groups have purely been added for the positioning:

<RelativeLayout>
<ImageView />
<ImageView />
<RelativeLayout>
<TextView />
<LinearLayout>
<TextView />
<RelativeLayout>
<EditText />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>

The more powerful positioning features of ConstraintLayout means we can rewrite the same layout to look something like this

<ConstraintLayout>
<ImageView />
<ImageView />
<TextView />
<TextView />
<EditText />
</ConstraintLayout>

This is much more readable and allows for easier refactoring as we only have to change the attributes of the individual views rather than the structure of the file.

When not to use ConstraintLayout

When a much simpler layout will suffice. A lot can be done with FrameLayout e.g a simple layout with progress bar, list and empty state can be achieved as follows

<FrameLayout><View
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<View
android:id="@+id/empty_state"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</FrameLayout>

Another example is when we are using ConstraintLayout purely to mimic the behaviour of another layout.

<ConstraintLayout>    <TextView
android:id="@+id/first"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="@+id/second"
app:layout_constraintTop_toBottomOf="@id/first"
/>
<TextView
android:id="@+id/third"
app:layout_constraintTop_toBottomOf="@id/third"
/>
</ConstraintLayout>

Using a LinearLayout here is much simpler and quicker to implement.

How to use ConstraintLayout

ConstraintLayout should be the root of the layout

Adding a ConstraintLayout arbitrarily to a deeply nested view hierarchy will probably not give us much benefit. In this situation it is probably better to allow time for a proper refactor to convert the full layout toConstraintLayout.

There are exceptions e.g. ScrollView or CardView which require to be the root of a layout.

The ConstraintLayout hierarchy should be flat (with caveats)

The performance benefits of constraint layout are achieved through the flat hierarchy, so if we add a child ViewGroup to the ConstraintLayout we will be negating this somewhat. Old habits die hard and we found this was the most common “mistake” for newcomers to ConstraintLayout. It just required a slightly different mindset and knowledge of a few features.

For example, when we are tempted to add a child LinearLayout, we can use chains instead. Adding a ViewGroup only for setting the background for a group of views can be achieved via this technique and with the same approach you could add a click listener to a collection of views.

Again there may be exceptions to this case e.g. we may be dynamically generating views and adding them to the hierarchy. While possible by setting the constraints, this may be pretty tricky to do. This would be a tradeoff of performance versus simplicity.

Nested ConstraintLayout

While it is possible to nest ConstraintLayout any decision to do so should take into account any performance impact.

Of course some of our designs are quite complex and having a single ConstraintLayout file for these could become unmaintainable. Consider the following design for a ticket

Design for the ticket view

We decided to divide this design into several smaller ConstraintLayout xml files: in effect nesting layouts. For example the middle part was extracted as shown below

Corresponding xml structure of design

In addition, some parts of the design may be re-used, for example this middle section was reused in the designs for single and return tickets so having the separate xml file helped us here.

So there is a degree of flexibility here: as long as we don’t nest several ConstraintLayouts deep, then it should be okay. And of course we should measure the performance before making any optimisations.

Upgrading the ConstraintLayout version

As we we added more ConstraintLayouts to our codebase we learned to be a bit more careful when upgrading.

Sometimes our code was working around bugs in ConstraintLayout which were then fixed and would produce weird behaviour in our workarounds. However this was probably due to a period where there were several changes in ConstraintLayout. We are currently on version 1.1.3 which we find to be stable.

If we did find bugs we would attempt to recreate the issue in as small as possible example and file at https://issuetracker.google.com/. Previous bugs that have been raised by members of the team have been fixed. Also as ConstraintLayout is a separate library we could get bug fixes without having to upgrade our target SDK.

Conclusion

Having this guide was a good step in improving the consistency and quality of our codebase. It gave us a starting point for further discussion in our weekly tech meeting and helped newcomers to the team or those that weren’t familiar with the library to get up to speed. In addition, going over the full codebase gave us a better picture of how we use a specific library.

However, it is very much the first step, once we have established the basic usage we intend to go into further detail of the various features of the library.

In hindsight, we should have been quicker to establish such basic usage patterns, but this has been a lesson for us and we will look to improve on this in the future. In addition, we aim to produce more guides for the other libraries we use.

Good Resources for ConstraintLayout

https://developer.android.com/reference/android/support/constraint/ConstraintLayout

https://constraintlayout.com/

https://medium.com/comparethemarket/three-key-lessons-when-migrating-to-constraintlayout-dff38c31a47

Thanks to Robert Papp and Orazio Cotroneo for proof reading.

--

--