Simple Circular ProgressView Animation
When it comes to UI and loading data, there is always a requirement to fetch the data we want to load in the UI. It is either from fetching from remote, or local database. And as we all know, fetching data adds latency more often than not, leaves us with a blank screen until the data is ready.
The challenge now is to actually keep the user engaged and provide some context while the data is loading. Most of the times, our first go-to way of indicating that the app is doing something
is to throw a ProgressView
with a default timeout of 15
seconds or so. In most cases this does the job but this shouldn't always be our go-to way. While there are several ways and recommendations of handling this with more contextual loading screens, placeholders
or Facebook's ShimmerLayout
, one simple way of adding some context to the progress is to display a ProgressView that actually
shows progress, rather than a continuously
spinning for 15 seconds.
Let me introduce a simple way that I implemented. This approach is to display an animated
circular progress view.
It has following pieces:
- Vector drawable for the circle
- Animated-vector drawable for the animation
- Animation trigger
- Actual Animation
Vector drawable for the circle
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28"
>
<path
android:name="circle"
android:pathData="M14,1.5C7.1,1.5 1.5,7.1 1.5,14C1.5,20.9 7.1,26.5 14,26.5C20.9,26.5 26.5,20.9 26.5,14C26.5,7.1 20.9,1.5 14,1.5Z"
android:strokeWidth="3"
android:fillColor="#00000000"
android:strokeColor="#88D445"
android:fillType="evenOdd"
/>
</vector>
Animated-vector drawable for the animation
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:drawable="@drawable/circle"
tools:targetApi="21"
>
<target
android:name="circle"
android:animation="@animator/circle_animation"
/>
</animated-vector>
Animation trigger
From ViewModel
val animationStart = MutableLiveData<Int>()
From Fragment
private fun observeAnimationStart() {
viewModel.animationStart.observe {
activity?.let { act ->
val drawableCircle: AnimatedVectorDrawableCompat? =
AnimatedVectorDrawableCompat.create(act as Context, R.drawable.animated_circle)
animateProgressView(drawableCircle)
}
}
}
Actual Animation
private fun animateProgressView(drawable: AnimatedVectorDrawableCompat) {
binding.animationImageView.background = drawable
val animatable: Animatable2Compat = drawable
animatable.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable?) {
binding.animationImageView.background = null
}
})
animatable.start()
}
This implementation was done for a simple MVVM
architecture. The ViewModel
sould expose the ProgressView show/hide
state via. LiveData
or SingleLiveEvent
which is observed
by the corresponding Fragment
. Now, this fragment is responsile for starting and ending the animation, or when the aimation is done, just assign null
for the drawable.
Please refer to the above code sample to get more information on the implementation. Also, please feel free to add comments if you have come across better ways of implementing this.
Note: Due to backward campatibility, I am using AminatedVectorDrawableCompat
and Animatable2Compat
for drawable. Also, there is no timing. It is simply drawing the path. So, this wouldn't be ideal for long running progress view.
Top comments (0)