DEV Community

Bigyan Thapa
Bigyan Thapa

Posted on

Simple circular progress indicator

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)