开发者

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);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