Android MotionLayout Tutorial – Collapsing View
source link: https://www.tuicool.com/articles/hit/eeMZrer
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
MotionLayout
is a layout class that extends
from
ConstraintLayout.
MotionLayout
has
all the features
of
ConstraintLayout
.
On top of that, it also provides the ability to easily animate changes to your UI, without needing to know much about UI interactions and the Android Animation Frameworks.
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
about
MotionLayout
is
that it uses the same constructs
as
ConstraintLayout
.
Everything you’ve previously learnt about ConstraintLayout
(ie barriers, chains etc) is applicable to layouts that we build
with
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
the
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
the
xml
folder
of your application. We will call
it
motion_scene.xml
.
The first thing that we will do,
is define
the
Transition
for
this
MotionScene
.
We set the reference to the start and
end
ConstraintSets
on
the
Transition
object
. We can also set the duration of the 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
the
OnSwipe
declaration
. This indicates to
the
MotionLayout
that
it should monitor the layout for a swipe movement. When the user performs a dragUp
gesture on the
specified
touchAnchorId
,
the MotionScene will start interpolating between the two states defined (start and end). In this case, it is
the
recyclerViewStatus
view
that it will be monitoring for the dragUp
gesture.
In the code snippet above, you may notice that there are
two
ConstraintSet
tags
defined in
the
MotionScene
.
These tags are for the start and end constraints of the view. The beautiful part
about
MotionLayout
is
that it will automatically interpolate between these two states and produce some pretty magical animations. At the moment, we don’t have any constraint changes applied, so let’s change that.
The first view we want to animate is
the
ImageView
.
We want the size of the view to
decrease
as a user scrolls up on
the
RecyclerView
.
To do that, we will add an end Constraint to
the
ImageView
to
adjust the width and height
to
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
the
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
device
, you will observe that as you scroll up on the RecyclerView
,
the
ImageView
decreases
in size.
Animating Visibility of a View
Now if you wanted to animate the background to go from invisible to visible, we can just add
a
Property
animation
to
the
start
and
end
constraints
. The final XML will look as follows:
<?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,
using
MotionLayout
can
decrease the amount of code you need to write in order to achieve delightful animations. In the next few posts, we will be covering more of the features of MotionLayout
, as this article has just touched the surface. Till next time!
Follow me on Twitter – @riggaroo .
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK