ConstraintLayout Explained

ConstraintLayout Explained

Lets look at the new Android ConstraintLayout, which was added to the Android Studio IDE recently, and made the default layout for all new layouts. The fundamental building block of ConstraintLayout is creating constraints. A constraint defines a relationship between two widgets in the layout and controls how those widgets will be positioned within the layout. For those new to ConstraintLayout, but familiar with RelativeLayout the basic principles of how constraints work are very similar to how we create relationships between widgets in RelativeLayout.

ContraintLayout Explained

So, lets start by explaining what a constraint layout is, and its key features. ConstraintLayout allows you to create large and complex layouts with a flat view hierarchy (no nested view groups). It’s similar to RelativeLayout in that all views are laid out according to relationships between sibling views and the parent layout, but it’s more flexible than RelativeLayout and easier to use with Android Studio’s Layout Editor. ConstraintLayout is available in an API library that’s compatible with Android 2.3 (API level 9) and higher. This article provides a guide to building a layout with ConstraintLayout in Android Studio 2.3 or higher.

Creating constraints in the editor

The easiest way to learn how to create constraints is using the visual editor in Android Studio. All of the examples here will show how the blueprint view represents things. Let’s start by looking at a simple TextView in the blueprint view:

blueprint

The TextView should be fairly obvious, and the two arrows show some existing constraints on this TextView which align the left and top edges to the parent ConstraintLayout. We’ll look at how to create them in a little while. We can also see the 16dp margins which will add some space between the parent ConstraintLayout and the bounds of the TextView. If we select the TextView we will see the sizing and anchor points:

anchor_points

The squares on the corners are the size handles and we can drag these to resize the TextView. These are not as useful as they may first appear because using them will result in a fixed size of the TextView and we will normally want the TextView to be sized responsively in some way.

The circles in the middle of each edge are the anchor points, and are used to create constraints. The anchor points on the left and top edges contain a blue circle which indicates that a constraint has been defined for this anchor point; and the ones on the right and bottom edge are empty which indicates that no constraint has been defined for them. So we can see how the position of the TextView has been defined by aligning the top and left edges to the parent.

Any View which subclasses TextView will also have an additional type of anchor point: the baseline. This enables us to align the baseline of the text in the TextView as well. We can view this by clicking the ‘ab’ button below the TextView:

baseline

The sausage-like shape is the baseline anchor point and we can create constraints from this in exactly the same way as we are about to see with the four edge anchor points.

The other button below the TextView (the one containing the ‘x’) will remove all constraints.

To create a constraint we simply need to grab an anchor point of one View, and drag it to one of the anchor point of another View. Here we have added a second TextView (with ID textView2) which has a left constraint to the parent, and we create a new constraint from its top to the bottom of the first TextView (which has ID textView). This will position the second TextView below the first one:

create_constraint

One thing worth noting is that while we have created a constraint from the top of textView2 to the bottom of textView, if we select both of the TextView instances then we only see a constraint is attached to the top of textView2 and there is no constraint associated with textView (the bottom anchor point of textView is still empty):

unidirectional

The reason for this is that constraints are one way (unless we’re talking about chains, which are a special case). So the constraint in question is attached to textView2, and will position it relative to textView. Since it is only attached to textView2, it has no direct influence on where textView is positioned.

Now that we know how to create a constraint between Views how do we go about creating a constraint to the parent layout itself? That is simply a case of dragging the anchor point to the appropriate edge of the parent:

create parent constraint

Creating constraints in XML

For those who like to understand what’s going on under the bonnet/hood, then the XML for that layout is:

The constraints are the attributes which begin with app:layout_constraint. We can see those on all child views of the ConstraintLayout, positioning them relative to the parent. You will also notice that textView2 specifies a constraint that the top of the view should be positioned relative to the bottom of textView.

It is worth noting that these are in the app namespace because ConstraintLayout is imported as a library so, like the support libraries, it becomes part of your app rather than the Android framework (which uses the android namespace).

Deleting a constraint

The final thing we’ll cover here is how to delete a constraint. If you’re working directly with the XML, it is simply a case of deleting the attribute itself. If you’re using the visual editor, you can delete a constraint by clicking its anchor point:

delete constraint

 Chains

Chains are a specific kind of constraint which allow us to share space between the views within the chain and control how the available space is divided between them. The closest analogue with traditional Android layouts is weights in LinearLayout, but chains do far more than that, as we shall see.

Creating a chain

As we have already mentioned, a chain consists of multiple views, so to create a chain we must select the views we wish to chain together, and then select either ‘Center Horizontally’ to create a horizontal chain, or ‘Center Vertically’ to create a vertical chain. Let’s create a horizontal chain from three views:

Create a chain

The first thing worth mentioning is that the two views at the ends of the chain (in this case the leftmost and rightmost views) already have constraints from their left & right edges respectively, to the parent. The creation of the chain simply defines the inter-relationships of the members of the chain. If we look at the chain that we have just created, there are a couple of visual representations that are worth explaining:

Create a chain

Firstly note that the two connections between the views resemble a chain (these are the chain constraints which we have just created), and the outer two connections (between the leftmost and rightmost views and the parent) resemble springs. These outermost connections denote the “chain mode” which has been applied to the chain. The “chain mode” specifies how the chain will fill the available space, and we can cycle between the three available modes using the “cycle chain mode” button which appears below all of the members of the chain:

Cycle Chain Mode

There are three possible modes: spreadspread_inside, and packed.

Spread Chain

The default mode is spread mode, and this will position the views in the chain at even intervals within the available space:

Spread Chain

Spread Inside chain

The next mode is spread_inside which snaps the outermost views in the chain to the outer edges, and then positions the remaining views in the chain at equal intervals within the available space:

