53

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 is 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?

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Todd Davies
  • 5,484
  • 8
  • 47
  • 71

6 Answers6

132

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.

now
  • 4,772
  • 2
  • 24
  • 26
  • 5
    @HamidrezaHosseinkhani - try `animation.setInterpolator(new LinearInterpolator());` – offset Jun 01 '15 at 11:20
  • 2
    Would like to add something, if you want it to move from point x to point y, just put: ObjectAnimator animation = ObjectAnimator.ofInt(seekbar, "progress", now, end); Also, if it isnt smooth, try new LinearInterpolator() – Vulovic Vukasin Jun 02 '16 at 12:00
  • Be aware that user can have different system animator scale value and setDuration will not be real time accurate. – osemec Mar 15 '18 at 09:04
18

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)

tom91136
  • 8,662
  • 12
  • 58
  • 74
  • Beautiful. Note, however, that this requires minSdkVersion of 11. – Raj Apr 10 '14 at 19:47
  • 9
    I hope that anything < sdk 11 would disappear; supporting old versions of Android is like supporting IE6 – tom91136 Jan 11 '15 at 10:53
  • nice, just a small fix in the setSecondaryProgress : animatorSecondary = ValueAnimator.ofInt(getSecondaryProgress(), secondaryProgress); animatorSecondary.setIntValues(getSecondaryProgress(), secondaryProgress); – awsleiman Apr 06 '17 at 12:19
  • This answer works like a charm as it reset the progress bar progress with reverse animation which is the problem I'm facing in a recyclerView. – Muhammad Farhan Nov 22 '19 at 10:13
13

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();
}
Pavel Kataykin
  • 1,527
  • 15
  • 14
3

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);
Todd Davies
  • 5,484
  • 8
  • 47
  • 71
  • 1
    Can you tell me what type of object is `incrementor` and how this is applied to a ProgressBar. I am also looking for this solution. Thanks – midhunhk Jul 30 '14 at 10:12
1

I am not sure but please check it:

pb.setProgress(pb.getProgress() * 100);
Satan Pandeya
  • 3,747
  • 4
  • 27
  • 53
Nikhil
  • 16,194
  • 20
  • 64
  • 81
1

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.

Alex Gitelman
  • 24,429
  • 7
  • 52
  • 49