Share

engineering

3min read

Customized progress bars in Android

Customized progress bars in Android

As an Android developer I’ve struggled with implementing progress bars. They need to be well-scaled, sometimes vertical and usually mapped to percentage value of some progress. Moreover, they are designed by the UI designer and customer expects they will look exactly as in the provided designs.

Recently, I’ve found a pretty simple solution to implement any progress bar your customer may desire, regardless of its direction, shape, gradient fill etc. All you need to have to achieve that is provide two PNGs of progress bar: empty and full.

How does it work?

The magic happens in the Android ClipDrawable class, which is available since the very first API Level 1. From the JavaDoc:

A Drawable that clips another Drawable based on this Drawable’s current level value. You can control how much the child Drawable gets clipped in width and height based on the level, as well as a gravity to control where it is placed in its overall container. Most often used to implement things like progress bars, by increasing the drawable’s level with setLevel().

Generally, you can clip one drawable to another, and configure the overlay level in range from 0 to 10000.

Let’s code!

By introducing a domain Percent class, we can map our current progress to progress bar state

public class Percent {

    public static final Percent PERCENT_0 = new Percent(0);
    public static final Percent PERCENT_25 = new Percent(25);
    public static final Percent PERCENT_50 = new Percent(50);
    public static final Percent PERCENT_75 = new Percent(75);
    public static final Percent PERCENT_100 = new Percent(100);

    private static final BigDecimal DIVISOR_PERCENT = new BigDecimal(100);

    private final int value;

    public Percent(int value) {
        if(value < 0 || value > 100){
            throw new IllegalArgumentException("Percentage value must be in <0;100> range");
        }
        this.value = value;
    }

    public int asIntValue() {
        return value;
    }

    public BigDecimal asBigDecimal() {
        return new BigDecimal(value).divide(DIVISOR_PERCENT);
    }
}

Let’s create custom VerticalProgressBar component which extends standard Android ImageView:

public class VerticalProgressBar extends ImageView {

    private static final BigDecimal MAX = BigDecimal.valueOf(10000);

    public VerticalProgressBar(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        setImageResource(R.drawable.progress_bar);
    }

    public void setCurrentValue(Percent percent){
        int cliDrawableImageLevel = percent.asBigDecimal().multiply(MAX).intValue();
        setImageLevel(cliDrawableImageLevel);
    }
}

Now, the most important part - progress_bar drawable, based on ClipDrawable:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <bitmap android:src="@drawable/pb_empty"/>
    </item>
    <item>
        <clip
                android:clipOrientation="vertical"
                android:gravity="bottom"
                android:drawable="@drawable/pb_full"/>
    </item>
</layer-list>

How to use it? Just bind VerticallProgressBar and call

verticalProgressBar.setCurrentValue(Percent percent)

Advantages:

  • lets you use any UI design for the progress bar
  • only two states (empty and full) are required
  • ability to map your progress value to even more than a percentage range: <0;10000>

Disadvantages:

  • not found so far ;)

A sample open sourced implementation can be found on my GitHubaccount.

Share

Mateusz

Lead Software Engineer

Did you enjoy the read?

If you have any questions, don’t hesitate to ask!

Did you enjoy the read?

If you have any questions, don’t hesitate to ask!