Spread Inside Chain

Packed Chain

The final mode is packed which will pack the views together (although you can provide margins to separate them slightly), and then centres the group within the available space:

Packed Chain

With a packed chain, the positioning of the packed views can be further controlled by altering the bias value – in this example the bias is set to 0.5 which will centre things. But altering this value can change the position of the packed chain:

Packed Chain Bias

Spread Chain Weights

One really useful feature of both spread and spread_inside chains is that we can apply weights to individual members of the chain and we get very similar behaviour to weights in LinearLayout. Currently there is no direct way of doing this in the editor, but we can use the properties view to change attributes manually.

Weighted Chain

To apply a weight to a specific View we must first select the View in the editor, and then (if the View is in a horizontal chain) specify a android:layout_width="0dp" andapp:layout_constraintHorizontal_weight="1":

Weighted Chain Properties

Note how the appearance of the View changes in the blueprint view – the top and bottom edges change from straight lines in to become accordion-like – this gives a visual indication of a weighted View.

It is also worth noting that weights do not play nicely if we try to use them in packed mode. Whereas spread and spread_inside modes will use as much space as they need, packed mode will try and pack things in to the smallest possible space. If you attempt to use weights in a packedchain then the weighted views will shrink to zero size:

Packed Weighted Chain

Chains in XML

It would be reasonable to assume that dedicated attributes exist in order to specify chains in XML, but this is not, in fact, the case. Instead the existing constraint attributes are combined. To create chains in XML the chain constraints are simply two way, complementary constraints. Here is the XML for the initial chain creation that we looked at earlier:


On textView there is app:layout_constraintEndToStartOf="@+id/textView2"; and on textView2there is app:layout_constraintStart_toEndOf="@+id/textView" – essentially creating two constraints between the same pair of anchor points in opposite directions. It is this which defines a chain.

Also on textView there is app:layout_constraintHorizontal_chainStyle="spread" which specifies the spread mode; you can manually set this to spread_inside or packed to specify different chain modes. This must always be done on the View at the head of the chain – in other words the first item in the chain.

We can set a bias on the chain by setting app:layout_constraintHorizontal_bias="0.75" with a value between 0.0 – 1.0.

Finally, we can define weights by specifying android:layout_width="0dp" and thenapp:layout_constraintHorizontal_weight="1".

Barriers

When we are creating Android layouts, sometimes we encounter situations where the layout can change depending on the localisation. Here is a really simple example:

Aligned Layout (English)

Here we have three TextViews: textView1 and textView2 on the left; and textView3 on the right. textView3 has been constrained to the end of textView1 and this works perfectly – it positions and sizes textView3 exactly as we need it.

However, things become more complicated if we need to support multiple languages. If we add a German translation then we have a problem because in the English version the text in textView1 is longer than that in textView2, whereas in German the text in textView2 is longer than that in textView1:

Aligned Layout (German)

The problem here is that textView3 is still constrained to textView1 and so textView2 is now running in to textView3. This is seen most clearly in the Design view (the one with the white background).

The obvious solutions to this would be to either use TableLayout, or to wrap textView1 & textView2 inside a vertical LinearLayout with android:layout_width="wrap_content" and then constrain textView3 to the end of this LinearLayout. But there is a better way: Barriers.

Barrier is a virtual view, similar to a Guideline, to which we can constrain objects. The difference between a Barrier and a Guideline is that the position of a Barrier is determined by the dimensions of multiple views. In the case of our example, we don’t know whether textView1 or textView2 will be wider, so we can create a Barrier based upon the widths of these two Views. Then we can constrain textView3 to the Barrier.

Creating Barriers in the editor

Setting up barriers requires a few steps. First we create a vertical barrier by selecting Add Vertical Barrier from the context menu:

Create Barrier

You can see the Barrier that is added to the component tree (the lower left panel).

We can optionally change its position within the layout hierarchy by dragging it within the component tree:

Move Barrier

Next we need to set the direction for the Barrier. In our case we want to position the Barrierrelative to the End of either textView1 or textView2 depending on whichever is the larger, so we need to specify a direction of end:

Barrier Direction

The final step in defining the Barrier is to tell it which Views we need to position it relative to. We don’t use a constraint here because a constraint is actually a one-to-one mapping, and here we have multiple Views. For a Barrier we need to define multiple Views as referenced IDs, which we can do by dragging them within the component tree:

Barrier References

These references are listed as children of the Barrier in the component tree once they have been defined. If you watch the Blueprint view (with the blue background) in that video, you will see the Barrier jumps in to position (the dotted vertical line which moves from the left edge) as the references are created.

Now that the Barrier has been defined, all that remains is to change the constraint of textView3to be relative to the Barrier rather than textView1:

Constraint to Barrier

textView3 is positioned to the end of textView2 once the constraint is changed.

To see how this affects things overall, we can now toggle the language selection and see that the Barrier is now positioned to the end of whichever one of the referenced Views (textView1 or textView2) is the wider:

Constraint to Barrier

Creating Barriers in XML

The XML for this is actually pretty simple:


The only thing specific to Barrier is the Barrier element itself. The app:barrierDirectionattribute determines the direction of the Barrier – in this case it will be positioned at the end of the referenced Views.

The list of referenced Views is a comma separated list of the IDs of other Views within the layout (without the @+id/ qualifier).

Conclusion

I hope this article will help you kickstart your UI development journey using constraint layout. Remember to leave your questions or comments below and I will be happy to help you out. In my opinion, the constraint layout is a very powerful tool for the Android Developers, and I hope you will start using it in your projects.

Happy Coding!

2 Replies to “ConstraintLayout Explained”

Comments are closed.