FloatingMusicActionButton an AnimatedVectorDrawable implementation

I could watch this all day

Animated Vector Drawable

Ever since API level 21 we got access to VectorDrawable and AnimatedVectorDrawable. Even better since the release of Android Support Library 23.2 we got support for older API’s.

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp"
android:width="64dp"
android:viewportHeight="600"
android:viewportWidth="600" >
<group
android:name="rotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="45.0" >
<path
android:name="v"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
</group>
</vector>
  • The vector-tag defines the width and height of the vector as well as the viewportHeight and -Width.
  • The group-tag can help you to separate different parts of the vector.
    You give it a name that later comes in handy when we start animating the vector.
  • The most important tag path-tag defines your actual shape with the pathData. The pathData defines clip path using the same format as “d” attribute in a SVG’s path data.

The most important thing when we want to morph different shapes into each other is that the paths should have exact same length of commands , and exact same length of parameters for each commands — Google Developers

For example following paths are compatible for morphing

<string name="before">M3,70 l 0,-70 70,70 0,0 -70,70z</string>
<string name="after">M5, 50 l 0, -80 80, 80, 0, 10 -90, 50z</string>

Sketching on an old fashioned paper

In order for our animation to work we started sketching the different points on a 128 by 128 canvas.
Drawing can help a lot here. Don’t be afraid of grabbing a piece of paper and a pencil to improve your spatial awareness.

All the button states in their final state

Play Button Coordinates

You can see our play-button is composed out of 2 triangles. In order to be compatible for morphing to the pause and stop-buttons a fourth point has to be defined on the triangles.
The point is not visible because it’s placed at (100,64) so it overlaps with another point.

play button upper part
(44, 32)
(44, 64)
(100,64)
(100,64)
play button bottom part
(44, 96)
(44, 64)
(100,64)
(100,64)

Pause Button Coordinates

In order to obtain the full effect from play- to pause state. We have to rotate the pause button -90°. Otherwise we get another effect then intended.

wrong
correct
The pause button
(32, 40)
(32, 56)
(96, 56)
(96, 40)
(32  88)
(32, 72)
(96, 72)
(96, 88)

Stop Button Coordinates

For the stop button the same rules apply as with the pause-button so we rotate the object to -90°.

the stop button
(32, 32)
(32, 64)
(96, 64)
(96, 32)
(32, 96)
(32, 64)
(96, 64)
(96, 96)

Android Icon Animator

A few weeks ago while browsing r/androiddev I discovered Android Icon Animator by Roman Nurik. This marvelous tool allows you to just drag and drop any SVG and it will convert the SVG into the pathData.

M XX XX L XX XX L XX XX L XX XX Z

Play to Pause Animation

Let’s use the Android Icon Animator tool to aid us in to making our first animation.

<!-- Play Icon -->
<string name="play_icon_upper_path_data">M 44 32 L 44 64 L 100 64 L 100 64 Z</string>
<string name="play_icon_bottom_path_data">M 44 96 L 44 64 L 100 64 L 100 64 Z</string>
<!-- Pause Icon -->
<string name="pause_icon_upper_path_data">M 32 40 L 32 56 L 96 56 L 96 40 Z</string>
<string name="pause_icon_bottom_path_data">M 32 88 L 32 72 L 96 72 L 96 88 Z</string>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<group
android:name="@string/play_icon_group_parts"
android:pivotX="64"
android:pivotY="64"
android:scaleX="1"
android:scaleY="1">

<group android:name="@string/play_icon_group_top">
<path
android:name="@string/play_icon_top_path_name"
android:pathData="@string/play_icon_upper_path_data"
android:fillColor="@android:color/black"
android:strokeLineCap="butt"
android:strokeLineJoin="miter"
android:strokeMiterLimit="10"/>
</group>
<group android:name="@string/play_icon_group_bottom">
<path
android:name="@string/play_icon_bottom_path_name"
android:pathData="@string/play_icon_bottom_path_data"
android:fillColor="@android:color/black"
android:strokeLineCap="butt"
android:strokeLineJoin="miter"
android:strokeMiterLimit="10"/>
</group>

</group>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/play_icon">

<target
android:name="@string/play_icon_group_parts"
android:animation="@animator/rotate_90_animation"/>

<target
android:name="@string/play_icon_top_path_name"
android:animation="@animator/upper_play_to_pause_animation"/>

<target
android:name="@string/play_icon_bottom_path_name"
android:animation="@animator/bottom_play_to_pause_animation"/>
</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/animation_duration"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="90"
android:valueType="floatType"/>

Custom View

Now our radio player app is able to stream ondemands and live radio.
That means we have two possible states the icon can animate to.
It also means we can animate back to the play icon when a stream is stopped or an ondemand was paused.

<be.rijckaert.tim.animatedvector.FloatingMusicActionButton
android:src="@drawable/play_to_pause_animation"
android:layout_width="wrap_content"
app:backgroundTint="@color/colorAccent"
app:mode="playToPause"
android:layout_height="wrap_content" />
val musicFab= fab as FloatingMusicActionButton
musicFab.setMode(FloatingMusicActionButton.Mode.PLAY_TO_STOP)

--

--

Developer @BNPParibasFortis

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store