I have started using motion layout for some top bar scrolling, and looks like there is an issue that prevents recycler view from showing updated data. Currently I am using 2.0.0-alpha3 of ConstraintLayout. In the view I have toolbar and 2 tabs that act as filter, lets say filterX and filterY those pass some rx stuff that basically just filters list of items based on the type this is not important because data is filtered properly, thread is correct, data is passed to adapter every time, but when I do scroll my motion layout to top or bottom, from time to time the changes are not reflected in recycler view, they reload after I scroll it a little bit or even touch, its not happening in case of standard ConstraitLayout. Have anyone experienced this and know any solution?
Edit: Layout with motion layout:
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="#xml/scene_month">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="#drawable/ic_back"/>
<TextView
android:id="#+id/title"
style="#style/ActivityTitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/toolbar" />
<com.example.FilterView
android:id='#+id/filter_view'
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/title"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:paddingBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/filter_view" />
</androidx.constraintlayout.motion.widget.MotionLayout>
And motion 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">
<OnSwipe
app:dragDirection="dragUp"
app:touchAnchorId="#id/recycler"
app:touchAnchorSide="top" />
</Transition>
<ConstraintSet android:id="#+id/start">
<Constraint android:id="#id/title"
android:text="#string/title"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center_vertical"
android:textSize="28sp"
android:textColor="#color/dark_gray"
android:fontFamily="#font/rubik_medium"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/toolbar">
<CustomAttribute
app:attributeName="textSize"
app:customFloatValue="28"/>
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="#+id/end">
<Constraint android:id="#id/title"
android:text="#string/title"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:gravity="center_vertical"
android:minHeight="?actionBarSize"
android:textSize="28sp"
android:textColor="#color/dark_gray"
android:fontFamily="#font/rubik_regular"
android:layout_marginStart="72dp"
android:layout_marginEnd="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<CustomAttribute
app:attributeName="textSize"
app:customFloatValue="20"/>
</Constraint>
</ConstraintSet>
</MotionScene>
By following the possible solution as discussed here:
https://issuetracker.google.com/issues/130386093
adding the tag motion:layoutDuringTransition="honorRequest" to the Transition fixed the issue
Example:
<Transition
android:id="#+id/myTransition"
motion:constraintSetEnd="#+id/end"
motion:constraintSetStart="#+id/start"
motion:layoutDuringTransition="honorRequest"
motion:motionInterpolator="linear">
<OnSwipe
motion:dragDirection="dragUp"
motion:onTouchUp="stop"
motion:touchAnchorId="#+id/swipeRefreshLayout"
motion:touchAnchorSide="top" />
besides that, can also try to update to newer version, in my case, I'm currently using
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta08'
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha05'
Note: layoutDuringTransition tag only available with beta04 version of constraint layout and above
RecyclerView.scrollBy(0, 0) as temporary workaround.
Link to these Google Issue Tracker
https://issuetracker.google.com/issues/130386093
https://issuetracker.google.com/issues/130862165
For some reason RecyclerView.scrollBy(0, 0) didn't work for me. I am scrolling the whole motion layout to top using below method when there are updates to recyclerView.
motionLayoutView.transitionToStart()
update to newer version
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta7'
as they fixed that problem as listed with release notes
Nested scroll view issues in MotionLayout
Transition listener issues with MotionLayout
Memory leak in MotionLayout
RecyclerView performances
Group visibility
Padding issues
https://androidstudio.googleblog.com/2020/06/constraintlayout-200-beta-7.html
None of these workarounds worked for me. I gave up on fixing these and I was fixing other UI bugs I had. I have several views sliding up/down and hiding/showing.
I was using TransitionManager.beginDelayedTransition(...) in my code and it wasn't inside a Runnable. After wrapping it in view.post{} like this the problem disappeared:
view.post{
TransitionManager.beginDelayedTransition()
}
This may seem irrelevant but I suggest checking your code to see if the problem is somewhere else. Like everyone else I thought this is caused by MotionLayout because when I swapped it with ConstraintLayout it worked fine.
Related
I'm trying to create a CollapsingToolbar animation using MotionLayout.
I've successfully animated everything to behave just like a CollapsingToolbar with a high level of flexibility, which means I can easily create awesome animations without writing a large amount of code.
My problem is no matter what I tried; I can't resize the title's TextView in a natural way.
I'm currently using ConstraintLayout version 2.0.0-beta3
Trial #1
CustomAttribute of textSize
<ConstraintSet android:id="#+id/dish_fragment_expanded_set">
...
<Constraint
android:id="#+id/dish_fragment_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/dish_fragment_cover_image">
<CustomAttribute
app:attributeName="textSize"
app:customFloatValue="24" />
</Constraint>
...
</ConstraintSet>
<ConstraintSet android:id="#+id/dish_fragment_collapsed_set">
...
<Constraint
android:id="#id/dish_fragment_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
app:layout_constraintBottom_toBottomOf="#+id/dish_fragment_navigation_icon"
app:layout_constraintStart_toEndOf="#+id/dish_fragment_navigation_icon"
app:layout_constraintTop_toTopOf="#id/dish_fragment_navigation_icon">
<CustomAttribute
app:attributeName="textSize"
app:customFloatValue="16" />
</Constraint>
...
</ConstraintSet>
Result
The solution above works, but the text flickers on movement, which means the animation is not smooth.
Trial #2
scaleX & scaleY
<ConstraintSet android:id="#+id/dish_fragment_expanded_set">
...
<Constraint
android:id="#+id/dish_fragment_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/dish_fragment_cover_image"
android:scaleX="1"
android:scaleY="1"/>
...
</ConstraintSet>
<ConstraintSet android:id="#+id/dish_fragment_collapsed_set">
...
<Constraint
android:id="#id/dish_fragment_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
app:layout_constraintBottom_toBottomOf="#+id/dish_fragment_navigation_icon"
app:layout_constraintStart_toEndOf="#+id/dish_fragment_navigation_icon"
app:layout_constraintTop_toTopOf="#id/dish_fragment_navigation_icon"
android:scaleX="0.70"
android:scaleY="0.70"/>
...
</ConstraintSet>
Result
The solution above changes the size but not the layout params, which means it breaks the constraints in a way that I can't align it correctly with the navigation icon.
I prefer to keep using MotionLayout because creating a smooth and detailed animation using CollapsingToolbar is a nightmare.
I used Trial 2 - 'scale' method and set the following textview attributes in the actual XML layout (NOT on the MotionScene XML).
android:transformPivotX="0sp"
android:transformPivotY= Equal to 1/2 of the size of the text view in expanded mode (in sp).
This changes the pivot point around which the text view will scale.
I struggle with creating more universal motion layout scenes. Every tutorial example I have seen so far considers using app:constraintSetStart="#+id/constraintSet1"
app:constraintSetEnd="#+id/constraintSet2"
Here is my motion scene xml, maybe some attributes are wrongly declared.
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition android:id="#+id/title1_motion"
app:duration="1000"
app:constraintSetEnd="#+id/title1_down">
<onClick
app:clickAction="toggle"
app:targetId="#id/title1_down" />
</Transition>
<ConstraintSet android:id="#+id/title1_down">
<Constraint android:id="#+id/textView1">
<Layout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
app:layout_constraintEnd_toStartOf="#+id/textView2"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<Transform android:translationY="50dp" />
</Constraint>
</ConstraintSet>
</MotionScene>
What I achieved for now is a smiple mechanism which tracks current constraintSetId with MotionLayout.TransitionListener. Based on the last motion layout transition it is possible to use
transitionToState(int id) followed by transitionToEnd() with the ID from transition listener.
The effect of transitioning from any constraintSet is achieved. Is doing this programmatically the only possible way?
I'm developing a webrtc for android application. Everything works as expected regarding the webrtc connection but now I'm animating the SurfaceViewRenderer so the remote video transitions from filling the entire screen to be a small rectangle in the upper right corner and vice versa (going from small to large).
I'm using a MotionLayout for achieving this, along with a MotionScene with several states defining the SurfaceViewRenderer properties to change over time to get a nice and smooth resizing effect.
The Views themselves resize properly over time, but the video playback stops during the animation so, when going from small to large, the last video frame remains small while the view increases its size. And at the end of the animation, when the video playback resumes, a new big video frame appears and fills the view. So instead of a smooth resizing effect from small to large I end up getting more like a skip effect, where the small video playback stops and then after a few milliseconds it resumes playback and gets larger.
What I've tried
To delve into the classes involved (SurfaceViewRenderer, VideoRenderer, VideoTrack) to find a way to get the current video frame and resize it, but found no way of doing so
Steps to reproduce
It'll be hard to reproduce the exact scenario since you'll need to set up a webrtc environment all along with signaling server, but I'll share the pieces of code I'm using. My main layout looks like this:
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:orientation="vertical"
app:layoutDescription="#xml/motion_master_view_scene"
android:id="#+id/webRtcViewsLayout">
<org.webrtc.SurfaceViewRenderer
android:id="#+id/slaveVideoView"
android:layout_width="300dp"
android:layout_height="169dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginEnd="84dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<org.webrtc.SurfaceViewRenderer
android:id="#+id/masterVideoView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.motion.MotionLayout>
Then my motion_master_view_scene.xml MotionScene looks like this:
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:motion="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
motion:duration="500">
</Transition>
<StateSet
motion:defaultState="#id/localMasterState">
<State android:id="#+id/bothMinimizedState" motion:constraints="#id/bothMinimizedConstraintSet"></State>
<State android:id="#+id/localMasterState" motion:constraints="#id/localMasterConstraintSet"></State>
</StateSet>
<ConstraintSet android:id="#+id/bothMinimizedConstraintSet">
<Constraint
android:id="#+id/masterVideoView"
android:layout_width="300dp"
android:layout_height="169dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginTop="196dp"
android:layout_marginRight="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<Constraint
android:id="#+id/slaveVideoView"
android:layout_width="300dp"
android:layout_height="169dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</ConstraintSet>
<ConstraintSet android:id="#+id/localMasterConstraintSet">
<Constraint
android:id="#+id/masterVideoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<Constraint
android:id="#+id/slaveVideoView"
android:layout_width="300dp"
android:layout_height="169dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</ConstraintSet>
</MotionScene>
And finally, I change between states in my MainActivity by doing this
webRtcViewsLayout.transitionToState(newState)
Any clues on how to solve this situation?
I'm playing around with the MotionLayout in Android. I'm using the alpha 2 version.
'com.android.support.constraint:constraint-layout:2.0.0-alpha2'
I want to react to two different button clicks and trigger an animation for each of them. My current approach is to set two Transitions in the MotionScene with an OnClick trigger in each one.
The problem is that only the first transition seems to be found. For the second one just nothing happens. Am I doing something wrong or can you just set one transition per MotionScene? If that's the case ist there a different solution to the problem?
Here are the important parts of my Motion Scene
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetStart="#id/startHome"
motion:constraintSetEnd="#id/endHome"
motion:duration="300">
<OnClick
motion:mode="toggle"
motion:target="#+id/imageView_bottom_home" />
</Transition>
<Transition
motion:constraintSetStart="#id/startSearch"
motion:constraintSetEnd="#id/endSearch"
motion:duration="300">
<OnClick
motion:mode="toggle"
motion:target="#+id/imageView_bottom_search" />
</Transition>
<ConstraintSet android:id="#+id/startSearch">
<Constraint
android:id="#id/imageView_bottom_search"
...startConstraints... />
</ConstraintSet>
<ConstraintSet android:id="#+id/endSearch">
<Constraint
android:id="#id/imageView_bottom_search"
...endConstraints... />
</ConstraintSet>
<ConstraintSet android:id="#+id/startHome">
<Constraint
android:id="#id/imageView_bottom_home"
...startConstraints... />
</ConstraintSet>
<ConstraintSet android:id="#+id/endHome">
<Constraint
android:id="#id/imageView_bottom_home"
...endConstraints... />
</ConstraintSet>
Any help appreciated.
Best regards
I had the same problem. The solution I found was to select which one transitions:
(in java code)...
MotionLayout motionConteiner = findViewById(R.id.motion_container);
button1.setOnClickListener((v) -> {
motionConteiner.setTransition(R.id.start1, R.id.end1);
motionConteiner.transitionToEnd();//
});
button2.setOnClickListener((v) -> {
motionConteiner.setTransition(R.id.start2, R.id.end2);
motionConteiner.transitionToEnd();//
});
A more kotlin'y answer:
with(view as MotionLayout) {
setTransition(R.id.startState, R.id.endState)
transitionToEnd()
}
Multiple transitions are supported.
In the code you shared, you have 4 constraint sets, start_home -> end_home, start_search -> end_search. Instead, have only 3 sets, with one being the base state like, start -> end_home and start -> end_search. The 'start' here represents the base state of the screen
This happens because, say you did the home action first and then you did the search action, then the search is not going to work because the starting criteria (start_search) is not going to match the start_home or end_home (which got applied last)
I think aba is right. I am also having problem adding multiple transitions to a single scene file. Theoretically, MotionLayout should support this because every transition will have a separate trigger (often as click or swipe). Maybe this is a bug with MotionLayout that needs to be fixed.
Out of my experience, only the first transition that is encountered in the scene file is cared for.
So, currently, I don't think there is a way to support more than one transition in a layout description (scene). To put it more specifically, all motion needs to be started once with the same trigger.
Seems to me that MotionLayout only supports one Transition, when you add a second Transition to the MotionScene file the second Transition appears to be ignored. You can however have multiple MotionLayouts in your layout and create a MotionScene for each MotionLayout. This will also keep the MotionScene file cleaner and allow for easier maintenance.
In your layout file, you'll need a Parent layout that can contain multiple MotionLayout files.
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
...
</data>
<!-- [databinding] {"msg":"Only one layout element with 1 view child is allowed. So a Parent Layout is required for Multiple motion layouts. -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.motion.widget.MotionLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="#xml/motion_scene_01"
tools:context=".menu.contextual.FragmentContextualOne"
tools:showPath="true">
<Button
android:id="#+id/btn_one"
android:layout_width="64dp"
android:layout_height="64dp"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="310dp" />
</androidx.constraintlayout.motion.widget.MotionLayout>
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="#+id/m2"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="#xml/motion_scene_02">
<Button
android:id="#+id/btn_two"
android:layout_width="64dp"
android:layout_height="64dp"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="500dp" />
</androidx.constraintlayout.motion.widget.MotionLayout>
</FrameLayout>
</layout>
Motion Scene One.
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
android:id="#+id/transition_sine_wave"
motion:constraintSetStart="#+id/wave_start"
motion:constraintSetEnd="#+id/wave_end"
motion:duration="2000"
motion:motionInterpolator="linear">
<OnClick
motion:touchAnchorId="#+id/btn_one"
motion:touchAnchorSide="right"
motion:targetId="#+id/btn_one"/>
</Transition>
<ConstraintSet android:id="#+id/wave_start">
<Constraint
android:id="#+id/btn_one"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintStart_toStartOf="parent"/>
</ConstraintSet>
<ConstraintSet android:id="#+id/wave_end">
<Constraint
android:id="#+id/btn_one"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
</MotionScene>
Motion Scene Two
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
android:id="#+id/transition_straight"
motion:constraintSetEnd="#+id/right_end"
motion:constraintSetStart="#+id/left_start"
motion:duration="2000"
motion:motionInterpolator="linear" >
<OnClick
motion:targetId="#+id/btn_two"
motion:clickAction="toggle"/>
</Transition>
<ConstraintSet android:id="#+id/left_start">
<Constraint
android:id="#+id/btn_two"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="100dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent" />
</ConstraintSet>
<ConstraintSet android:id="#+id/right_end">
<Constraint
android:id="#+id/btn_two"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent" />
</ConstraintSet>
</MotionScene>
This is the only XML solution I could come up with.
I'm trying to show one view in different places depending on which button was clicked.
But MotionScene not working when I'm using two in it.
<MotionScene xmlns:motion="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<Transition android:id="#+id/toTimePicker2"
motion:duration="500" motion:constraintSetStart="#layout/add_event_fragment"
motion:constraintSetEnd="#id/enableTimePicker2">
<OnClick motion:target="#+id/time_to" motion:mode="toggle"/>
</Transition>
<Transition android:id="#+id/toTimePicker1"
motion:constraintSetStart="#layout/add_event_fragment"
motion:constraintSetEnd="#id/enableTimePicker1"
motion:duration="500">
<OnClick motion:target="#+id/time_from"
motion:mode="toggle" />
< /Transition >
Does anyone have an idea how to implement it with motion layout?
Looks like I should use ConstraintSet in code, but I just want to know is this possible with Motion or not
After hours and hours of trying different things, I finally found out why it wasn't working for me. I knew it must be possible because google provides a sample that has multiple transitions https://github.com/googlesamples/android-ConstraintLayoutExamples/blob/multi_state/motionlayout/src/main/res/xml/scene_26.xml. But no matter what I tried, only the first transition in the xml file would work. What I finally realized, is that in order for a transition to work it, the current state of the UI must match the begin or end state of the transition as defined in the constraint set. Not only that, but you can't have the same constraint set defined more than once, meaning if you have two different transitions that can happen on the initial screen, both transitions need to share the constraint set.
I created a simple example of two buttons that move with different translations. I couldn't find a simple example of this anywhere so hopefully this will help some people. As you'll see in the video, when the left to right text has moved to the right, you can't move the top to bottom text to the bottom because that state was not defined in a transition.
Finally, the last gotcha I encountered is every constraint set seems to have to contain every view that is part of a transition, regardless of whether it is in the current transition. Otherwise views just seem to move at random.
Here is the layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/activity_main"
app:layoutDescription="#xml/motion_scene"
tools:context=".MainActivity">
<TextView
android:id="#+id/left_to_right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Left to right"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="#+id/top_to_bottom_text"
android:text="Top to bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
And this is the MotionScene
<?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:constraintSetStart="#id/base"
app:constraintSetEnd="#id/bottom">
<OnClick
app:targetId="#id/top_to_bottom_text">
</OnClick>
</Transition>
<Transition
app:constraintSetStart="#id/base"
app:constraintSetEnd="#id/right">
<OnClick
app:targetId="#id/left_to_right_text">
</OnClick>
</Transition>
<ConstraintSet android:id="#+id/base">
<Constraint
android:id="#id/left_to_right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Constraint
android:id="#id/top_to_bottom_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</ConstraintSet>
<ConstraintSet android:id="#+id/bottom">
<Constraint
android:id="#id/left_to_right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Constraint
android:id="#id/top_to_bottom_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</ConstraintSet>
<ConstraintSet android:id="#+id/right">
<Constraint
android:id="#id/left_to_right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Constraint
android:id="#id/top_to_bottom_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</ConstraintSet>
</MotionScene>
Google makes this comment on their Google Developers medium blog:
Note: It is possible to define multiple ConstraintSets within a MotionScene, so if you have a multi-step motion where such steps are valid “resting” state, you can use them instead of keyframes. Transitioning state to state would have to be done in code (change listeners are available).
So I was able to do it by creating a listener:
motion_layout.setTransitionListener(
object : MotionLayout.TransitionListener {
override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
}
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
if (currentId == R.id.two) {
//if the 1st transition has been made go to the second one
intro_motion_layout.setTransition(R.id.two, R.id.three)
intro_motion_layout.transitionToEnd()
}
}
}
)
intro_motion_layout.transitionToEnd()
And then in my MotionScene I had three transitions:
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetStart="#+id/one"
motion:constraintSetEnd="#+id/two"
motion:duration="200"
motion:interpolator="linear" />
<ConstraintSet android:id="#+id/one">
<Constraint />
</ConstraintSet>
<ConstraintSet android:id="#+id/two">
<Constraint />
</ConstraintSet>
<ConstraintSet android:id="#+id/three">
<Constraint />
</ConstraintSet>
I faced a similar issue wherein I had defined 2 transitions:
A -> B (will refer as transition X)
B -> C (will refer as transition Y)
both triggered on clicking the animated view.
It was the order of transitions in the motion scene file that mattered in my case. Whichever transition was defined first would get executed first. So, if transition Y was defined above transition X, then only B -> C -> B transitions were possible.
However, on placing transition X above transition Y in the motion scene file, following transitions became possible - A -> B -> C -> B.
Note: A -> B could still only be performed once.