ConstraintLayout in practice
When creating complex and flexible views we need to combine and nest multiple layouts within each other, which often leads to performance issues on lower-end devices, especially when a complex view is used as a list item.
Android recently released the ConstraintLayout
1.0.2 version and within Android Studio 2.3, the ConstraintLayout
is used as the default layout in view templates. It should help us create more efficient and flat, but still flexible layouts.
Let’s create a happy little view using “old” layouts
On the left, we have a complex view which can have multiple states, and the views are positioned differently depending on the visibility of other views. Its components are very common and I believe that we’ve all received similar design requirements from our clients and/or designers.
With this kind of view, it’s impossible to achieve a flat hierarchy and keep the flexibility, even with the RelativeLayout
. In this case, we need to combine multiple layouts with nesting to achieve the desired behaviour.
To achieve this behaviour without using a ConstraintLayout
we need to create a hierarchy that looks like this:
You can see I used a lot of LinearLayouts
since I needed to handle cases where other views should take the place of invisible views and everything should wrap evenly. LinearLayout
weights are really useful :). Since we all know how to create a similar view, I won’t delve deeper into explaining this, and you can always check the source code.
Writing XML vs Design mode
When I started developing on Android, I wanted to create XML layouts, I mostly used Design
mode since it looked easier than writing. But, very soon I realised that writing XML is not as hard as it seemed and I have switched to writing solely in XML. It’s a lot faster and you have a better control on placing views — at least from my experience. New versions of Android Studio added a lot of interesting new features; Design
mode brought a lot of good things and I’ve often heard that we should give it a try. I’ve tried using it while building a view with a ConstraintLayout
. While it really is good, especially for learning how constraints are created and what happens to the views, I still prefer writing XML than Design mode
because Android Studio adds some extra properties which make code less readable and noisy. I’ve found it handy when I use it side-by-side since you can interact with the design and blueprint while writing XML. It’s also very useful when you want to check how the view will react to different constraints.
It’s time to create some constraints
So, let’s see how my view and hierarchy look after being converted to a ConstraintLayout:
Well, this looks a lot better now, don’t you think? (except for the LinearLayout
, but we’ll get back to it later).
Just to mention, Android Studio provides the option for converting existing views to ConstraintLayout
, but it didn’t really work for me. Maybe it’s ok for simple layouts and maybe it will work better in the future, but I didn’t find it very useful at this moment.
On the 3rd image you can see how our view looks cleaner in contrast to the 2nd image while everything remained in its place.
You may have noticed that I’m not using AspectRatioImageView
anymore. That’s because every child in the ConstraintLayout
is able to define aspect ratio with the property, e.g:
app:layout_constraintDimensionRatio=”5:2"
Which I find very useful, especially for images.
All properties that are specific to ConstraintLayout
start with a app:layout_constraint.
At first it might look like there are too many of them; and that it’s too complicated for use, but they are really straightforward; e.g. if you want to align a top of the view to a bottom of an another view, simply use the layout_constraintTop_toBottomOf.
Also, I didn’t need a helper View to centre FAB to the bottom-right of the image. If you want to centre a certain view to the other view’s side, make constraints from two sides of the view towards the one side of the other view. In my case I have used:
app:layout_constraintBottom_toBottomOf=”@+id/image”app:layout_constraintTop_toBottomOf=”@+id/image”
And how do we define a view size? Does it work like it does in any other view? Well, we can use a fixed size and wrap_content
like with any other view, but we can’t use match_parent
any more. I mean, you can try, but Android studio will instantly change that value to some fixed size which it thinks is good; and there is no arguing with him :). Instead, you should use match constraints, aka any size, which is defined by setting the height or/and width to zero dp.
I mentioned that I like weights in the LinearLayout
, especially when I need to align multiple views which are prone to visibility changes (I’ve used this a lot in the example). Well, in the ConstraintLayout
you can align those views directly without nesting. When a view’s visibility
is set to GONE, it actually doesn’t disappear from the layout like in similar layouts.
Instead, it’s dimensions are set to 0 and the constraints are not lost, so all views which are linked with it will still be in the right place and this makes a UI very responsive. In this case we can also set a specific margin which will be used, e.g:
app:layout_goneMarginTop=”20dp”
Just to mention, all constraints also support start and end attributes for the RTL support.
As you can see on the 5th picture, I couldn’t make it fully flat, and that’s why I’ve added an additional nesting within the LinearLayout
. Buttons are placed below two texts, and the top can be constrained only to the bottom of one of them.
Let’s say that the buttons are constrained to the description. If the description view has a smaller height than the comments view, the buttons would overlap it, so I had to put them in a new layout which is used for the top constraint of the buttons. Hopefully, we’ll get a solution for these situations in the future and if you already have one, please leave a comment. :)
And lastly, the buttons are nicely packed and centred on the bottom of the view. By making a bi-directional connection between views, we create a chain within the views and it can have one of four styles. I’ve used "packed”
since they need to be placed in the middle and side by side. If you need some other kind of behaviour, there is also a "spread"
and "spread_inside"
. With a combination of 0dp on the height or width, it will behave like weights in the LinearLayout
, and then we achieve the fourth style. Chain can be vertical or horizontal and their style is set to a “head view” (very top one on a vertical, and a very left one on the horizontal chain) with the:
app:layout_constraintHorizontal_chainStyle=”packed”
There are some other features like bias
and guidelines
, although there was no need to use them in this example, they could be very helpful in many cases.
What about performance?
We all know that perf matters and we should always make well optimised views. Nested and complex views can create performance issues. Obviously, ConstraintLayout
helps with creating flatter views, but how does it affect performance?
First of, let’s see how view and it’s children are measured on both views.
We can see that only buttons aren’t measured twice in the “normal” layout. RelativeLayout
always has two passes while LinearLayout
will make two passes if we use weight, and that’s the case with the first two LinearLayouts
.
In ConstraintLayout
some views are measured twice and some only once. That’s because it only measures twice the children whose dimension depend on the constraints which could affect the final height or width of the view. When you set the width
or height
to wrap_content
or to a fixed size, that view will be measured once and that could improve performance. In case you’ve used match constraints
, that view will be measured twice.
I wanted to see some numbers, so I tracked the time between the start of onMeasure()
method and the end of onDraw().
Average speed improvement was around 9% in favour to ConstraintLayout
. Average time for LinearLayout
was 76 ms, and for ConstraintLayout
69 ms. The device used was Samsung Galaxy S4 mini with KitKat.
Obviously, the results will depend on the complexity of the view, but ConstraintLayout
should generally give better results.
Conclusion
The ConstraintLayout
is great for making flexible and responsive views in Android without having a deep hierarchy and it’s a real step up from the RelativeLayout. You should be able to make more efficient views already and we can only expect that it will get better over time. If you still have performance issues with complex views, and ConstraintLayout
doesn’t solve it, I found creating a custom ViewGroup
is still the best way to make a fast and fluid UI, but it can also be time consuming.
So go, play and experiment with it, if you already haven’t. :)
Thanks to Sinisa Cvahte for the design.
Thank you for reading. Please comment, like or share it with your friends and we hope to see you soon.
Would you like to join us? Check out the open positions at our Careers page.