-
-
Save cbeyls/133164625e06b16520c1 to your computer and use it in GitHub Desktop.
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); | |
} | |
} | |
} | |
} | |
} |
@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?
@SUPERCILEX SystemClock.uptimeMillis()
is the system native clock (used by Handler
and other classes) and it's guaranteed to be monotonic, unlike System.currentTimeMillis()
.
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
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
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.