Android中的动画ProgressBar更新

时间:2011-11-07 11:15:31

标签: android animation user-interface progress-bar

我在我的应用程序中使用ProgressBar,我在AsyncTask的onProgressUpdate中更新。到现在为止还挺好。

我想要做的是为进度更新设置动画,这样它不仅可以“跳转”到值,而且可以平滑地移动到它。

我尝试运行以下代码:

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();
                }
            }
        }

    });

问题是进度条在完成其最终值(进度变量)之前不会更新其状态。中间的所有状态都不会显示在屏幕上。 调用progressBar.invalidate()也无济于事。

有什么想法吗?谢谢!

13 个答案:

答案 0 :(得分:142)

我使用了android动画:

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

}

并称之为:

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

注意:如果from和to值太低而无法生成平滑动画,只需将它们相乘100左右即可。如果你这样做,不要忘记将setMax(..)相乘。

答案 1 :(得分:36)

我使用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();

答案 2 :(得分:10)

以下是@Eli Konky解决方案的改进版本:

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

用法:

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

/* Update progress later anywhere in code: */
mProgressAnimation.setProgress(progress);

答案 3 :(得分:7)

最简单的方法,使用ObjectAnimator

ObjectAnimator.ofInt(progressBar, "progress", progressValue)
    .setDuration(300)
    .start();

