Motion Scene Programmatically

MotionLayout: creating MotionScene without XML

Vladislav Shesternin
3 min readApr 27, 2021
Creating a MotionScene using Kotlin

RESULT:

Result

In this example, you will learn how to implement MotionScene completely programmatically.

1️⃣ Creating a Layout

res/layout/activity_main

<?xml version="1.0" encoding="utf-8"?>
<
androidx.constraintlayout.motion.widget.MotionLayout 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"
app:layoutDescription="@xml/activity_main_scene"
tools:context=".MainActivity">

<
View
android:id="@+id/view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/black"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent=".1" />


</
androidx.constraintlayout.motion.widget.MotionLayout>

2️⃣ Necessarily -> app:layoutDescription

res/xml/activity_main_scene

<?xml version="1.0" encoding="utf-8"?>
<
MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<
Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start" />

<
ConstraintSet android:id="@+id/start">
<
Constraint android:id="@+id/widget" />
</
ConstraintSet>

<
ConstraintSet android:id="@+id/end">
<
Constraint android:id="@id/widget" />
</
ConstraintSet>
</
MotionScene>

app:layoutDescription — must be present even as a placeholder.

3️⃣ Create ids file

res/values/ids

<resources>
<
item name="transition_id" type="id"/>
<
item name="start_set_id" type="id"/>
<
item name="end_set_id" type="id"/>
</
resources>

4️⃣ Preparing the starting and final states

I use viewBinding:

bild.gradle(Module: …)

android {
buildFeatures {
viewBinding = true
}
}

MainActivity.class

// Start
private fun ConstraintLayout.initStartSet() = ConstraintSet().apply {
clone(this@initStartSet)
applyTo(this@initStartSet)
}

// End
private fun ConstraintLayout.initEndSet() = ConstraintSet().apply {
clone(this@initEndSet)
constrainPercentWidth(binding.view.id, .5f)
connect(binding.view.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END)
connect(binding.view.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
applyTo(this@initEndSet)
}

In this example, I am sure that the binding will be initialized, if your situation is different, you can pass views to the function arguments.

5️⃣ Create MotionScene

private lateinit var scene: MotionScene

To further add and change Transitions to it and ConstraintSets.

6️⃣ Initialize and Launch-> MotionScene

Initialize and Lounch MotionScene when clicking on parent ViewGroup.

FIRST method:

binding.root.setOnClickListener {
scene = MotionScene(binding.root).apply {
val transition = TransitionBuilder.buildTransition(this, R.id.transition_id, R.id.start_set_id, binding.root.initStartSet(), R.id.end_set_id, binding.root.initEndSet())
setTransition(transition)
binding.root.setScene(this)
}
binding.root.apply {
setTransitionDuration(1000)
transitionToEnd()
}

SECOND method:

scene = MotionScene(binding.root).apply {
val transition = TransitionBuilder.buildTransition(this, R.id.transition_id, R.id.start_set_id, ConstraintSet(), R.id.end_set_id, ConstraintSet())
setTransition(transition)
binding.root.setScene(this)
}

binding.root.apply {
getConstraintSet(R.id.start_set_id).clone(initStartSet())
getConstraintSet(R.id.end_set_id).clone(initEndSet())
setTransitionDuration(1000)
transitionToEnd()
}

We could end on this, but there is one more thing:

7️⃣ The Interesting 😋😉🤩

Intrigue

8️⃣ Add id for next transition

res/values/ids

<resources>
<
item name="transition_id" type="id"/>
<
item name="start_set_id" type="id"/>
<
item name="end_set_id" type="id"/>

<
item name="transition_rotation_id" type="id"/>
<
item name="rotation_set_id" type="id"/>
</
resources>

9️⃣ Preparing the state

// Rotation
private fun ConstraintLayout.initRotationSet() = ConstraintSet().apply {
clone(this@initRotationSet)
setRotation(binding.view.id, 360f)
applyTo(this@initRotationSet)
}

🔟 Creating a field for counting clicks

When pressed for the first time, the transition from the start to the end state;

When pressed for the second time, the transition from the end to the rotation state;

private var clickCounter = 0binding.root.setOnClickListener {
when (clickCounter) {
0 -> {
scene = MotionScene(binding.root).apply {
val transition = TransitionBuilder.buildTransition(this, R.id.transition_id, R.id.start_set_id, binding.root.initStartSet(), R.id.end_set_id, binding.root.initEndSet())
setTransition(transition)
binding.root.setScene(this)
}

binding.root.apply {
setTransitionDuration(1000)
transitionToEnd()
}

clickCounter++
}
1 -> {
scene.apply {
val transition = TransitionBuilder.buildTransition(this, R.id.transition_rotation_id, R.id.end_set_id, binding.root.initEndSet(), R.id.rotation_set_id, binding.root.initRotationSet())
addTransition(transition)
}

ConstraintSet().apply {
clone(binding.root)
setTransformPivot(binding.view.id, binding.view.width / 2f, binding.view.height / 2f)
applyTo(binding.root)
}

binding.root.apply {
setTransition(R.id.transition_rotation_id)
setTransitionDuration(5000)
transitionToEnd()
}
}
}
}

We have defined setTransformPivot in a separate ConstraintSet since the ConstraintSet defined in the functions will change their values after the time specified in setTransitionDuration().

setTransformPivot — determine the coordinates around which the rotation will occur.

addTransition — adds a transition to the MotionScene(there may be several).

setTransition — sets the default transition to the MotionScene (only one).

getConstraintSet() — provides the ConstraintSet on the specified ID, if available in MotionScene.

The resulting ConstraintSet needs to be configured (how to change the value of a variable). Imagine a typical ConstreintSet setup in MotionScene xml.

Programmatically vs XML

LAUNCH 🥳🤩👏🕺💃

PS. vel_dan: Love what You DO 💚

--

--