Introduction to MotionLayout (Part II)
preface
This is part II of the series “Introduction to MotionLayout.” Check out Part I before reading! (In Chinese)
In the text, we’ll continue to expose basic MotionLayout features through various examples, including Custom attributes, Image Operations, and keyframes.
Example 03: Custom Attributes
At the end of Part I, we create a MotionLayout (self-contained MotionScene) that references the MotionScene. We can make further use of it to transition other properties.
In fact, the original ConstraintSet encapsulated only layout rules; However, we often need to do something else (such as background colors) for the sake of animation. ConstraintLayout 2.0, ConstraintSet can also store custom attribute state. Check out the animation below. The background color changes as you move.
Previously, you had to deal with this in code. Now you can specify attributes directly through XML:
<Constraint
android:id="@+id/button" .>
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#D81B60"/>
</Constraint>
Copy the code
This is the modified MotionScene file for the animation
<?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:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000"
motion:interpolator="linear">
<OnSwipe
motion:dragDirection="dragRight"
motion:touchAnchorId="@+id/button"
motion:touchAnchorSide="right" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#D81B60" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/button"
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">
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#9999FF" />
</Constraint>
</ConstraintSet>
</MotionScene>
Copy the code
A custom attribute is specified by an attributeName that corresponds to the getter/setter method in the object:
- getter:
getName
(e.g. getBackgroundColor) - setter:
setName
(e.g. setBackgroundColor)
You also need to specify the type of the attribute value:
customColorValue
customIntegerValue
customFloatValue
customStringValue
customDimension
customBoolean
Finally, when we define a custom attribute, you need to define both the start and end ConstraintSet
Example 04: ImageFilter (ImageFilterView)(1/2)
When we work with complex transitions, we often need to do something with the images and animate them. ConstraintLayout2.0 introduces a useful utility class called ImageFilterView (inheritance with AppCompatImageView) to make this easy.
Here is the cross-fade we made between the two images:
First we need to create a MotionLayout file that contains the ImageFilterView.
<?xml version="1.0" encoding="utf-8"? >
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/motionLayout"
app:layoutDescription="@xml/scene_04"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.utils.ImageFilterView
android:id="@+id/image"
android:background="@color/colorAccent"
android:src="@drawable/roard"
app:altSrc="@drawable/hoford"
android:layout_width="64dp"
android:layout_height="64dp"/>
</android.support.constraint.motion.MotionLayout>
Copy the code
The main difference from ImageView is the altSrc property
<android.support.constraint.image.ImageFilterView
android:id="@+id/image"
.
android:src="@drawable/roard"
app:altSrc="@drawable/hoford"/>
Copy the code
Use the corresponding crossfade property in the MotionScene file
<?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:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000"
motion:interpolator="linear">
<OnSwipe
motion:dragDirection="dragRight"
motion:touchAnchorId="@+id/image"
motion:touchAnchorSide="right" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="crossfade"
motion:customFloatValue="0" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginEnd="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="crossfade"
motion:customFloatValue="1" />
</Constraint>
</ConstraintSet>
</MotionScene>
Copy the code
Example 05: ImageFilter (ImageFilterView)(1/2)
ImageFilterView also provides more functionality:
Saturation: 0 = grayscale, 1 = original, 2 = hyper saturated contrast: 4 = Unchanged, 0 = gray, 2 = high warmth: 1 = neutral, 2 = warm (red tint), 0.5 = Cold (blue tint) Crossfade (with app:altSrc)
Here’s another example of how to use filter saturation:
Saturation can be manipulated by simply specifying custom properties:
<CustomAttribute
motion:attributeName="saturation"
motion:customFloatValue="1" />
Copy the code
Here is the MotionLayout file used for this example:
<?xml version="1.0" encoding="utf-8"? >
<android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/motionLayout"
app:layoutDescription="@xml/scene_05"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.utils.ImageFilterView
android:id="@+id/image"
android:src="@drawable/sunset2"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="300dp" />
</android.support.constraint.motion.MotionLayout>
Copy the code
Here is the corresponding Scene file:
<?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/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<OnSwipe
motion:touchAnchorId="@+id/image"
motion:touchAnchorSide="top"
motion:dragDirection="dragUp" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="300dp"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="saturation"
motion:customFloatValue="1" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="300dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent">
<CustomAttribute
motion:attributeName="saturation"
motion:customFloatValue="0" />
</Constraint>
</ConstraintSet>
</MotionScene>
Copy the code
Key frames (Keyframes)
Most of the time, MotionLayout is implemented using ConstraintSets in “resting states.” In this way, we know that the final layout results will adapt to different screen sizes: essentially, a MotionLayout behaves like a typical ConstraintLayout.
The original: The general idea for MotionLayout is that "resting states" are implemented as ConstraintSets. This way, we know that the resulting layouts will correctly adapt to different screen sizes: essentially, MotionLayout will behave like a typical ConstraintLayout.
In some cases, you might want to have an intermediate state — a state to go through, not a state to stay in. Of course you can specify more than two ConstraintSets, but it is better to use Keyframes.
Keyframes can be applied to positions or property values. They basically let you specify changes at a point in time during the transition.
For example, you might want a control to turn red at 25% of the transition point. Or 50 percent of the way through the transition, move up instead.
Example 06: Keyframe (1/2), Coordinates (postion)
There are several ways to set keypositions (pathRelative, deltaRelative, parentRelative), which we will cover in detail in Part 4 of this series.
A brief introduction to position Keyframes, which specify that the position is 25% of the screen height at 50% of the transition.
<Transition .>
<KeyFrameSet>
<KeyPosition
motion:keyPositionType="parentRelative"
motion:percentY="0.25"
motion:framePosition="50"
motion:target="@+id/button"/>
</KeyFrameSet>
</Transition>
Copy the code
The final effect is as follows:
As always, the MotionLayout file is very simple:
<?xml version="1.0" encoding="utf-8"? >
<android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/motionLayout"
app:layoutDescription="@xml/scene_06"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/button"
android:background="@color/colorAccent"
android:layout_width="64dp"
android:layout_height="64dp" />
</android.support.constraint.motion.MotionLayout>
Copy the code
The MotionScene file is very similar to what we saw before, except that we add a KeyPosition element:
<?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/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000"
motion:interpolator="linear">
<OnSwipe
motion:touchAnchorId="@+id/button"
motion:touchAnchorSide="right"
motion:dragDirection="dragRight" />
<KeyFrameSet>
<KeyPosition
motion:keyPositionType="parentRelative"
motion:percentY="0.25"
motion:framePosition="50"
motion:target="@+id/button"/>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#D81B60"/>
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/button"
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">
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#9999FF"/>
</Constraint>
</ConstraintSet>
</MotionScene>
Copy the code
Example 07: Key frames (2/2), attribute
Like position keyframes, you can specify specific attribute values in transitions (using KeyAttribute).
For example, we might need to manipulate the object at 50% of the position by specifying that scaling and rotation should be performed as follows:
This can be done by adding a KeyAttribute element to KeyFrameSet:
<KeyFrameSet>
<KeyAttribute
android:scaleX="2"
android:scaleY="2"
android:rotation="- 45"
motion:framePosition="50"
motion:target="@id/button" />
</KeyFrameSet>
Copy the code
The MotionLayout file is the same as the previous example, except that KeyAttribute is added to the MotionScene file:
<?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/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000"
motion:interpolator="linear">
<OnSwipe
motion:touchAnchorId="@+id/button"
motion:touchAnchorSide="right"
motion:dragDirection="dragRight" />
<KeyFrameSet>
<KeyAttribute
android:scaleX="2"
android:scaleY="2"
android:rotation="- 45"
motion:framePosition="50"
motion:target="@id/button" />
<KeyPosition
motion:keyPositionType="screenRelative"
motion:percentY="0.2"
motion:framePosition="50"
motion:target="@id/button"/>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#D81B60"/>
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/button"
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">
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#9999FF"/>
</Constraint>
</ConstraintSet>
</MotionScene>
Copy the code
conclusion
Chapter 2 introduces the more advanced features of MotionLayout and shows examples of how custom properties and keyframes can be used to create more compelling animations.
You can view the source code for these examples at ConstraintLayout Examples Github Repository.
There’s more in this series:
- Introduction to MotionLayout (part I)
- Custom attributes, image transitions, keyframes (part II)
- Taking advantage of MotionLayout in your existing layouts (CoordinatorLayout, DrawerLayout, ViewPager) (part III)
- All about Keyframes! (part IV)
- MotionLayout as a choreographer of root layout
- Nesting MotionLayout & other Views
- MotionLayout with fragments