In this tutorial, we will discuss the Android ConstraintLayout attributes. We will learn how to position views based on constraints through XML layout and programmatically using Kotlin.
What is Android ConstraintLayout?
Android Constraint is RelativeLayout with additional features. They were introduced to prevent too many nested layouts. They flatten out the layouts.
Some of the constraint layout featrues are:
- Positioning sides of a view relative to sides of another view.
- ConstraintSets
- Setting horizontal/vertical bias
- Chaining and grouping views together
- Barriers
- Setting the percent of width or height the view would occupy on the screen.
- Circular positioning the views.
Constraint Layout Dependencies
Please add the following dependency in your build.gradle file for constraint layout support.
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
Let’s look into some examples of using constraint layout in android apps.
Positioning Views Relative to one another
Let’s drag and drop a view into the center of the screen.
As you see in the above GIF, the Button is positioned in the center of the screen with each side having the constraint to the parent view.
We can remove either a single constraint or all as shown below.
The cross button is used to remove ALL constraints. Clicking the circle would remove the constraint of that specific side.
You can drag the same circle to create a new constraint.
Let’s look at the XML layout for the above design.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Button"
app:layout_constraintStart_toEndOf="@+id/button2"
app:layout_constraintTop_toBottomOf="@+id/button2" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
The parent refers to the root view.
The list of XML attributes to position views by each other’s sides(top/left/bottom/right) is as follows.
We can align two views by their baselines as well.
Let’s learn how to create constraints programmatically using Kotlin.
ConstraintSet
The ConstraintSet instance is defined to set the constraints programmatically.
The activity_main.xml does not contain anything inside the ConstraintLayout tag. Let’s look at the main activity Kotlin code.
package net.androidly.androidlyconstraintlayout
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.constraint.ConstraintLayout
import android.support.constraint.ConstraintSet
import android.widget.Button
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
val ID_1 = 1
val ID_2 = 2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = Button(this)
button.text = "Button"
button.id = ID_1
val lp = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT)
button.layoutParams = lp
constraintLayout.addView(button)
val button2 = Button(this)
button2.text = "Button2"
button2.id = ID_2
constraintLayout.addView(button2)
val constraintSet = ConstraintSet()
//Copy all the previous constraints present in the constraint layout.
constraintSet.clone(constraintLayout)
constraintSet.connect(ID_1, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP)
constraintSet.connect(ID_1, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
constraintSet.connect(ID_1, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.LEFT)
constraintSet.connect(ID_1, ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT)
constraintSet.connect(ID_2, ConstraintSet.BOTTOM, ID_1, ConstraintSet.TOP)
constraintSet.connect(ID_2, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.LEFT)
constraintSet.connect(ID_2, ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT)
constraintSet.applyTo(constraintLayout)
}
}
The clone()
function is used to copy all the previous constraints defined in the constraint layout.
In the above code, we are setting the first button in the center of the layout and the second button is on top of it.
The connect()
function has an additional fifth parameter as well for margin values.
The equivalent of parent in XML layout is ConstraintSet.PARENT_ID.
Horizontal and Vertical Bias
The attributes for setting bias are: app:layout_constraintVertical_bias
and app:layout_constraintHorizontal_bias
.
They expect a value between 0 and 1, and 0.5 is the default value.
Chaining and Grouping of Constraints
Chains are used to evenly space views horizontally or vertically.
The xml layout code:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toTopOf="@+id/button5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button4" />
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Button"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread_inside" />
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button" />
</android.support.constraint.ConstraintLayout>
The chain styles can be of the following types.
- spread
- spread inside
- packed
- weighted
The weighted style requires setting weight on each of the views. The view with most weight occupies the most space.
The app:layout_constraintHorizontal_weight
is used to set the weights in a horizontal chain.
Groups are used to toggle the visibility of a group of views together.
Add the following inside XML code to the previously defined constraint layout.
<android.support.constraint.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
app:constraint_referenced_ids="button4,button9" />
Barriers
A Barrier is used to create a virtual divider or a guideline on a group of views based on the largest view in the group.
This virtual divider can be connected with other child views.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextViewnTextViewnTextViewnTextViewnTextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextViewnTextViewnTextViewnTextViewnTextViewnTextViewnTextViewnTextViewnTextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="textView, textView2" />
<Button
android:id="@+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/barrier" />
</android.support.constraint.ConstraintLayout>
ConstraintLayout Percentage Width and Height
Constraint Layouts support percentage width and height, just like LinearLayout.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextViewnTextViewnTextViewnTextViewnTextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextViewnTextViewnTextViewnTextViewnTextViewnTextViewnTextViewnTextViewnTextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="textView, textView2"
tools:layout_editor_absoluteY="331dp" />
<Button
android:id="@+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/barrier" />
</android.support.constraint.ConstraintLayout>
For percentage width, you have to set the Button’s width to 0dp and vice-versa for percentage height.
Circular Positioning of Constraint Layout
We can position the views at radial distances and angles from one another. The app:layout_constraintCircle
is used to reference the view, which would act as the center of the circle.
The app:layout_constraintCircleRadius
and app:layout_constraintCircleAngle
are used to set the radius distance from the center of the circle and the angle at which our view would be positioned.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="Hello"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hi"
android:textSize="18sp"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircle="@+id/textView"/>
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Another"
android:textSize="18sp"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircle="@+id/textView"/>
</android.support.constraint.ConstraintLayout>