Skip to content

Instantly share code, notes, and snippets.

@cbeyls
Last active December 20, 2021 22:16
Show Gist options
  • Star 29 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cbeyls/133164625e06b16520c1 to your computer and use it in GitHub Desktop.
Save cbeyls/133164625e06b16520c1 to your computer and use it in GitHub Desktop.
ContentLoadingProgressBar implemented The Right Way™
package be.digitalia.common.widgets;
import android.content.Context;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ProgressBar;
/**
* 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).
* <p/>
* This version is similar to the support library version but implemented "the right way".
*
* @author Christophe Beyls
*/
public class ContentLoadingProgressBar extends ProgressBar {
private static final long MIN_SHOW_TIME = 500L; // ms
private static final long MIN_DELAY = 500L; // ms
private boolean mIsAttachedToWindow = false;
private boolean mIsShown;
long mStartTime = -1L;
private final Runnable mDelayedHide = new Runnable() {
@Override
public void run() {
setVisibility(View.GONE);
mStartTime = -1L;
}
};
private final Runnable mDelayedShow = new Runnable() {
@Override
public void run() {
mStartTime = SystemClock.uptimeMillis();
setVisibility(View.VISIBLE);
}
};
public ContentLoadingProgressBar(Context context) {
this(context, null, 0);
}
public ContentLoadingProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ContentLoadingProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mIsShown = getVisibility() == View.VISIBLE;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mIsAttachedToWindow = true;
if (mIsShown && (getVisibility() != View.VISIBLE)) {
postDelayed(mDelayedShow, MIN_DELAY);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mIsAttachedToWindow = false;
removeCallbacks(mDelayedHide);
removeCallbacks(mDelayedShow);
if (!mIsShown && mStartTime != -1L) {
setVisibility(View.GONE);
}
mStartTime = -1L;
}
/**
* Hide the progress view if it is visible. The progress view will not be
* hidden until it has been shown for at least a minimum show time. If the
* progress view was not yet visible, cancels showing the progress view.
*/
public void hide() {
if (mIsShown) {
mIsShown = false;
if (mIsAttachedToWindow) {
removeCallbacks(mDelayedShow);
}
long diff = SystemClock.uptimeMillis() - mStartTime;
if (mStartTime == -1L || diff >= MIN_SHOW_TIME) {
// 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);
mStartTime = -1L;
} 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.
postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
}
}
}
/**
* 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() {
if (!mIsShown) {
mIsShown = true;
if (mIsAttachedToWindow) {
removeCallbacks(mDelayedHide);
if (mStartTime == -1L) {
postDelayed(mDelayedShow, MIN_DELAY);
}
}
}
}
}
@kcochibili
Copy link

Hello, can you provide screenshots?

@cbeyls
Copy link
Author

cbeyls commented Jul 22, 2015

It displays exactly the same thing as a standard ProgressBar so I don't see how screenshots could be useful.

@TimSongCoder
Copy link

Why not contribute this to support library? Then developer like me can use it much more conveniently.
Thank you for your work:)

@cbeyls
Copy link
Author

cbeyls commented May 21, 2017

I'm already contributing to the support library, but currently only bug fixes are accepted and this is more than a bug fix, it's a behavior change so I'm not sure I can submit it.

@SUPERCILEX
Copy link

@cbeyls Just curious, why are you using SystemClock.uptimeMillis() instead of System.currentTimeMillis()? The support lib implementation uses System.ctm()... any reason for the change?

@cbeyls
Copy link
Author

cbeyls commented Jan 26, 2018

@SUPERCILEX SystemClock.uptimeMillis() is the system native clock (used by Handler and other classes) and it's guaranteed to be monotonic, unlike System.currentTimeMillis().

@neiljaywarner
Copy link

my apologies for not digging further or if this is supposed to be super obvious, but what makes it the right way? or has the one in the support library been fixed?
thanks

@alexfrostdev
Copy link

my apologies for not digging further or if this is supposed to be super obvious, but what makes it the right way? or has the one in the support library been fixed?
thanks

I dont use this implementation, but looks like it fixed "ContentLoadingProgressBar never shown issue". You call show, than system call onAttachedToWindow() and remove all callbacks at original ContentLoadingProgressBar implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment