12

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'
StayCool
  • 421
  • 1
  • 9
  • 23

3 Answers3

11

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)
   }
}
}
hoford
  • 4,918
  • 2
  • 19
  • 19
  • I will test it and will be back with answer tomorrow – StayCool Jun 28 '20 at 21:38
  • Works perfectly. Now I have to dig and understand why my layout is not working. – StayCool Jul 07 '20 at 12:04
  • How does motionlayout determine which state (1-5) to be its initial state? ie. what should motionLayout.currentState return when run for the first time? – ddolce Nov 18 '22 at 00:57
  • 1
    Due to historical reasons the start state of the first transition in the file is the initial state. (original motionLayout had only 2 states and a transition) – hoford Nov 18 '22 at 01:41
3

Unless you have it should only transition to the state you call if you call ml.transitionToState(R.id.xx);

Remove all autoTransition attributes in you motionScene

hoford
  • 4,918
  • 2
  • 19
  • 19
0

maybe try something like this:

motion_layout.setTransitionListener(
    object : MotionLayout.TransitionListener {
        override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
        }

        override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
            when(currentId) {
                R.id.set1 -> motion_layout.setTransition(currentId, R.id.set2)
                R.id.set2 -> motion_layout.setTransition(currentId, R.id.set3)
                R.id.set3 -> motion_layout.setTransition(currentId, R.id.set4)
                R.id.set4 -> motion_layout.setTransition(currentId, R.id.set1)
        }
    }
)

motion_layout.transitionToEnd()

And control then call motion_layout.transitionToEnd() every time you want to change the state

Mariusz Brona
  • 1,549
  • 10
  • 12
  • 1
    Actually I just want to stop Transition after one is completed. And trigger next one after some time. I will try this callback and be right back with feedback – StayCool Jun 16 '20 at 11:07
  • As I said, it will stop at R.id.set2, and after some time just invoke `motion_layout.transitionToEnd` and it will go to R.id.set3 – Mariusz Brona Jun 16 '20 at 11:18
  • This motionLayout is so messy. Actually, I couldn't even try your solution. Because my scene was completely broken, and I wasn't able to just run it all to the end. Will be back with this task in few days, and I will try your solution first – StayCool Jun 17 '20 at 21:01