-
-
Notifications
You must be signed in to change notification settings - Fork 9
Experiment #4: Expanding an image with MotionLayout
That was my first time using MotionLayout, nothing too complex but a solid way to start. The animation I did consists on expand (to fullscreen) and shrink (to its original size) an ImageView
. Here's the final result:
![](https://github.com/adrielcafe/gwent-wallpapers/raw/master/art/image-expanding.gif)
To achieve this animation, I use a single ImageView
on top of a RecyclerView
. When a wallpaper is selected I manually place the ImageView
on top of the selected wallpaper (with the same size and aspect ratio) and start the expanding animation.
Let's see, step by step, how to implement this animation:
The MotionLayout
must contain a RecyclerView
and an ImageView
:
<androidx.constraintlayout.motion.widget.MotionLayout
app:layoutDescription="@xml/wallpaper_scene">
<androidx.recyclerview.widget.RecyclerView/>
<androidx.appcompat.widget.AppCompatImageView/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Full implementation on section_wallpapers.xml
In the MotionScene
we need to declare two ConstraintSet
s (for the start and end animations) and a Transition
(to specify the ConstraintSet
s, animation duration and other attributes).
The animation should start with the ImageView
invisible. The value of width and height will be defined at runtime (explained in the next step).
The animation should end with the ImageView
visible. The value of width and height must be match_parent
.
<MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@+id/end"/>
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/currentWallpaper">
<PropertySet android:visibility="invisible"/>
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/currentWallpaper"
android:layout_width="match_parent"
android:layout_height="match_parent">
<PropertySet android:visibility="visible"/>
</Constraint>
</ConstraintSet>
</MotionScene>
Full implementation on wallpaper_scene.xml
Let's start by creating an extension property which returns the coordinates of any View
in the window:
val View.windowLocation: PointF
get() {
val (x, y) = IntArray(2)
.also(::getLocationInWindow)
.map(Int::toFloat)
return PointF(x, y)
}
Now we get the coordinates of the selected wallpaper and change the translationX and translationY values of the ImageView
used in the start ConstraintSet
:
val itemWindowLocation = itemBinding.image.windowLocation
sectionBinding.root
.getConstraintSet(R.id.start)
.setTranslation(R.id.currentWallpaper, itemWindowLocation.x, itemWindowLocation.y)
Full implementation on WallpaperExpanderHelper.kt
It's done! We can now control the animation programmatically:
// Expanding
sectionBinding.root.transitionToEnd()
// Shrinking
sectionBinding.root.transitionToStart()
MotionLayout is very powerful and simpler to use than I thought. I'll definitely dive into it from now on.