Loading...

"The solution often turns out more beautiful than the puzzle."

Arushi Pant

Always eager to learn. Determined to make the best of the time I've got on Earth.

Custom loading icon using AnimatedVectorDrawable - Path Tracing

Posted on July 3, 2018

This post is as part of my learning for Project #3 of my Google-Udacity Android Developer Nanodegree[1] . It continues from my last post on Custom loading icons using Android Vector Drawables as the basics will not be repeated here.

In this post we look at the concept of path tracing. I learnt this concept by going through Alex Lockwood's git repository files for Google IO 2016. If you get the gist of path tracing, you can use the concept with any shape. Here, I worked with a circle.

So, let's dive in.

 

1. Create a Vector Drawable XML file

We create an XML file, and start with the following code

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:name="circle_1"
        android:strokeColor="#3F51B5"
        android:strokeWidth="2"
        android:pathData="@string/path_circle" />
</vector>

 

The path_circle string here is defined in strings.xml as -

<string name="path_circle">M 1,10
                              C 1,4.8 4.8,1 10,1
                              C 14.8,1 19,4.8 19,10
                              C 19,14.8 14.8,19 10,19
                              C 4.8,19 1,14.8 1,10</string>

When you look in the Preview pane right now, you will see a circle of the defined colour (blue for this code).

Now, let us add two more circles of 2 other colours, so that all three overlap.

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:name="circle_1"
        android:strokeColor="#3F51B5"
        android:strokeWidth="2"
        android:pathData="@string/path_circle" />
    <path
        android:name="circle_2"
        android:strokeColor="#FF4081"
        android:strokeWidth="2"
        android:pathData="@string/path_circle" />
    <path
        android:name="circle_3"
        android:strokeColor="#7CB342"
        android:strokeWidth="2"
        android:pathData="@string/path_circle" />
</vector>

 

As all the three circles are overlapping, we will only see the last circle (green colour) in the Preview pane now.

What we want to show is a part of each of the three circles to make one complete circle. Something, like this -

 

We can achieve this using the trimPathStart and the trimPathEnd properties. These define the start and end points of the segment to be displayed out of the whole path. A 0 value suggests the start of the defined path, and a 1 value suggests the end point. Using these 2 properties, we will define segments of approx one-third the length of the circle each.

Our vector drawable is now ready -

 

2. Create an Animator XML

We create a new XML file in the res/animator folder called trace.xml.

This animation plays on the property trimPathOffset. While trimPathStart and trimPathEnd define where the visible segment starts and ends, trimPathOffset actually resets the point from where we can calculate our 0 value for defining the other two properties.

When trimPathOffset is 0, defining the trimPathStart value as 0 meant we start the segment at 0 (the beginning of the path). When trimPathOffset is 0.4, our path starts from 0.4, and therefore, a trimPathStart value of 0 would mean that we start the segment at 0.4.

Applying the knowledge of this property, we define an animation where the trimPathOffset increases from 0 to 1, and is repeated infinitely. This will give an illusion of rotation when we check it later in the front-end.

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- https://github.com/alexjlockwood/adp-delightful-details/blob/master/app/src/main/res/animator/handwriting_io16_offset_trim_one.xml -->
    <objectAnimator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="4000"
        android:interpolator="@android:interpolator/linear"
        android:propertyName="trimPathOffset"
        android:repeatCount="infinite"
        android:repeatMode="restart"
        android:valueFrom="0"
        android:valueTo="1"/>
</set>

 

3. Create an AnimatedVectorDrawable XML file

We come to the third step where we will tie up our animation with our vector drawable.

<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/av_round" >

    <target
        android:name="circle_1"
        android:animation="@animator/trace" />

    <target
        android:name="circle_2"
        android:animation="@animator/trace" />

    <target
        android:name="circle_3"
        android:animation="@animator/trace" />
</animated-vector>

 

4. Add ImageView for loader to layout

To show our loader in the Activity, we add an ImageView to our layout. We can directly define the AVD as the background for our ImageView here.

Unlike the case in our post 2, we do not need to add a separate code to make our animation repeat. This is because our animator is simpler here, consisting of just one animation (tracing) and we have defined the repeatCount as "infinite" in the animator xml itself.

<ImageView
   android:id="@+id/pb_round"
   android:layout_width="90dp"
   android:layout_height="90dp"
   android:background="@drawable/avd_animated_round"
   android:contentDescription="@string/desc_round_loader"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintLeft_toLeftOf="parent"
   app:layout_constraintRight_toRightOf="parent"
   app:layout_constraintTop_toTopOf="parent" />

 

5. Show animated loader in Activity

To show our loader, we add the below two lines to the Activity code. 

/* Loader - Circle */
ImageView pbRound = findViewById(R.id.pb_round);
((AnimatedVectorDrawable) pbRound.getBackground()).start();

 

You can now customise the code to hide and show progress with our custom Animated Vector Drawable.

AVDs are just so cool!  😍

Below is a video of the loader icon we just made.

 

 

My code featuring the work for this post, and that for the previous one, is hosted here on Github 

 

P.S. - You may like this library by Alex Lockwood - Introducing Kyrie

It has got some really good animations. (Git link: Kyrie)

 

[1] Referral link - Will give you a Rs. 1000 cashback on your first Nanodegree enrollment


Liked the post?

Show your appreciation, and share with others.