其中progressValue是0到100范围内的整数(默认情况下上限设置为100,但是您可以使用Progressbar#setMax()方法进行更改)

您还可以通过使用setInterpolator()方法设置不同的插值器来更改值的更改方式。这是不同插值器的可视化效果:https://www.youtube.com/watch?v=6UL7PXdJ6-E

答案 4 :(得分:3)

编辑:虽然我的回答有效,但Eli Konkys的回答更好。使用它。

如果您的线程在UI线程上运行,那么它必须放弃UI线程以使视图有机会更新。目前你告诉进度条“更新到1,更新到2,更新到3”而不释放UI线程,所以它实际上可以更新。

解决此问题的最佳方法是使用Asynctask,它具有在UI线程内外运行的本机方法:

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起初可能看起来很复杂,但它对许多不同的任务非常有效,或者在Android API中指定:

  

“AsyncTask可以正确,方便地使用UI线程。这个类   允许执行后台操作并在UI上发布结果   线程,而不必操纵线程和/或处理程序。“

答案 5 :(得分:2)

您可以尝试使用处理程序/ runnable而不是......

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

答案 6 :(得分:2)

一种Kotlin的方式

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()

答案 7 :(得分:2)

ProgressBar().setProgress(int progress, boolean animate)

Android已经为您解决了

答案 8 :(得分:2)

简单的Kotlin解决方案

if (newValue != currentValue) {
    ObjectAnimator.ofInt(bar, "progress", currentValue, newValue)
        .setDuration(500L) // ms
        .start()
}

更简单:

ObjectAnimator.ofInt(bar, "progress", currentValue, newValue).apply {
    duration = 500L
    start()
}

答案 9 :(得分:2)

我只是想为那些希望将数据绑定与进度条动画一起使用的人增加额外的价值。

首先创建以下绑定适配器:

@BindingAdapter("animatedProgress")
fun setCustomProgressBar(progressBar: ProgressBar, progress: Int) {
    ObjectAnimator.ofInt(progressBar, "progress", progressBar.progress, progress).apply {
        duration = 500
        interpolator = DecelerateInterpolator()
    }.start()
}

然后在包含报告状态更新的ViewModel的布局中使用它:

<ProgressBar
    android:id="@+id/progress_bar_horizontal"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="0dp"
    android:layout_height="30dp"
    android:layout_marginStart="32dp"
    android:layout_marginEnd="32dp"
    android:indeterminate="false"
    android:max="100"
    app:animatedProgress="@{viewModel.progress ?? 0}"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

ViewModel本身将使用以下LiveData报告状态:

private val _progress = MutableLiveData<Int?>(null)
val progress: LiveData<Int?>
    get() = _

答案 10 :(得分:1)

这是a.ch的改进版本。解决方案,您还可以使用循环ProgressBar旋转。有时需要设置不断的进度并仅改变旋转,甚至改变进度和旋转。也可以强制顺时针或逆时针旋转。我希望它会有所帮助。

public class ProgressBarAnimation extends Animation {
    private ProgressBar progressBar;
    private int progressTo;
    private int progressFrom;
    private float rotationTo;
    private float rotationFrom;
    private long animationDuration;
    private boolean forceClockwiseRotation;
    private boolean forceCounterClockwiseRotation;

    /**
     * Default constructor
     * @param progressBar ProgressBar object
     * @param fullDuration - time required to change progress/rotation
     */
    public ProgressBarAnimation(ProgressBar progressBar, long fullDuration) {
        super();
        this.progressBar = progressBar;
        animationDuration = fullDuration;
        forceClockwiseRotation = false;
        forceCounterClockwiseRotation = false;
    }

    /**
     * Method for forcing clockwise rotation for progress bar
     * Method also disables forcing counter clockwise rotation
     * @param forceClockwiseRotation true if should force clockwise rotation for progress bar
     */
    public void forceClockwiseRotation(boolean forceClockwiseRotation) {
        this.forceClockwiseRotation = forceClockwiseRotation;

        if (forceClockwiseRotation && forceCounterClockwiseRotation) {
            // Can't force counter clockwise and clockwise rotation in the same time
            forceCounterClockwiseRotation = false;
        }
    }

    /**
     * Method for forcing counter clockwise rotation for progress bar
     * Method also disables forcing clockwise rotation
     * @param forceCounterClockwiseRotation true if should force counter clockwise rotation for progress bar
     */
    public void forceCounterClockwiseRotation(boolean forceCounterClockwiseRotation) {
        this.forceCounterClockwiseRotation = forceCounterClockwiseRotation;

        if (forceCounterClockwiseRotation && forceClockwiseRotation) {
            // Can't force counter clockwise and clockwise rotation in the same time
            forceClockwiseRotation = false;
        }
    }

    /**
     * Method for setting new progress and rotation
     * @param progress new progress
     * @param rotation new rotation
     */
    public void setProgressAndRotation(int progress, float rotation) {

        if (progressBar != null) {
            // New progress must be between 0 and max
            if (progress < 0) {
                progress = 0;
            }
            if (progress > progressBar.getMax()) {
                progress = progressBar.getMax();
            }
            progressTo = progress;

            // Rotation value should be between 0 and 360
            rotationTo = rotation % 360;

            // Current rotation value should be between 0 and 360
            if (progressBar.getRotation() < 0) {
                progressBar.setRotation(progressBar.getRotation() + 360);
            }
            progressBar.setRotation(progressBar.getRotation() % 360);

            progressFrom = progressBar.getProgress();
            rotationFrom = progressBar.getRotation();

            // Check for clockwise rotation
            if (forceClockwiseRotation && rotationTo < rotationFrom) {
                rotationTo += 360;
            }

            // Check for counter clockwise rotation
            if (forceCounterClockwiseRotation && rotationTo > rotationFrom) {
                rotationTo -= 360;
            }

            setDuration(animationDuration);
            progressBar.startAnimation(this);
        }
    }

    /**
     * Method for setting only progress for progress bar
     * @param progress new progress
     */
    public void setProgressOnly(int progress) {
        if (progressBar != null) {
            setProgressAndRotation(progress, progressBar.getRotation());
        }
    }

    /**
     * Method for setting only rotation for progress bar
     * @param rotation new rotation
     */
    public void setRotationOnly(float rotation) {
        if (progressBar != null) {
            setProgressAndRotation(progressBar.getProgress(), rotation);
        }
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float progress = progressFrom + (progressTo - progressFrom) * interpolatedTime;
        float rotation = rotationFrom + (rotationTo - rotationFrom) * interpolatedTime;

        // Set new progress and rotation
        if (progressBar != null) {
            progressBar.setProgress((int) progress);
            progressBar.setRotation(rotation);
        }
    }
}

用法:

ProgressBarAnimation progressBarAnimation = new ProgressBarAnimation(progressBar, 1000);

// Example 1
progressBarAnimation.setProgressAndRotation(newProgress, newRotation);

// Example 2
progressBarAnimation.setProgressOnly(newProgress);

// Example 3
progressBarAnimation.setRotationOnly(newRotation);

答案 11 :(得分:0)

我的自定义ProgressBar解决方案。您可以使用属性(在XML布局中使用动画时)指定动画(animationLength)的长度和“平滑度”(animationSmoothness)

AnimatedProgressBar.java

public class AnimatedProgressBar extends ProgressBar {
private static final String TAG = "AnimatedProgressBar";

private static final int BASE_ANIMATION_DURATION = 1000;
private static final int BASE_PROGRESS_SMOOTHNESS = 50;

private int animationDuration = BASE_ANIMATION_DURATION;
private int animationSmoothness = BASE_PROGRESS_SMOOTHNESS;

public AnimatedProgressBar(Context context) {
    super(context);
    init();
}

public AnimatedProgressBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    obtainAnimationAttributes(attrs);
    init();
}

public AnimatedProgressBar(Context context, AttributeSet attrs, int theme) {
    super(context, attrs, theme);
    obtainAnimationAttributes(attrs);
    init();
}

private void obtainAnimationAttributes(AttributeSet attrs) {
    for(int i = 0; i < attrs.getAttributeCount(); i++) {
        String name = attrs.getAttributeName(i);

        if (name.equals("animationDuration")) {
            animationDuration = attrs.getAttributeIntValue(i, BASE_ANIMATION_DURATION);
        } else if (name.equals("animationSmoothness")) {
            animationSmoothness = attrs.getAttributeIntValue(i, BASE_PROGRESS_SMOOTHNESS);
        }
    }
}

private void init() {

}

@Override
public synchronized void setMax(int max) {
    super.setMax(max * animationSmoothness);
}

public void makeProgress(int progress) {
    ObjectAnimator objectAnimator = ObjectAnimator.ofInt(this, "progress", progress * animationSmoothness);
    objectAnimator.setDuration(animationDuration);
    objectAnimator.setInterpolator(new DecelerateInterpolator());
    objectAnimator.start();
}}

values / attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="AnimatedProgressBar">
        <attr name="animationDuration" format="integer" />
        <attr name="animationSmoothness" format="integer" />
    </declare-styleable>
</resources>

答案 12 :(得分:0)

类似于UI线程上的Kotlin

activity?.runOnUiThread {
        ObjectAnimator.ofInt(binding.progressAudio, "progress", currentPosition)
            .setDuration(100)
            .start();
    }