Indeterminate ProgressBars are a useful tool for communicating to our users that an operation is in progress when we cannot predict how long it is likely to take. Previously on Styling Android we’ve covered how to create a backwardly compatible approximation of the material styled horizontal indeterminate ProgressBar but we haven’t looked at the circular form – in this series we’ll create an approximation of the material circular indeterminate ProgressBar which will be backwardly compatible to API 11 (Honeycomb).
We’re edging closer to get our material-like circular indeterminate drawable working, we just need to fine tune our Interpolators to get closer to what the Lollipop+ implementation is doing. The trick for doing that it to use something that I mentioned earlier in the series PathInterpolatorCompat.
PathInterpolatorCompat is part of the part of the v4 support library and we already have this included in our project because the design support library depends on it. It works in much the same way as the PathInterpolator which we looked at back in – it maps an input value of x to its corresponding y value based upon a a path drawn from 0,0, to 1,1. However rather than using SVG path data like PathInterpolator does, it actually uses android.graphics.Path
instead!
So let’s have a look at how we can do this. The SVG path data that we looked at in the Lollipop+ implementation is actually divided in to to parts: a straight line, and a cubic bezier. Path supports both of these so we just need to map thing appropriately:
final class IndeterminateAnimatorFactory { . . . private static Interpolator createStartInterpolator() { Path path = new Path(); path.cubicTo(0.2f, 0f, 0.1f, 1f, 0.5f, 1f); path.lineTo(1f, 1f); return PathInterpolatorCompat.create(path); } . . . private static Interpolator createEndInterpolator() { Path path = new Path(); path.lineTo(0.5f, 0f); path.cubicTo(0.7f, 0f, 0.6f, 1f, 1f, 1f); return PathInterpolatorCompat.create(path); } . . . }
If we actually run that we’ll see that it’s close but not quite what we’re after. Even though we’ve copied the paths used to create the start and end point interpolation what we haven’t taken in to account is the trimPathOffset animation which was used to prevent from the leading edge catching up with the trailing edge. While we could try and factor that in to our Drawable we actually don’t need to if we factor it in to our Paths used for the interpolators instead. The trick is that rather than remaining static during the line phase of the Paths, we actually move slowly instead:
final class IndeterminateAnimatorFactory { . . . private static Interpolator createStartInterpolator() { Path path = new Path(); path.cubicTo(0.3f, 0f, 0.1f, 0.75f, 0.5f, 0.85f); path.lineTo(1f, 1f); return PathInterpolatorCompat.create(path); } . . . private static Interpolator createEndInterpolator() { Path path = new Path(); path.lineTo(0.5f, 0.1f); path.cubicTo(0.7f, 0.15f, 0.6f, 0.75f, 1f, 1f); return PathInterpolatorCompat.create(path); } . . . }
This changes our paths from this:
To this:
If we try it with those tweaks to the Paths then we get pretty close to the Lollipop+ implementation:
It’s pretty difficult to tell the difference although there is a very occasional glitch when the rotation animator finishes and restarts (you can see it towards the end of the video) but it is only occasional so I can live with it.
Just to prove that it truly is backwardly compatible here’s the same thing running on a Genymotion 4.1.1 emulator. Note how the native ProgressBar is rendered as a Holo-styled one:
That concludes our look at creating our own indeterminate progress bar and we’ve met a new friend along the way: PathInterpolator!
The source code for this article is available here.
© 2016, Mark Allison. All rights reserved.
Copyright © 2016 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.
Thanks a lot sir for these awesome tutorials,
its very helpful for us (beginner developers),
i used your ‘IndeterminateDrawable’ class to show ProgressBar, when i show it using dialog.start() then its working fine but when i stop the dialog by dialog.stop() , it stops and gone but it leaves a small spot on screen, a tiny piece of that bar,
i used this in AsynkTask,
please guide me how can i remove that tiny spot ??
i am using your IndeterminateDrawable,
Thanks for this,
now i have no need to worry about putting ProgressDialog in worker thread or AsynkTask to show,
now i simply use your IndeterminateDrawable and put it wherever i want,
just one question, how can i change color of IndeterminateDrawable ???
It loads the accent colour from your theme as documented at http://android-developers.blogspot.co.uk/2014/10/appcompat-v21-material-design-for-pre.html