Make a ProgressBar update smoothly
I'm using a progress bar (in bar form). I wish to make the bar increase and decrease smoothly using an interpolator, but It's not working. This i开发者_开发百科s what I have at the moment:
pb.setInterpolator(main.this, android.R.anim.bounce_interpolator);
pb.setProgress(pb.getProgress()+10);
Am I doing something really wrong?
The Interpolator has to be attached to an animation and this will work only on Honeycomb or higher:
if(android.os.Build.VERSION.SDK_INT >= 11){
// will update the "progress" propriety of seekbar until it reaches progress
ObjectAnimator animation = ObjectAnimator.ofInt(seekbar, "progress", progress);
animation.setDuration(500); // 0.5 second
animation.setInterpolator(new DecelerateInterpolator());
animation.start();
}
else
seekbar.setProgress(progress); // no animation on Gingerbread or lower
If your minimum SDK is Gingerbread or lower, add:
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
// or
@SuppressLint("NewApi")
to your function/class.
I used a DecelerateInterpolator, but this is optional and there are others possibilities.
Here is a self-contained drop in solution:
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.util.AttributeSet;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.ProgressBar;
public class AnimatingProgressBar extends ProgressBar {
private static final Interpolator DEFAULT_INTERPOLATER = new AccelerateDecelerateInterpolator();
private ValueAnimator animator;
private ValueAnimator animatorSecondary;
private boolean animate = true;
public AnimatingProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public AnimatingProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AnimatingProgressBar(Context context) {
super(context);
}
public boolean isAnimate() {
return animate;
}
public void setAnimate(boolean animate) {
this.animate = animate;
}
@Override
public synchronized void setProgress(int progress) {
if (!animate) {
super.setProgress(progress);
return;
}
if (animator != null)
animator.cancel();
if (animator == null) {
animator = ValueAnimator.ofInt(getProgress(), progress);
animator.setInterpolator(DEFAULT_INTERPOLATER);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
AnimatingProgressBar.super.setProgress((Integer) animation.getAnimatedValue());
}
});
} else
animator.setIntValues(getProgress(), progress);
animator.start();
}
@Override
public synchronized void setSecondaryProgress(int secondaryProgress) {
if (!animate) {
super.setSecondaryProgress(secondaryProgress);
return;
}
if (animatorSecondary != null)
animatorSecondary.cancel();
if (animatorSecondary == null) {
animatorSecondary = ValueAnimator.ofInt(getProgress(), secondaryProgress);
animatorSecondary.setInterpolator(DEFAULT_INTERPOLATER);
animatorSecondary.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
AnimatingProgressBar.super.setSecondaryProgress((Integer) animation
.getAnimatedValue());
}
});
} else
animatorSecondary.setIntValues(getProgress(), secondaryProgress);
animatorSecondary.start();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (animator != null)
animator.cancel();
if (animatorSecondary != null)
animatorSecondary.cancel();
}
}
replace ProgressBar
with AnimatingProgressBar
in your layout
You many also change the type to AnimatingProgressBar to utilize setAnimate()
to disable animation (could be useful when restoring activity state)
If you change progress value each time by 1 (for example from 45 to 46) you'll not see animation. Better to change progress by 100 points, for this you just need to multiply your max value to 100 and each progress value to 100 too. For example:
private void setProgressMax(ProgressBar pb, int max) {
pb.setMax(max * 100);
}
private void setProgressAnimate(ProgressBar pb, int progressTo)
{
ObjectAnimator animation = ObjectAnimator.ofInt(pb, "progress", pb.getProgress(), progressTo * 100);
animation.setDuration(500);
animation.setInterpolator(new DecelerateInterpolator());
animation.start();
}
I worked out how to do it, by using a runnable I was able to update the progress bar several times a second and so give the sliding effect. The code is below:
private Runnable SmoothIncrement = new Runnable() {
public void run() {
final long start = mStartTime;
long millis = SystemClock.uptimeMillis() - start;
if(track!=increase) {
if((pb.getProgress()==100)&&(count<target)) {
pb.setProgress(0);
}
pb.incrementProgressBy(1);
track++;
incrementor.postAtTime(this, start + millis);
}
else {
incrementor.removeCallbacks(this);
}
}
};
Here, 'track' keeps track of how many increments have been done, and increase is the total number of increments that should be done. I can dynamically increase the number of increments from the UI thread to give a smooth effect. The code only works for progress bars that won't need to decrease.
To run it, simply use this code:
mStartTime = System.currentTimeMillis();
incrementor.removeCallbacks(SmoothIncrement);
if(track!=0) {
track -= increase;
}
incrementor.postDelayed(SmoothIncrement, 0);
According to documentation interpolator applies to indeterminate progress. Since you set progress I think you intend to use regular one with values. I think the best for you would be to increase maximum value of progress and go in smaller increments.
I am not sure but please check it:
pb.setProgress(pb.getProgress() * 100);
精彩评论