Android Creating Complex Layouts using ConstraintLayout

Android ConstraintLayout helps you to create large and complex layouts with a flat view hierarchy without nested view groups. This helps to create high performing layouts. Android Studio’s Layout Editor makes it really easy to define the constraints for the views.

ConstraintLayout, which is now the default layout in Android Studio, gives you many ways to place objects. You can constrain them to their container, to each other or to guidelines.

In this ConstraintLayout tutorial, you’ll learn various types of constraints that you can use to build a UI with constraint layout is explained in detail.

There are currently various types of constraints that you can use:

  • Relative Positioning
  • Center Positioning
  • Circular Positioning
  • Dimension Constraint
  • Chains
  • Virtual Helper Objects

Relative Positioning

Relative positioning is to constraint a given side of the widget relative to a side of any other widget or to the parent ViewGroup either horizontally or vertically.

You can constrain a widget on the horizontal and vertical axis:

  • Horizontal Axis: left, right, start and end sides
  • Vertical Axis: top, bottom sides and text baseline

Given below is the basic format of the attribute for relative positioning:

app: layout_constraint[Source Side]_to[Target Side]Of=”[Target id or parent]

Here is the list of available Relative constraints :

  • layout_constraintLeft_toLeftOf
  • layout_constraintLeft_toRightOf
  • layout_constraintRight_toLeftOf
  • layout_constraintRight_toRightOf
  • layout_constraintTop_toTopOf
  • layout_constraintTop_toBottomOf
  • layout_constraintBottom_toTopOf
  • layout_constraintBottom_toBottomOf
  • layout_constraintBaseline_toBaselineOf
  • layout_constraintStart_toEndOf
  • layout_constraintStart_toStartOf
  • layout_constraintEnd_toStartOf
  • layout_constraintEnd_toEndOf

For example, in order to position button B to the right of button A we will constrain the left anchor point of button B to the right anchor point of button A with margin of 60dp.

relative_positioning

This is achieved using the attribute:

app: layout_constraintLeft_toRightOf = "@+id/buttonA"

Center Positioning

Center positioning constraint is used to achieve the same behavior that we achieve using the below attributes of RelativeLayout:

  • layout_centerInParent
  • layout_centerHorizontal
  • layout_centerVerticle

For centering horizontally or vertically we need to relatively constraint the widget between two targets.

The Target can either be widget’s anchor point or the parent’s anchor point.

For example, below you can see we want ButtonA to be at the center of the parent, ButtonB to be at the left of ButtonA and horizontally-centered and ButtonC to be below ButtonA and vertically_centered.

center_positioning

You can also place a view at horizontally-centered or vertically-centered instead of dragging an anchor point and connecting it with another anchor point as we have done above by doing this:

Screenshot 2019-11-29 11.57.20

There might be a requirement where you don’t want the element to be precisely at the center instead you want it to be some x percent to the left or right/top or bottom from the center in that case an interesting constraint comes into play known as bias.

Bias favor one side over another using the bias attributes:

  • layout_constraintHorizontal_bias
  • layout_constraintVertical_bias

For example, the following will make the top side with a 30% bias instead of the default 50%, such that the top side will be shorter, with the widget leaning more toward the top side :

center_positioning_with_bias

Or you can simply add the attribute in buttonA:

app:layout_constraintVertical_bias="0.3"

Circular Positioning

Circular Positioning constraint allows us to constrain the center of a widget relative to the center of another widget, at an angle and a distance.

Using this constraint we can align a widget on a circle.

              circular_positioning1circular_positioning2

Following are the attributes for circular positioning constraint:

  • layout_constraintCircle =”[references another widget id]
  • layout_constraintCircleRadius =”[the distance to the other widget center in dp]
  • layout_constraintCircleAngle =”[the angle the widget should be at (in degrees, from 0 to 360) of type int]

