I've been scratching my head over this for a long time now and searched for an answer without any luck! It seems to be trivial, but as far as I know, it isn't.
I use a listview in my Android application where every item (view) displays a spinning ProgressBar before the content is loaded and displayed (the content is retrieved through http calls and json, so it may take a while to process). The problem is that the spinning progress bars rotates independently of each other, thus creating the effect of a whirling chaos instead of a syncronised row of good-looking loading markers.
I've tried everything i could come up with... not recycling the ProgressBar in getView()... have only one instance of the same ProgressBar... resetting the ProgressBars android:progress whenever the list items gets visible (via onScroll() in the activity)... etc, but since they start to spin in creation time (when getView gets called in the adapter) they will never have the same cycle sync.
Any suggestions are welcome!
EDIT: Now works perfectly! - inserted animation listener call setStartOffset() back to 0 after the first repeat so that it doesn't keep "jumping" randomly.
I found a working solution for this issue, which works by timing the animation to the current system milliseconds. It's a bit of a hack, as it uses reflection to get the mAnimation
field in ProgressBar
. That being said, this field has remained in place in the Android sources since it was created (works up to 4.2).
Create the android.widget.SyncedProgressBar
class, and use it instead of ProgressBar
in your .xml files. It essentially makes the animation start at the beginning of the animation duration. You can also play around with setDuration(500)
to verify that it works (the progress wheel will spin really quickly). Hope this helps!
package android.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import java.lang.reflect.Field;
/**
* @author Oleg Vaskevich
*
*/
public class SyncedProgressBar extends ProgressBar {
public SyncedProgressBar(Context context) {
super(context);
modifyAnimation();
}
public SyncedProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
modifyAnimation();
}
public SyncedProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
modifyAnimation();
}
@Override
public void setVisibility(int v) {
super.setVisibility(v);
modifyAnimation();
}
@SuppressLint("NewApi")
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
modifyAnimation();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
modifyAnimation();
}
@Override
public synchronized void setIndeterminate(boolean indeterminate) {
super.setIndeterminate(indeterminate);
modifyAnimation();
}
public void modifyAnimation() {
Field mAnim;
try {
mAnim = ProgressBar.class.getDeclaredField("mAnimation");
mAnim.setAccessible(true);
AlphaAnimation anim = (AlphaAnimation) mAnim.get(this);
if (anim == null)
return;
// set offset to that animations start at same time
long duration = anim.getDuration();
long timeOffset = System.currentTimeMillis() % duration;
anim.setDuration(duration);
anim.setStartOffset(-timeOffset);
anim.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
animation.setStartOffset(0);
}
@Override
public void onAnimationEnd(Animation animation) {
}
});
} catch (Exception e) {
Log.d("SPB", "that didn't work out...", e);
return;
}
postInvalidate();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With