MotionLayout
is a layout class that extends ConstraintLayout.
MotionLayout
ConstraintLayout
Take for example this subtle animation of a view being scrolled and the profile picture shrinking. Before MotionLayout, this would be a tedious task to complete. We may have needed a CollapsingToolbar and some other custom animation code to ensure the profile picture scales correctly. Now with MotionLayout, this is really easy to achieve with one extra XML file.
In this article, we will be looking at implementing a simple swipe action on a RecyclerView
and how we can achieve the scaling animation with MotionLayout
.
Add MotionLayout as a Gradle Dependency
To get started with MotionLayout, you need to make sure we have the latest version in your build.gradle
file. (Note: MotionLayout is still in alpha at the time of writing)
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
Create your layout XML as per usual
The great part MotionLayout
ConstraintLayout
ConstraintLayout
(ie barriers, chains etc) is applicable to layouts that we build MotionLayout
To get started, open up your editor and change the root element of your layout to use MotionLayout
. Add a RecyclerView
and an ImageView
to your layout. Make sure the RecyclerView
is constrained to the bottom of ImageView
The XML behind the following layout should look similar to this:
<?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:orientation="vertical" android:layout_width="match_parent" app:showPaths="false" app:layoutDescription="@xml/motion_layout_example" android:layout_height="match_parent"> <View android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintStart_toStartOf="parent" android:background="@color/colorPrimary" android:id="@+id/background" app:layout_constraintBottom_toBottomOf="@id/space" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" android:alpha="0" /> <ImageView android:layout_width="140dp" android:layout_height="0dp" android:scaleType="centerCrop" android:id="@+id/imageViewAvatar" android:layout_marginTop="16dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="16dp" app:layout_constraintDimensionRatio="h,1:1" app:srcCompat="@drawable/veruca"/> <TextView android:text="@string/veruca_salt_name" android:layout_width="0dp" android:layout_height="wrap_content" android:id="@+id/textViewName" android:fontFamily="@font/willywonka" app:layout_constraintStart_toEndOf="@+id/imageViewAvatar" android:layout_marginStart="16dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="16dp" android:textAppearance="@style/TextAppearance.AppCompat.Display1" android:layout_marginTop="8dp" app:layout_constraintTop_toTopOf="@+id/imageViewAvatar" android:layout_marginBottom="8dp" app:layout_constraintBottom_toTopOf="@+id/space"/> <androidx.recyclerview.widget.RecyclerView android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp" app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="8dp" app:layout_constraintBottom_toBottomOf="parent" tools:listitem="@layout/list_item_status" android:id="@+id/recyclerViewStatus" android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/imageViewAvatar"> </androidx.recyclerview.widget.RecyclerView> <Space android:layout_width="0dp" android:layout_height="8dp" android:id="@+id/space" app:layout_constraintStart_toEndOf="@+id/imageViewAvatar" app:layout_constraintTop_toBottomOf="@id/imageViewAvatar" app:layout_constraintEnd_toEndOf="@+id/imageViewAvatar" app:layout_constraintStart_toStartOf="@+id/imageViewAvatar"/> </androidx.constraintlayout.motion.widget.MotionLayout>
Create the MotionScene XML
In order to animate this layout we need to describe how views should animate in the layout. To do this, create an XML file in xml
motion_scene.xml
The first thing that we will do, Transition
MotionScene
ConstraintSets
Transition
<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" app:duration="1000"> <OnSwipe app:touchAnchorId="@+id/recyclerViewStatus" app:touchAnchorSide="top" app:dragDirection="dragUp" /> </Transition> <ConstraintSet android:id="@+id/start"> </ConstraintSet> <ConstraintSet android:id="@+id/end"> </ConstraintSet> </MotionScene>
The next step, that can be seen in the code snippet above, is to create OnSwipe
MotionLayout
dragUp
gesture on the touchAnchorId
recyclerViewStatus
dragUp
gesture.
In the code snippet above, you may notice that there are ConstraintSet
MotionScene
MotionLayout
The first view we want to animate is ImageView
RecyclerView
ImageView
40dp
<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" app:duration="1000"> <OnSwipe app:touchAnchorId="@+id/recyclerViewStatus" app:touchAnchorSide="top" app:dragDirection="dragUp" /> </Transition> <ConstraintSet android:id="@+id/start"> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/imageViewAvatar" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginTop="16dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="16dp"> </Constraint> </ConstraintSet> </MotionScene>
Linking the MotionScene to the Layout
In order to link MotionScene
to the Layout, you will need to set the property app:layoutDescription="@xml/motion_layout_example"
to point to your newly created XML file on the root MotionLayout
element.
Running this on RecyclerView
, ImageView
Animating Visibility of a View
Now if you wanted to animate the background to go from invisible to visible, we can just add Property
start
end
<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" app:duration="1000"> <OnSwipe app:touchAnchorId="@+id/recyclerViewStatus" app:touchAnchorSide="top" app:dragDirection="dragUp" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/background"> <PropertySet app:alpha="0"/> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/imageViewAvatar" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginTop="16dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="16dp"> </Constraint> <Constraint android:id="@id/background"> <PropertySet app:alpha="1"/> </Constraint> </ConstraintSet> </MotionScene>
It’s a wrap!
As you can see from the short example above, MotionLayout
MotionLayout
, as this article has just touched the surface. Till next time!
Follow me on Twitter – @riggaroo.