For example, we have buttonA, buttonB, buttonC, and buttonD. Now we want buttonB, buttonC, and buttonD to be arranged around buttonA.

Screenshot 2019-11-29 14.32.18

To achieve the above UI we have to write the following code in our xml file:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_redcolor"
        android:text="Button A"
        android:textColor="@android:color/white"
        android:textSize="18sp"
        tools:layout_editor_absoluteX="243dp"
        tools:layout_editor_absoluteY="290dp" />

    <Button
        android:id="@+id/buttonB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_redcolor"
        android:text="Button B"
        android:textColor="@android:color/white"
        android:textSize="18sp"
        app:layout_constraintCircle="@id/buttonA"
        app:layout_constraintCircleAngle="315"
        app:layout_constraintCircleRadius="200dp"
        tools:layout_editor_absoluteX="252dp"
        tools:layout_editor_absoluteY="192dp" />

    <Button
        android:id="@+id/buttonC"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_redcolor"
        android:text="Button C"
        android:textColor="@android:color/white"
        android:textSize="18sp"
        app:layout_constraintCircle="@id/buttonA"
        app:layout_constraintCircleAngle="360"
        app:layout_constraintCircleRadius="200dp"
        tools:layout_editor_absoluteX="225dp"
        tools:layout_editor_absoluteY="341dp" />

    <Button
        android:id="@+id/buttonD"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_redcolor"
        android:text="Button D"
        android:textColor="@android:color/white"
        android:textSize="18sp"
        app:layout_constraintCircle="@id/buttonA"
        app:layout_constraintCircleAngle="45"
        app:layout_constraintCircleRadius="200dp"
        tools:layout_editor_absoluteX="313dp"
        tools:layout_editor_absoluteY="637dp" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

Dimension Constraints

The dimension of the widgets can be specified by setting the android:layout_width and android:layout_height attributes in 3 different ways:

  • Hardcode value in dp
  • WRAP_CONTENT
  • MATCH_CONSTRAINT

Note : In constraintLayout use of MATCH_PARENT is discouraged because MATCH_PARENT does not take constraints applied on the widget into consideration.

WRAP_CONTENT

The widget with its dimension set to WRAP_CONTENT will only take the space that is needed.

Screenshot 2019-11-29 15.29.54

But if you only set the dimension to WRAP_CONTENT then we might not get the desired result.

For example, I have a TextView whose dimension is set to WRAP_CONTENT and its right anchor point is constrained with the left anchor point of a button. Below you can see TextView is not respecting the constrained applied on it.

Screenshot 2019-11-29 15.29.01

This was the issue encounters in the earlier version of ConstrainLayout ie. before 1.1. In 1.1 a new constraint was introduced to handle this issue. The attributes are:

  • app:layout_constrainedWidth=”true|false”
  • app:layout_constrainedHeight=”true|false”

Now after setting the below attribute in the TextView, you can see the expected output:

app:layout_constrainedWidth=”true”

Screenshot 2019-11-29 15.46.57

There are some already known modifiers that are used frequently in conjunction with WRAP_CONTENT and those modifiers are valid in ConstraintLayout as well. These modifiers are:

  • android:minWidth set the minimum width for the layout
  • android:minHeight set the minimum height for the layout
  • android:maxWidth set the maximum width for the layout
  • android:maxHeight set the maximum height for the layout

Note: These modifiers are only valid with WRAP_CONTENT and not with MATCH_CONSTRAINT.

MATCH_CONSTRAINT

The widget that specifies the dimension to be MATCH_CONSTRAINT will take all the available space.

We use odp to represent MATCH_CONSTRAINT in Constraintlayout.

For example, below you can see as we change the textview margin to MATCH_CONSTRAINT it will occupy all available space after leaving margins.

match_constraint

