2023-10-01 · 3 min read

Simplifying Constraint Layout Constraints Programmatically

Read on Medium ↗

Article illustration

Hey there, Android developers!

If you’re an Android developer, you’ve likely faced the challenge of making your app’s user interface (UI) responsive across a multitude of different devices. It’s a common struggle, and it can be especially tough when dealing with ConstraintLayout’s ConstraintSet().

It can be a bit tricky, with syntax that’s easy to forget and functions that might send you running to Google for help. But fear not; I’ve got your back.

Article illustration

Let’s dive right in.

  1. Setting up the project
    Add these two MaterialTextViews in the MainActivity.xml, like this which will generate the output shown below in the image.
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
tools:context=".MainActivity">

<com.google.android.material.textview.MaterialTextView
android:id="@+id/textHello"
style="@style/TextAppearance.Material3.DisplayMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, "
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.textview.MaterialTextView
android:id="@+id/textName"
style="@style/TextAppearance.Material3.DisplayMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Meet"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/textHello"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btnMoveName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Move Name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

2. Now modifying the ConstraintSets (Note: I am using ViewBinding here)

The Traditional Method (Hard to remember): What we are doing here is modifying the constraints of the “Meet” — “textName”. We are connecting it’s startToStartOf — “parent” & topToBottomOf —” textHello”.

private fun setupViews() {
binding.btnMoveName.setOnClickListener {
moveNameBelowHello()
}
}

private fun moveNameBelowHello(){
with(binding) {
ConstraintSet().apply {
clone(root)
connect(textName.id, ConstraintSet.START, root.id, ConstraintSet.START)
connect(textName.id, ConstraintSet.TOP, textHello.id, ConstraintSet.BOTTOM)
clear(textName.id,ConstraintSet.BOTTOM)
applyTo(root)
}
}
}

This will move the “Meet” below “Hello” on a click of (Move Name) Button.

Now KTX way (Easy to Use):

Here’s my contribution to the Android developer community: Kotlin Extension Functions (KTX Functions) that can make working with ConstraintSet() a breeze.

These functions not only simplify the process but also make your code more readable and easier to understand.

private fun setupViews() {
binding.btnMoveName.setOnClickListener {
moveNameBelowHello()
}
}

private fun moveNameBelowHello(){
with(binding) {
textName.startToStartOf(root)
textName.topToBottomOf(textHello)
textName.disconnectBottom()
}
}
Article illustration
Eassyyy!!!, isn’t it ?

Seems like I have written some statement as it is. But trust me it works like a charm. What I have done here is created a Kotlin Extension function.

Let me show you one.

infix fun View.startToStartOf(target: View) {
ConstraintSet().apply {
clone(parent as ConstraintLayout)
connect(id, ConstraintSet.START, target.id, ConstraintSet.START)
applyTo(parent as ConstraintLayout)
}
}

This functions can also be called like this:

private fun moveNameBelowHello(){
with(binding) {
textName startToStartOf root
textName topToBottomOf textHello
textName.disconnectBottom()
}
}

Why??? Because,

Functions marked as infix are called infix functions. They can be called using infix notation. The infix notation is a way of calling the function without ‘.’ and ‘()’. On the left side, you will place the receiver instance and on the right side, you will place the parameter.
Article illustration

So now let’s wrap it up here, and check what’s I have added so far.

  1. Set Constraint Connections: Ever wanted to connect one view to another, say, starting from the start of a view or ending at the end of another? These functions, like startToStartOf and endToEndOf, will be your new best friends.
  2. Disconnect Connections: Sometimes, you need to sever the connection between views. With these KTX Functions, it’s as easy as pie.
  3. Set Margins: Adjusting margins for views is a common task in UI design. These functions streamline the process and keep your layout code neat.
  4. What’s in Store for the Future (Coming Soon): I’m not stopping here. I have plans to add even more functions to make your life as an Android developer even easier. Stay tuned for updates.
Also don’t forget that Constraints and Ratio works only for the childs inside ConstraintLayout

Want to dive deeper into this? You can check out the code and more details on my GitHub profile. Here’s the link.

So there you have it, a handy set of Kotlin Extension Functions that simplifies ConstraintLayout tasks for Android developers. Say goodbye to puzzling syntax and hello to a more efficient and enjoyable UI development experience.

Happy coding, and may your UIs always be responsive!

If you like the article, please support by Clapping 👏 , Saving and Sharing.

Article illustration

Github Gist:

https://medium.com/media/0d39e2c95bc4bd81f0452c4e015a69ea/href

Originally published on Medium.