[MUSIC PLAYING]
SEAN MCQUILLAN: Welcome to the fourth MotionLayout MAD Series
Video.
In this video, you'll learn how to build a collapsible toolbar
using MotionLayout.
Now, this is a classic place to use MotionLayout,
as it allows you to build rich animations with almost
no constraints on your design.
Along the way, we'll take a look at how
to drive a custom attribute using MotionLayout.
Now, if you're new to MotionLayout,
be sure to check out the previous episodes first
to learn the basics.
I've already created a screen with MotionLayout
inside a CoordinatorLayout.
There's two constraint sets with some constraints
and key frames already set.
The first thing to note when making a collapsible header
with MotionLayout is to put the MotionLayout inside an app bar
layout that has a fixed height.
Then set the collapsed height as the min height
on the MotionLayout child.
Now, when building a collapsed header with MotionLayout,
it does behave in a strange way.
We want to keep the MotionLayout a fixed
height using Match Parent.
If we click the MotionLayout to show the Motion Editor,
you can see that it uses the full space in expanded mode.
But in collapsed mode, it actually
draws in just the bottom region of the MotionLayout.
Let's switch to the blueprint and see how to handle this.
A good strategy is to use a guideline.
That's a fixed horizontal or vertical line
in a constraint layout that you can constrain other views to.
Here, we've created a collapsed top guideline
that everything is constrained inside when
the MotionLayout is collapsed.
If you've used guidelines before,
you're probably used to fixed positions,
for example, 100 DP from the top or 50%.
You can also set them in code, which
we can use for insets, like camera cutouts.
We'll define another guideline for the system inset
that we'll use in this animation.
And we'll just give it a default value of 80 DP in XML.
So that way, the preview can display it correctly.
The other guideline in this layout
is used for the animation itself.
Expanded bottom is the visual bottom
of the background when we're in expanded mode.
This gives us some area to paint the oversized emoji
in below the apparent visual bottom of the header.
This makes it look like we're drawing outside the view.
To drive the MotionLayout as a collapsible header,
we need to write some code in the activity.
In the AppBarLayout, the OnOffsetChangedListener
will inform us every time the collapsing toolbar should
change its collapsed progress.
With a bit of math, we can convert the coordinates
that it provides into a range between 0F and 1F
that MotionLayout needs.
Then we can tell MotionLayout about the new progress,
jumping right to the value.
This will be called every time the user scrolls.
So they'll see the animation play one frame at a time.
These lines are the real trick to integrating MotionLayout
as a collapsible toolbar.
In this animation, we also need to know
the insets to make sure we don't draw anything important
under the camera cutout.
To make that work, we're going to dynamically change
the guidelines to the insets for the screen at runtime.
First, we have to grab the desired collapsed
height of the toolbar.
We want to try to end up with this much height
after the inset is applied.
Then we add an inset listener to read the system window inset
top and find out how much we should allow the system window
area.
Update the MotionLayout to allow for the new inset
area in its collapsed height by updating min height.
And finally, set the guidelines.
In MotionLayout, you can modify the guidelines
of any constraints set in code by reading the constraints set.
Here, we're reading the collapsed constraint set
and calling set guidelines to update the guideline.
The idea is the collapsed top will
point to the top of the screen.
And inset will always point to the bottom of the inset region.
We can do the same thing for the expanded guideline.
By setting the inset here, the guideline
will animate from expanded to collapsed
in the correct position the entire time.
That's a bit of code to set up.
But it will give us the two guidelines
we need to build our animation.
Before we switch back to the animation,
I want to briefly scroll down and show you
that our background is using a custom image view subclass that
defines three new properties that we can animate via getters
and setters.
Bottom cut, end cut.
And it also defines a property translation progress
for efficiently translating the background image
using a matrix.
Let's hop back to the Motion Editor
and take a look at using these properties from MotionLayout.
Selecting the expanded constraint set,
I can create a custom attribute to constrain these custom
properties.
Select background.
Then scroll down to Custom Attributes.
I've already defined translation progress
since it's needed to make preview work.
Let's add bottom cut and end cut.
In the expanded state, we want bottom cut
to be 120 DP and end cut to be 0.
Click the Plus button and add a new Custom Attribute.
Custom Attributes can be any of these types.
And the only requirement is that the object
exposes a getter and a setter.
For bottom cut, we want to specify a dimension.
Specify the value you want in DP or with a dimension resource.
For this animation, we want 120 DP.
The autocomplete box will show you all of the properties
that MotionLayout can animate and allow you
to autocomplete dimensions.
And do the same for end cut size.
We want to specify an end cut of 0 DP in the expanded state.
Again, click the Plus button.
Select Dimension.
Pick the properties.
Specify dimension in DP.
We'll do the same thing for collapsed constraint sets.
Though we'll skip the dialogue with the magic cut.
One, two, and done.
Before we preview it, let's add a few key frames
to finish the animation.
Select the transition.
And click on the Create button on the top right.
And select Key Attribute.
This will let us modify any attribute during the animation.
We want to hide the bottom cut a bit early.
So, with the background view selected,
add a key attribute at frame position 60.
Scrolling to the bottom of the list,
custom attributes are listed.
Select Custom Bottom Cut.
Click on the diamond for the New Key attribute.
And set the end cut size to 0 DP at frame position 60.
And to make end cut overshoot a bit,
we'll add another key attribute at position 80
and make it oversized to 220 DP.
I'll do this one with a magic cut.
And done.
And if we playback the animation now,
you can see that we're animating the custom
properties on our custom view with MotionLayout.
The circle cuts are completely controlled by MotionLayout
calling setters on my custom view.
There's one more step to finish this animation.
We want the emoji to jump instead of move.
You may have noticed MotionLayout
loves to make straight lines and smooth curves.
In fact, it had to solve a lot of really fun calculus problems
to make all of that work efficiently.
However, sometimes, you just want
things to move in ways that MotionLayout didn't anticipate.
Let's take a look at one way to do it.
We're going to make two key positions one frame
position apart and tell MotionLayout
to complete the entire motion in that one frame position.
Let's get started by creating a new key position
at frame position 35 on Icon.
Set the path to deltaRelative-- that is, relative to the path
it was going to take already.
And set both the x and the y to 0.
This will cause the icon to not move for the first 35 frames.
And do the exact same thing at frame position 36.
But this time, we'll set x and y to 1, which
is all the way along the path.
That means between frame position 35 and 36,
MotionLayout will move the view completely along its path.
Since we also made the view invisible for this frame
in another key frame, it will appear to pop to the user.
This almost works.
But if you look really closely, you'll
notice the motion right before the view disappears.
And this is because MotionLayout is trying to smooth the curve.
It's anticipating the fast motion
before frame position 35.
To fix that, select the key position at frame 35
and add an attribute curveFit set to linear.
This tells MotionLayout to calculate straight lines
instead of curves.
And do the same thing for frame position 36.
If we play the animation again, we'll
see the icon appears to pop out without moving,
then pops back in at its final position.
By using the linear curve fit, MotionLayout
doesn't anticipate the fast movement between 35 and 36.
And that wraps up the MotionLayout MAD Skill Series.
Thanks for watching.
I can't wait to see the animations
you build in your apps.
To learn more about MotionLayout,
check out the Codelab and MotionLayout integration
samples that these videos were based on in the links below.
[MUSIC PLAYING]