Along with MATCH_CONSTRAINT several modifiers are also available:

  • layout_constraintWidth_min and layout_constraintHeight_min : will set the minimum size for this dimension
  • layout_constraintWidth_max and layout_constraintHeight_max : will set the maximum size for this dimension
  • layout_constraintWidth_percent and layout_constraintHeight_percent : will set the size of this dimension as a percentage of the parent

To specify the width as the percent of parent viewgroup width. Before applying percent width we need to make sure that the anchor points on both the sides are constrained to the respective sides of the parent viewgroup.

For example, below you can see button width is 50% of the parent viewgroup width after adding the attribute layout_constraintWidth_percent in button.

<Button
    android:id="@+id/button12"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="Button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintWidth_percent="0.5"
    tools:layout_editor_absoluteY="335dp" />

Screenshot 2019-11-29 17.21.50

Ratio

In ConstraintLayout you can define one dimension of a widget as a ratio of the other one.

The attribute for specifying the ratio is

app:layout_constraintDimensionRatio=”[ratio]”

while applying ratio constraint we need to keep the following points in mind:

  • Setting one or both view’s dimension to MATCH_CONSTRAINT(0dp).
  • Opposite sides of the widget needs to be constrained.

For example, if we set a ratio of an ImageView to 1:1 then it will set the height of the image to be the same as its width.

Screenshot 2019-11-29 17.47.14

Chains

A chain is a group of views that are linked to each other with bi-directional position constraints.

The views within a chain can be distributed either vertically or horizontally.

Chain Style

When setting the attribute layout_constraintHorizontal_chainStyle  or layout_constraintVertical_chainStyle on the first element of a chain, the behavior of the chain will change according to the specified style (default is CHAIN_SPREAD).

Chains can be styled in one of the following ways:

  • Spread: The views are evenly distributed (after margins are accounted for). This is the default.

Screenshot 2019-11-28 18.03.37

 

  • Spread inside: The first and last view are affixed to the constraints on each end of the chain and the rest are evenly distributed.

Screenshot 2019-11-28 18.02.35

  • Weighted: When the chain is set to either spread or spread inside, you can fill the remaining space by setting one or more views to “match constraints” (0dp). By default, space is evenly distributed between each view that’s set to “match constraints,” but you can assign a weight of importance to each view using the attributes:

            layout_constraintHorizontal_weight

            layout_constraintVertical_weight

For example, on a chain containing three buttons using width MATCH_CONSTRAINT, with the first button using a weight of 1, second, a weight of 2 and third with a weight of 3, the space occupied by the third button will be thrice that of the first button and the space occupied by the second button will be twice that of the first button.

Screenshot 2019-11-28 18.25.16

If you’re familiar with layout_weight in a linear layout, this works the same way. So the view with the highest weight value gets the most amount of space; views that have the same weight get the same amount of space.

  • Packed: The views are packed together (after margins are accounted for). You can then adjust the whole chain’s bias (left/right or up/down) by changing the chain’s head view bias.

Screenshot 2019-11-28 18.03.09

Chain Heads

Chains are controlled by attributes set on the first element of the chain (the “head” of the chain).

The head is the left-most view in a horizontal chain and the top-most view in a vertical chain.

Creating a Chain

To create a chain, select all of the views to be included in the chain, right-click one of the views, select Chains and then select either Center Horizontally or Center Vertically, as shown below:

horizontal_chain_new

Switch between Chain Styles

You can switch between spreadspread inside, and packed chain by selecting any view in the chain, right-click and then select Cycle Chain mode.

Virtual Helper Objects

Virtual Helper Objects are views that are not displayed on the device and are only used for layout purposes.

The three basic Virtual Helper Objects are:

  • Guideline
  • Barrier
  • Group

Guideline

Guideline is a helper view that is used to align other views.

The position of all the views that are aligned to a guideline can be changed at once or allowing reactive layout behavior by using percent positioning.

A Guideline can be either horizontal or vertical:

Vertical Guideline

Width=0     Height = Height of parent

Horizontal Guidelines

Height=0    Width = Width of parent

