You’ve probably stumbled upon ContentLoadingProgressBar and don’t even know how it is different from ProgressBar or even when to use it. Taking a look at the android documentation, one would find:

ContentLoadingProgressBar implements a ProgressBar that waits a minimum time to be dismissed before showing. Once visible, the progress bar will be visible for a minimum amount of time to avoid “flashes” in the UI when an event could take a largely variable time to complete (from none, to a user perceivable amount)

From this, we can pick out a few things :

  1. It implements a ProgressBar and can be used in place of a ProgressBar.
  2. Handles its visibility properly to avoid “flashes” in the UI.

Why is Avoiding Flashes on UI important ?

  1. “Flashes” in a UI forfeits the reason of presenting the view. Progressbar is meant to convey information about the progress of an operation to a user. If the opeation happens so fast, there is actually no need to display the ProgressBar. This is what the ContentLoadingProgressBar handles.

  2. Not confusing the user. “Flashes” in UI is quite deceitful. User’s doesn’t seem know what exactly is going on.

Achieving the ContentLoadingProgressBar

To achieve a progressbar marked toward better user experience, the ContentLoadingProgressBarextends ProgressBar and adds show() and hide().This works in a way that if the hide() is called before mDelayedShow is executed, the view is never shown.


	private static final int MIN_SHOW_TIME = 500; // ms
    private static final int MIN_DELAY = 500;
    private final Runnable mDelayedHide = new Runnable() {

        @Override
        public void run() {
            mPostedHide = false;
            mStartTime = -1;
            setVisibility(View.GONE);
        }
    };

    private final Runnable mDelayedShow = new Runnable() {

        @Override
        public void run() {
            mPostedShow = false;
            if (!mDismissed) {
                mStartTime = System.currentTimeMillis();
                setVisibility(View.VISIBLE);
            }
        }
    };
    public void hide() {
        mDismissed = true;
        removeCallbacks(mDelayedShow);
        long diff = System.currentTimeMillis() - mStartTime;
        if (diff >= MIN_SHOW_TIME || mStartTime == -1) {
            // The progress spinner has been shown long enough
            // OR was not shown yet. If it wasn't shown yet,
            // it will just never be shown.
            setVisibility(View.GONE);
        } else {
            // The progress spinner is shown, but not long enough,
            // so put a delayed message in to hide it when its been
            // shown long enough.
            if (!mPostedHide) {
                postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
                mPostedHide = true;
            }
        }
    }

    /**
     * Show the progress view after waiting for a minimum delay. If
     * during that time, hide() is called, the view is never made visible.
     */
    public void show() {
        // Reset the start time.
        mStartTime = -1;
        mDismissed = false;
        removeCallbacks(mDelayedHide);
        if (!mPostedShow) {
            postDelayed(mDelayedShow, MIN_DELAY);
            mPostedShow = true;
        }
    }

Looking at the implementation carefully, you might have noticed a few downsides.

  1. MIN_SHOW_TIME and MIN_DELAY is fixed t0 500ms. It can’t be changed.
  2. There is another downsized that can’t really noticed until one explore ContentLoadingProgressBar.If it ever happens that hide() is called a couple of milliseconds after show() and mDelayedShow has not yet been executed, it will never be visible again on subsequent calls to show().

There is a fix of this available at github.com

What’s in the Fix ?

  1. exposed method to change value of MIN_SHOW_TIME and MIN_DELAY
  2. Fix bug stated in downsize - 2
public void show() {
        // Reset the start time.
        mStartTime = -1;
        mDismissed = false;
        removeCallbacks(mDelayedHide);
        if (shouldResetShown()) {
            mPostedShow = false;
        }
        if (!mPostedShow) {
            lastShownTime = Calendar.getInstance().getTimeInMillis();
            postDelayed(mDelayedShow, MIN_DELAY);
            mPostedShow = true;
        }
    }

    private long lastShownTime = Calendar.getInstance().getTimeInMillis();

    private boolean shouldResetShown() {
        long now = Calendar.getInstance().getTimeInMillis() - lastShownTime;
        return now > MIN_DELAY;
    }

shouldResetShown() helps determine when to reset mPostedShow in cases were hide() was called before mDelayedShow is executed. Taking a look at the video shared earlier, you will see 3 progressBar.

  1. ProgresBar - default progress bar in android
  2. ContentLoadingProgressBar
  3. Custom ContentLoadingProgressBar

Also download demo apk here

The app demonstrates how the 3 progressbar behaves in same scenerio. You would however noticed Custom ContentLoadingProgressBar works best as you change the Duration, MIN_SHOW_TIME and MIN_DELAY.

     protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final ContentLoadingProgressBar loadingProgressBar = (ContentLoadingProgressBar) findViewById(R.id.loading_bar);
        final CustomContentLoadingProgressBar customProgressBar = (CustomContentLoadingProgressBar) findViewById(R.id.custom_loading_bar);
        final ProgressBar progressBar = (ProgressBar) findViewById(R.id.normal_bar);


        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                 // house keeping - collect input from editText
                String duration_text = ((EditText) findViewById(R.id.duration)).getText().toString();
                String minTime_text = ((EditText) findViewById(R.id.mindelayTime)).getText().toString();
                String min_shown_time_text = ((EditText) findViewById(R.id.minShowTime)).getText().toString();
                int duration = Integer.parseInt(duration_text.trim().isEmpty() ? "0" : duration_text);
                int min_delay = Integer.parseInt(minTime_text.trim().isEmpty() ? "0" : minTime_text);
                int min_shown = Integer.parseInt(min_shown_time_text.trim().isEmpty() ? "0" : min_shown_time_text);


                // change values of MIN_SHOW_TIME and MIN_DELAY for CustomContentLoadingProgressBar
                customProgressBar.setMinDelay(min_delay);
                customProgressBar.setMinShowTime(min_shown);

                progressBar.setVisibility(View.VISIBLE);
                loadingProgressBar.show();
                customProgressBar.show();
                // delay for specified duration before hiding the progress bar again
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        progressBar.setVisibility(View.GONE);
                        loadingProgressBar.hide();
                        customProgressBar.hide();
                    }
                }, duration);


            }
        });
    }

Wraping Up

ContentLoadingProgressBar Usecases
  • Using progress bar for operations that might sometimes happen very fast . This is remarkbly the best use case for it that I can find at the moment.

The idea however is endeavour to always use ContentLoadingProgressBar since it implements ProgressBar but has better promises.

You read it this far. This wasn’t boring afterall. You should probably drop a feedback. Thanks

Beliv