In my animation one of the views must grow from fixed width to app:layout_constraintEnd_toEndOf="parent" (which is the value from the destination ConstraintSet) by 70% of animation time.
I tried to achive it with the following KeyAttribute:
<KeyAttribute
motion:motionTarget="#id/my_view"
motion:framePosition="70"
>
<CustomAttribute
motion:attributeName="layout_constraintEnd_toEndOf"
motion:customReference="#id/root"/>
</KeyAttribute>
But it has no effect
I would be grateful for working solution of this problem.
If in any case you are interested in Motion Layout for Constraint layout, then you can refer to this provided link for your animation Motion Layout with Stevdza.
MotionLayout animates Views between two ConstraintSets.
It cannot animate Constraints.
You define KeyPosition
With its Size reaching 100% of the size by frame 70.
Constraints control the start and end.
KeyPosition controls the evolution position & size from "start" to "end"
Attributes typically control the viewTransform
For a brief introduction see the KeyPosition video in the MotionTags Series
This is roughly what it should look like:
<Transition
motion:constraintSetEnd="#+id/end"
motion:constraintSetStart="#id/start"
motion:duration="1000">
<KeyFrameSet>
<KeyPosition
motion:framePosition="70"
motion:motionTarget="#+id/view"
motion:sizePercent="1"/>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="#+id/end"
motion:deriveConstraintsFrom="#id/start" >
<Constraint
android:id="#+id/view"
android:layout_width="40dp"
android:layout_height="wrap_content"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintStart_toStartOf="parent"
/>
</ConstraintSet>
<ConstraintSet android:id="#+id/start">
<Constraint
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintStart_toStartOf="parent"
/>
</ConstraintSet>
I need animation which expands a view from height and width of wrap_contents to match constraints, in my case it is to expand as much as parent allows in width but in 50% animation I want that view to first move to center without modifying its height or width.
In first constraint set i have:
<ConstraintSet android:id="#+id/base">
<Constraint android:id="#+id/backgroundView">
<Layout
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<CustomAttribute
app:attributeName="radius"
app:customDimension="30dp" />
</Constraint>
</ConstraintSet>
And the final one is:
<ConstraintSet
android:id="#+id/final"
app:deriveConstraintsFrom="#id/base">
<Constraint android:id="#+id/backgroundView">
<Layout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="0dp"
android:layout_marginBottom="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<CustomAttribute
app:attributeName="radius"
app:customDimension="12dp" />
</Constraint>
</ConstraintSet>
My transition is:
<Transition
app:constraintSetEnd="#id/final"
app:constraintSetStart="#id/base"
app:duration="300"
app:motionInterpolator="easeInOut">
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:keyPositionType="parentRelative"
app:motionTarget="#id/backgroundView"
app:pathMotionArc="startHorizontal"
app:percentX="0.5"
app:percentY="0.5" />
<KeyAttribute
app:framePosition="50"
app:motionTarget="#id/backgroundView">
<CustomAttribute
app:attributeName="radius"
app:customDimension="30dp" />
</KeyAttribute>
</KeyFrameSet>
</Transition>
With this transition I managed to move the view to center but at the same time view is expanding to match new constraints. How can I keep the view to have width and height of 60dp until frame position goes past 50?
Just add this key position to your keyframe set:
<KeyPosition
app:keyPositionType="deltaRelative"
app:framePosition="50"
app:motionTarget="#id/backgroundView"
app:sizePercent="0" />
By combining keyPositionType="deltaRelative" and sizePercent="0" you define that start size should remain unchanged until this key frame.
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 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.