Android Development
ConstraintLayout
Programmatically Modify Layout
Android UI Design
Layout Constraints

ConstraintLayout change constraints programmatically

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The standard way to change ConstraintLayout constraints at runtime is to use ConstraintSet. It lets you clone the current layout, change the relationships between views, and then apply the new constraints back to the layout without rebuilding the whole screen.

Why ConstraintSet Is the Right Tool

Each child view inside a ConstraintLayout has constraint-related layout parameters, but editing those parameters directly becomes hard to read once the relationships get more complex. ConstraintSet gives you a more declarative way to update the layout.

The normal sequence is:

  1. get the ConstraintLayout
  2. clone its current constraints into a ConstraintSet
  3. clear or reconnect the constraints you want to change
  4. apply the set back to the layout

That is easier to maintain than manually editing every margin and anchor field on LayoutParams.

Basic Example in Kotlin

Suppose a button starts aligned under one view and later needs to move under another view:

kotlin
1import android.os.Bundle
2import android.widget.Button
3import androidx.appcompat.app.AppCompatActivity
4import androidx.constraintlayout.widget.ConstraintLayout
5import androidx.constraintlayout.widget.ConstraintSet
6
7class MainActivity : AppCompatActivity() {
8    override fun onCreate(savedInstanceState: Bundle?) {
9        super.onCreate(savedInstanceState)
10        setContentView(R.layout.activity_main)
11
12        val root = findViewById<ConstraintLayout>(R.id.rootLayout)
13        val moveButton = findViewById<Button>(R.id.moveButton)
14
15        moveButton.setOnClickListener {
16            val set = ConstraintSet()
17            set.clone(root)
18
19            set.clear(R.id.targetView, ConstraintSet.TOP)
20            set.connect(
21                R.id.targetView,
22                ConstraintSet.TOP,
23                R.id.secondAnchor,
24                ConstraintSet.BOTTOM,
25                16
26            )
27
28            set.applyTo(root)
29        }
30    }
31}

clear removes the old anchor. connect creates the new one. applyTo tells the layout to re-run with the new constraints.

XML IDs Still Matter

Programmatic constraint changes work best when the views already have stable IDs in XML:

xml
1<androidx.constraintlayout.widget.ConstraintLayout
2    android:id="@+id/rootLayout"
3    android:layout_width="match_parent"
4    android:layout_height="match_parent">
5
6    <TextView
7        android:id="@+id/firstAnchor"
8        android:layout_width="wrap_content"
9        android:layout_height="wrap_content" />
10
11    <TextView
12        android:id="@+id/secondAnchor"
13        android:layout_width="wrap_content"
14        android:layout_height="wrap_content" />
15
16    <Button
17        android:id="@+id/targetView"
18        android:layout_width="wrap_content"
19        android:layout_height="wrap_content" />
20</androidx.constraintlayout.widget.ConstraintLayout>

Without stable IDs, you have nothing reliable to connect against.

Animating the Constraint Change

A direct applyTo works, but it can look abrupt. If you want a smoother UI transition, wrap the change with TransitionManager:

kotlin
1import android.transition.TransitionManager
2
3TransitionManager.beginDelayedTransition(root)
4set.applyTo(root)

That is often enough for simple position or size changes. For more advanced motion, MotionLayout may be a better fit, but ConstraintSet plus a delayed transition handles many day-to-day cases cleanly.

Creating Alternative Layout States

If the same constraint configuration is reused, it can be cleaner to maintain multiple states rather than building every rule from scratch in code.

kotlin
1val collapsed = ConstraintSet().apply { clone(root) }
2val expanded = ConstraintSet().apply {
3    clone(root)
4    clear(R.id.details, ConstraintSet.TOP)
5    connect(R.id.details, ConstraintSet.TOP, R.id.header, ConstraintSet.BOTTOM, 24)
6}
7
8expandButton.setOnClickListener {
9    expanded.applyTo(root)
10}
11
12collapseButton.setOnClickListener {
13    collapsed.applyTo(root)
14}

This pattern works well for simple UI state machines such as collapsed versus expanded content, login versus signed-in layouts, or compact versus detailed cards.

When Direct LayoutParams Editing Is Enough

For tiny one-off changes, direct layout parameter edits can work, but only if you are disciplined about casting and reassigning correctly. In most real screens, ConstraintSet stays clearer because it mirrors the mental model of "connect this view to that anchor".

Common Pitfalls

The most common mistake is adding a new constraint without clearing the old conflicting one. That can leave the view over-constrained or positioned in an unexpected place.

Another issue is forgetting to call applyTo, which means all your edits stay in the ConstraintSet object and never affect the screen. Developers also often forget IDs on views created dynamically, making runtime connection logic much harder. Finally, if you expect smooth animation and do not use a transition, the layout jump can look like a bug even when the constraints are correct.

Summary

  • Use ConstraintSet to change ConstraintLayout relationships at runtime.
  • Clone the current layout, edit constraints, then call applyTo.
  • Clear conflicting constraints before connecting new ones.
  • Keep stable view IDs so constraints can be targeted reliably.
  • Add TransitionManager.beginDelayedTransition if you want the changes to animate smoothly.

Course illustration
Course illustration

All Rights Reserved.