Search code examples
androidanimationandroid-constraintlayoutandroidxandroid-motionlayout

How to run particular transition in MotionLayout?


I have a MotionScene with 4 ConstraintSets representing 4 states of screen (loading states) and with 3 Transitions between them. When my app state changes from e.g. loading to processing, I want to run Transition 1 (set1 -> set2), when state changes again, I want to run Transition 2 (set2 -> set3). And I can't find a way to do it.

I tried next:

To set current transition with

     motion_layout.setTransition(R.id.set1, R.id.set2)
     motion_layout.transitionToState(R.id.set2)

To just set transition

motion_layout.setTransition(R.id.set1)

To transition to some state:

motion_layout.transitionToState(R.id.set1)

but all of the above methods run all my sets together, even if I use app:autoTransition="none" .


I tried to put everything in one Transition and set app:progress = 0 on , and then control state of animation with progress:

 motion_layout.setProgress(0.25f, 1.0f)

which just runs all animation to the end, or I tried

motion_layout.progress = 0.25f

which don't animate, it just shows me 0.25 progress of animation without any motion.

How to control the flow of animation? How to run particular set? Would it be better to use progress? How to solve it?

P.S. I use

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta7'

Solution

  • I am not sure what you are doing (not enough code) but here is a as simple as I can make it 5 state example.

    <MotionScene 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">
    
    <Transition
        motion:constraintSetStart="@id/state1"
        motion:constraintSetEnd="@+id/state2"/>
    <Transition
        motion:constraintSetStart="@id/state2"
        motion:constraintSetEnd="@+id/state3"/>
    
    <Transition
        motion:constraintSetStart="@id/state3"
        motion:constraintSetEnd="@+id/state4"/>
    
    <Transition
        motion:constraintSetStart="@id/state4"
        motion:constraintSetEnd="@+id/state5"/>
    
    <Transition
        motion:constraintSetStart="@id/state1"
        motion:constraintSetEnd="@+id/state5"/>
    
    <ConstraintSet android:id="@+id/state1">
        <Constraint         android:id="@+id/view">
        <CustomAttribute motion:attributeName="text" motion:customStringValue="state1" />
        </Constraint>
    </ConstraintSet>
    
    <ConstraintSet android:id="@+id/state2">
        <Constraint         android:id="@+id/view">
            <Transform android:translationX="-100dp"/>
            <CustomAttribute motion:attributeName="text" motion:customStringValue="state2" />
        </Constraint>
    
    </ConstraintSet>
    
    <ConstraintSet android:id="@+id/state3">
        <Constraint         android:id="@+id/view">
            <Transform android:translationX="100dp"/>
            <CustomAttribute motion:attributeName="text" motion:customStringValue="state3" />
    
        </Constraint>
    </ConstraintSet>
    <ConstraintSet android:id="@+id/state4">
        <Constraint         android:id="@+id/view">
            <Transform android:translationY="-100dp"/>
            <CustomAttribute motion:attributeName="text" motion:customStringValue="state4" />
        </Constraint>
    </ConstraintSet>
    <ConstraintSet android:id="@+id/state5">
        <Constraint         android:id="@+id/view">
            <Transform android:translationY="100dp"/>
            <CustomAttribute motion:attributeName="text" motion:customStringValue="state5" />
        </Constraint>
    </ConstraintSet>
    

    this scene file works with this main_activity.xml

    <?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"
    android:id="@+id/motionlayout"
    tools:context=".MainActivity">
    
    <TextView
        android:id="@+id/view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#4DD3D3"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="transition"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:onClick="nextState"
        />
     </androidx.constraintlayout.motion.widget.MotionLayout>
    

    and this MainActivity.kt

    package com.example.multistate
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.view.View
    import kotlinx.android.synthetic.main.activity_main.*
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
    
    fun nextState(view: View) {
       when (motionlayout.currentState ) {
           R.id.state1 -> motionlayout.transitionToState(R.id.state2)
           R.id.state2 -> motionlayout.transitionToState(R.id.state3)
           R.id.state3 -> motionlayout.transitionToState(R.id.state4)
           R.id.state4 -> motionlayout.transitionToState(R.id.state5)
           R.id.state5 -> motionlayout.transitionToState(R.id.state1)
       }
    }
    }