Animate ProgressBar update in Android

2019-01-06 11:52发布

I am using a ProgressBar in my application which I update in onProgressUpdate of an AsyncTask. So far so good.

What I want to do is to animate the progress update, so that it does not just "jump" to the value but smoothly moves to it.

I tried doing so running the following code:

this.runOnUiThread(new Runnable() {

        @Override
        public void run() {
            while (progressBar.getProgress() < progress) {
                progressBar.incrementProgressBy(1);
                progressBar.invalidate();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    });

The problem is that the progress bar does not update its state until it finished its final value (progress variable). All states in between are not displayed on the screen. Calling progressBar.invalidate() didn't help either.

Any ideas? Thanks!

8条回答
SAY GOODBYE
2楼-- · 2019-01-06 12:25

You could try using a handler / runnable instead...

private Handler h = new Handler();
private Runnable myRunnable = new Runnable() {
        public void run() {
            if (progressBar.getProgress() < progress) {
                        progressBar.incrementProgressBy(1);
                        progressBar.invalidate();
            h.postDelayed(myRunnable, 10); //run again after 10 ms
        }
    };

//trigger runnable in your code
h.postDelayed(myRunnable, 10); 

//don't forget to cancel runnable when you reach 100%
h.removeCallbacks(myRunnable);
查看更多
看我几分像从前
3楼-- · 2019-01-06 12:28

EDIT: While my answer works, Eli Konkys answer is better. Use it.

if your thread runs on the UI thread then it must surrender the UI thread to give the views a chance to update. Currently you tell the progress bar "update to 1, update to 2, update to 3" without ever releasing the UI-thread so it actually can update.

The best way to solve this problem is to use Asynctask, it has native methods that runs both on and off the UI thread:

public class MahClass extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... params) {
        while (progressBar.getProgress() < progress) {
            publishProgress();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        progressBar.incrementProgressBy(1);
    }
}

AsyncTask might seem complicated at first, but it is really efficient for many different tasks, or as specified in the Android API:

"AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers."

查看更多
Anthone
4楼-- · 2019-01-06 12:29

A Kotlin way of doing this

import kotlinx.android.synthetic.main.activity.*

progressBar.max = value * 100
progressBar.progress = 0

val progressAnimator = ObjectAnimator.ofInt(progressBar, "progress", progressBar.progress, progressBar.progress + 100)
progressAnimator.setDuration(7000)
progressAnimator.start()
查看更多
劫难
5楼-- · 2019-01-06 12:33

I used android Animation for this:

public class ProgressBarAnimation extends Animation{
    private ProgressBar progressBar;
    private float from;
    private float  to;

    public ProgressBarAnimation(ProgressBar progressBar, float from, float to) {
        super();
        this.progressBar = progressBar;
        this.from = from;
        this.to = to;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        float value = from + (to - from) * interpolatedTime;
        progressBar.setProgress((int) value);
    }

}

and call it like so:

ProgressBarAnimation anim = new ProgressBarAnimation(progress, from, to);
anim.setDuration(1000);
progress.startAnimation(anim);

Note: if from and to value are too low to produce smooth animation just multiply them by a 100 or so. If you do so, don't forget to multiply setMax(..) as well.

查看更多
相关推荐>>
6楼-- · 2019-01-06 12:34

Here is an improved version of @Eli Konky solution:

public class ProgressBarAnimation extends Animation {
    private ProgressBar mProgressBar;
    private int mTo;
    private int mFrom;
    private long mStepDuration;

    /**
     * @param fullDuration - time required to fill progress from 0% to 100%
     */
    public ProgressBarAnimation(ProgressBar progressBar, long fullDuration) {
        super();
        mProgressBar = progressBar;
        mStepDuration = fullDuration / progressBar.getMax();
    }


    public void setProgress(int progress) {
        if (progress < 0) {
            progress = 0;
        }

        if (progress > mProgressBar.getMax()) {
            progress = mProgressBar.getMax();
        }

        mTo = progress;

        mFrom = mProgressBar.getProgress();
        setDuration(Math.abs(mTo - mFrom) * mStepDuration);
        mProgressBar.startAnimation(this);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float value = mFrom + (mTo - mFrom) * interpolatedTime;
        mProgressBar.setProgress((int) value);
    }
}

And usage:

ProgressBarAnimation mProgressAnimation = new ProgressBarAnimation(mProgressBar, 1000);
...

/* Update progress later anywhere in code: */
mProgressAnimation.setProgress(progress);
查看更多
Root(大扎)
7楼-- · 2019-01-06 12:43

I use an ObjectAnimator

private ProgressBar progreso;
private ObjectAnimator progressAnimator;
progreso = (ProgressBar)findViewById(R.id.progressbar1);
progressAnimator = ObjectAnimator.ofFloat(progreso, "progress", 0.0f,1.0f);
progressAnimator.setDuration(7000);
progressAnimator.start();
查看更多
登录 后发表回答