Creating Guideline

To create a guideline, click Guidelines  in the toolbar, and then click either Add Vertical Guideline or Add Horizontal Guideline.

Switch between Guidelines

To switch between different types of guidelines we have to click the button available at the starting point of the guideline.

For example, in the below figure, I have set the guideline 112dp away from the start of the screen. Now if I click on Cycle Guideline button it positions itself 489dp away from the end of the parent layout and on clicking it third time it positions itself at 18% of the parent width.

cycle_guidelines

Positioning a Guideline is possible in three different ways:

  • layout_constraintGuide_begin : specifying a fixed distance from the left or the top of a layout
  • layout_constraintGuide_end: specifying a fixed distance from the right or the bottom of a layout 
  • layout_constraintGuide_percent: specifying a percentage of the width or the height of a layout 

In the below figure, all the four TextViews are constrained with the guideline and if I move the guideline all the TextViews that are constrained with the guideline are moving along with it.

guideline

Let’s see one more use case in which we are going to use the third type of guideline (i.e. layout_constraintGuide_percent) which positions the guideline at a certain percentage of parent’s width.

Using multiple guidelines of such type to make the dimension of a view to be of a certain percentage of the parent’s width or height.

For now, we are making the width of the ImageView to be of a certain percentage of the parent’s width.

In the above figure, the left guideline is at 25% of the parent’s width and the right guideline is at 75% of the parent’s width. Now irrespective of the screen size the width of the ImageView will always be 50% of the screen width.

guideline_percentage

Barrier

A barrier is a view that takes references of multiple widgets as input and creates a virtual guideline based on the most extreme widget on the specified side.

Similar to a guideline, a barrier is an invisible line that you can constrain views to. Except a barrier does not define its own position; instead, the barrier position moves based on the position of views contained within it.

This is useful when you want to constrain a view to a set of views rather than to one specific view.

Let’s take a use case and understand the barrier.

Here we have three TextViews what we want is we want TextView with long text to be on the right of Constraint and Linear TextViews. So we are going to constrain the left anchor point of longer TextView with the right anchor point of Constraint TextView.

Screenshot 2019-11-30 15.32.20

In case if we change the text Linear to Linearlayoutt you can see the TextView with longer text run over the TextViews on the left of it. To solve this problem we can make use of barrier.

Screenshot 2019-11-30 15.37.15

Creating Barrier

To create a barrier, follow these steps:

  1. Click Guidelines  in the toolbar, and then click Add Vertical Barrier or Add Horizontal Barrier.
  2. In the Component Tree window, select the views you want inside the barrier and drag them into the barrier component.
  3. Select the barrier from the Component Tree, open the Attributes  window, and then set the barrierDirection.

To solve this issue here we make use of Vertical barrier.

Barrier basically takes reference id of views as input to create a barrier for them. So we are going to add references of Constraint and LinearLayoutt TextViews.

Select barrier from the component tree and change barrier direction to end. Now to get the expected result constrain the left anchor point of longer TextView with the barrier.

barrier

Above you can see the barrier adjusted its position according to the view with the larger size. This is how the barrier works.

Group

Group is a helper view that controls the visibility of a set of referenced widgets. It is a convenient way to hide or show a set of widgets.

Using Group we don’t have to set the visibility of each widget individually and same as barrier it takes references of multiple widgets.

Group is useful in the case where we want to change the visibility of a set of views like in case of error occurred, loading where we don’t want to show a set of widgets.

In the below-animated figure, you can see after setting the visibility of the group to gone it is no more visible on the screen.

group

In your xml file you can see the references of the views has been added to the Group like this:

<androidx.constraintlayout.widget.Group
    android:id="@+id/group2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="invisible"
    app:constraint_referenced_ids="button,imageView,textView" />

I hope this article will help you in understanding various types of constraints that you can use to build a complex layout with Constraintlayout.

Leave a Reply