From Punched Cards to Prompts
AndroidIntroduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
At Google I/O 2018, MotionLayout
was announced promising to ease difficulty for developers when creating complex animations. Recently, at Android Dev Summit 2019, the Android Studio and MotionLayout
teams showed off a new MotionEditor
tool that aims to even further simplify creating complex animations with MotionLayout
. We are going to explore both during this post.
We are going to work on an animation that transitions a search box for a destination to two search boxes for both destination and origin locations.
To take advantage of the new MotionEditor
features within Android Studio, we will need to download the latest 4.0 Preview
.
Starting from a new “Empty Activity” project, we need to open our app/build.gradle
to add the dependency needed for MotionLayout
:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3' ... }
A MotionLayout
is actually a ConstraintLayout
which allows us to animate layouts between various states.
MotionLayout
We can now open the activity_main.xml
file:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
We can see that with the project template, Android Studio has already generated a ConstraintLayout
which we can use to convert to MotionLayout
. With the layout file open, look for a new set of buttons in the top right of Android Studio and click the Design
button:
With this viewing mode selected, we can now right-click on ConstraintLayout
in the Component Tree
section and click “Convert to MotionLayout”:
Android Studio is going to convert our ConstraintLayout
to MotionLayout
, generate a MotionScene
file (res/xml/activity_main_scene.xml
), and display the new Motion Editor tooling that will let us build our animations.
<?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:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="@xml/activity_main_scene" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.motion.widget.MotionLayout>
Note: the conversion automatically added the attribute app:layoutDescription="@xml/activity_main_scene"
to our MotionLayout
, which links it to our MotionScene
.
For this example, we want to create a swipe-able sheet with two EditText
s and a FloatingActionButton
. Before we start working on the MotionScene
that will coordinate and animate the UI, we need to add the components to our activity_main.xml
(there are String
and Drawable
resources referenced here that you will need to add from this project):
Note: We didn’t add constraints to the views in this layout file. Views that are animated should have their constraints set in the motion scene file instead.
If a view should not be animated, its constraints should be set in the layout XML file. We also did not include any attributes that adjust visibility or opacity. We will see in just a moment how these attributes are controlled and adjusted in the MotionScene
.
Now that we have our UI components added to our activity_main.xml
, we can shift focus to activity_main_scene.xml
where we will work on adding two ConstraintSet
s to define a “start” and “end” of our MotionScene
. Let’s start by visually defining how we would like to organize the UI before and after the animation.
If we open up activity_main_scene.xml
, we can see that when we converted our ConstraintLayout
to a MotionLayout
, Android studio generated a start and end ConstraintSet
for us, but left it empty:
<MotionScene> <Transition motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@id/start" motion:duration="1000"> ... </Transition> <ConstraintSet android:id="@+id/start"> </ConstraintSet> <ConstraintSet android:id="@+id/end"> </ConstraintSet> </MotionScene>
Now we can define our start
:
And end
ConstraintSet
:
One of the first updates we can make to our Transition
is the ability to handle user interaction to start our animation. Fortunately, with MotionLayout
, we can add handling right in the activity_main_scene.xml
with a few lines of code:
<Transition motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@id/start" motion:duration="2000"> <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@id/bottomSheet" motion:touchRegionId="@id/bottomSheet" /> ... </Transition>
By adding OnSwipe
with the defined dragDirection
, touchAnchorId
, and touchRegionId
we will now be able to handle a user swiping on our bottom sheet. Note here that the touchRegionId
allows us to scope the swipe handling to just the bottom sheet View
. This way, users can still interact with the map behind the bottom sheet.
With our Constraint
s defined, we get to tackle the fun part – let’s use the new MotionEditor
to define our KeyFrameSet
within the Transition
. We can open activity_main.xml
again and make sure we have the “Design” tab clicked in the top right corner.
Let’s work on our FloatingActionButton
first. As the bottom sheet expands, we can add KeyFrame
s to animate the button off of the screen to the right:
To achieve this, we are going to add two KeyFrame
s with the MotionEditor
. The first, to adjust the button’s alpha
(notice how it fades) and the second to adjust the position of the button.
Let’s make sure we have our activity_main.xml
open with the Design
button selected. We also want to have the Transition
panel open in the bottom right window. Click the arrow in between the start and end screen if this isn’t showing:
We can then click the icon in the top right of the Transition
panel:
We can then click KeyAttribute
> ID: fabMyLocation
> Position: 20
> Attribute: alpha
:
Let’s repeat the same process with KeyPosition
> ID: fabMyLocation
> Position: 20
> Type: parentRelative
(take a look at this post) for more on position type) > Percent X: 1.2
. After we’ve added both of these KeyFrames, we can take a look at activity_main_scene.xml
and see where these have automatically been added:
<KeyFrameSet> <KeyPosition motion:framePosition="20" motion:keyPositionType="parentRelative" motion:motionTarget="@+id/fabMyLocation" motion:percentX="1.2" /> <KeyAttribute android:alpha="0.0" motion:framePosition="20" motion:motionTarget="@+id/fabMyLocation" /> ... </KeyFrameSet>
Note: we have adjusted the android:alpha
to 0.0
as we want the FloatingActionButton
to be invisible by frame 20 of the Transition
.
At this point, although we haven’t added the KeyFrame
s for our EditText
s, let’s build our app and try it out with just the button animation.
We can now finish the rest of the KeyFrame
s for the origin and destination EditText
s. We want the origin to be hidden until the animation has finished as well as the origin and destination icons. We also want the search icon to fade as it is replaced with the origin and destination icons. Try adding these KeyFrame
s yourself – if you get stuck, reference the finished scene code below.
One great thing about the MotionEditor
is that it allows you to preview and fine-tune your KeyFrame
s without having to rebuild the app each time:
Build the app and check out your finished animation! MotionLayout
now combined with the new MotionEditor
Android Studio tooling makes it much easier to create complex animations for your project.
Introduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Jetpack Compose is a declarative framework for building native Android UI recommended by Google. To simplify and accelerate UI development, the framework turns the...
Big Nerd Ranch is chock-full of incredibly talented people. Today, we’re starting a series, Tell Our BNR Story, where folks within our industry share...