使进度条平滑更新

53

我正在使用进度条 (以条形形式显示)。我希望使用插值器使进度条平滑地增加和减少,但它不起作用。目前我的代码如下:

pb.setInterpolator(main.this, android.R.anim.bounce_interpolator);             
pb.setProgress(pb.getProgress()+10);

我是否在做一些非常错误的事情?


它为什么不工作?你是否收到错误消息或只是黑屏? - BadSkillz
这是我的库链接:My Library Link 如果需要,可以直接使用。 - Masayuki Suda
6个回答

132

插值器必须附加到动画上,而且仅在Honeycomb或更高版本上才能使用:

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

如果您的最低SDK版本是Gingerbread或更低版本,请添加:

@TargetApi(Build.VERSION_CODES.HONEYCOMB) 
// or 
@SuppressLint("NewApi") 

传递给你的函数/类。

我使用了DecelerateInterpolator,但这是可选的,还有其他可能性。


5
尝试使用animation.setInterpolator(new LinearInterpolator());来设置动画插值器。 - offset
2
想要补充一点,如果你想让它从x点移动到y点,只需加入:ObjectAnimator animation = ObjectAnimator.ofInt(seekbar, "progress", now, end); 另外,如果不够平滑,可以尝试使用新的LinearInterpolator()。 - Vulovic Vukasin
请注意,用户可能具有不同的系统动画缩放值,并且setDuration可能不是实时准确的。 - osemec

18

这里提供了一个自包含的解决方案:

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

}

在您的布局中使用 AnimatingProgressBar 替换 ProgressBar

您还可以将类型更改为 AnimatingProgressBar,以利用 setAnimate() 禁用动画(在恢复活动状态时可能很有用)


美丽。但是请注意,这需要最低SDK版本为11。 - Raj
9
我希望任何低于SDK 11的东西都能够消失;支持旧版Android就像支持IE6一样。 - tom91136
很好,只需要在setSecondaryProgress中进行一小处修复: animatorSecondary = ValueAnimator.ofInt(getSecondaryProgress(), secondaryProgress); animatorSecondary.setIntValues(getSecondaryProgress(), secondaryProgress); - awsleiman
这个答案非常有效,因为它使用反向动画重置了进度条的进度,而这正是我在recyclerView中遇到的问题。 - Muhammad Farhan

13

如果每次将进度值增加1(例如从45变为46),则不会看到动画效果。最好每次以100个点的方式更改进度,为此只需将最大值乘以100,每个进度值也乘以100即可。例如:

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

3

我找到了一种方法来实现它,通过使用一个可运行的程序,我能够每秒更新进度条多次,从而产生滑动效果。以下是代码:

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

这里,'track' 跟踪了已经完成的增量数量,而 'increase' 是应该完成的总增量数。我可以从 UI 线程动态增加增量的数量,以获得平滑的效果。该代码仅适用于不需要减少的进度条。

要运行它,只需使用以下代码:

                    mStartTime = System.currentTimeMillis();
                    incrementor.removeCallbacks(SmoothIncrement);
                    if(track!=0) {
                        track -= increase;
                    }
                    incrementor.postDelayed(SmoothIncrement, 0);

1
你能告诉我incrementor是什么类型的对象,以及它如何应用于ProgressBar吗?我也在寻找这个解决方案。谢谢。 - midhunhk

1
我不确定,请检查一下:
pb.setProgress(pb.getProgress() * 100);

1

根据文档,插值器适用于不确定的进度。由于您设置了进度,我认为您打算使用具有值的常规进度。我认为对您来说最好的方法是增加进度的最大值并以较小的增量进行操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接