【问题标题】:Animate ProgressBar update in AndroidAndroid 中的动画进度条更新
【发布时间】:2011-12-23 13:19:46
【问题描述】:

我在我的应用程序中使用了一个 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() 也没有帮助。

有什么想法吗?谢谢!

【问题讨论】:

    标签: android animation user-interface progress-bar


    【解决方案1】:

    我为此使用了 android Animation:

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

    【讨论】:

    • @EliKonky。不错的作品。工作正常。我有个问题。我想在进度条达到 100(最大值)后隐藏它。达到最大值后如何设置其可见性?请帮帮我。
    • interpolatedTime 会得到什么值???是否取决于您为 setDuration() 设置的值???
    • @hamid_c 是 interpolatedTime 是传递给 setDuration() 的值
    • @EliKonky 正在寻找一种方法来访问动画回调方法以更新另一个视图,applyTransformation() 拯救了我的一天!非常感谢。
    • 如果有人想要动画二次进度然后更改 ProgressBarAnimation 类 progressBar.setSecondaryProgress((int)value); 而不是 progressBar.setProgress((int) value);
    【解决方案2】:

    我使用 ObjectAnimator

    private ProgressBar progreso;
    private ObjectAnimator progressAnimator;
    progreso = (ProgressBar)findViewById(R.id.progressbar1);
    progressAnimator = ObjectAnimator.ofInt(progreso, "progress", 0,1);
    progressAnimator.setDuration(7000);
    progressAnimator.start();
    

    【讨论】:

    • 应该是 ofInt ;-) 并且可能是 0 到 100 (但你应该在 progreso 上设置 Max(100) 以确保。但除此之外很好。喜欢它比其他答案更好。
    • 比 AsyncTask 好很多
    • 检查my answer是否正确实施
    • 由于某种原因,动画看起来不流畅。
    【解决方案3】:

    最简单的方法,使用ObjectAnimator(Java 和 Kotlin 都可以):

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

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

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

    【讨论】:

    • 在进度条上设置动画的最简单简洁的答案。
    • 最好将 maxProgress 设置为 10000 之类的值,然后 50% 为 5000。这会使动画更流畅。
    【解决方案4】:

    这里是@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);
    

    【讨论】:

      【解决方案5】:
      ProgressBar().setProgress(int progress, boolean animate)
      

      Android 已经为您解决了这个问题

      【讨论】:

      • 你能用这个改变动画的持续时间吗?
      • 在你的 .xml 文件中试试这个@Myk android:animationResolution = "timeInMilliseconds"
      • 这只是 API 24+
      【解决方案6】:

      一种 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】:

        编辑:虽然我的回答有效,但 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 上发布结果 线程,而无需操作线程和/或处理程序。”

        【讨论】:

          【解决方案8】:

          简单的 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】:

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

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

            @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】:

              您可以尝试使用处理程序/可运行...

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

              【讨论】:

                【解决方案11】:

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

                【讨论】:

                  【解决方案12】:

                  在 UI 线程上与 Kotlin 类似

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

                  【讨论】:

                    【解决方案13】:

                    我使用自定义 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();
                    }}
                    

                    值/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>
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2015-01-24
                      • 2023-04-10
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-10-06
                      相关资源
                      最近更新 更多