Android 尝试随时间设置值时视图不会更新

Android view doesn't update when trying to set values over time

我得到了一个片段,它有一个名为 RingProgress 的控件,它只是一个根据给定的百分比值填充自身的环。例如,如果我这样做:

ringProgress.setProgress(20);

这意味着现在将填充 20% 的环。

我想要做的是在几秒钟内为正在填充的圆环制作动画。所以我尝试做的是:

@Override
public void onResume()
{
    super.onResume();
    HandlerThread handlerThread = new HandlerThread("countdown");
    handlerThread.start();
    Handler handler = new Handler(handlerThread.getLooper());
    handler.post(new Runnable()
    {
        @Override
        public void run()
        {
            final Timer timer = new Timer();
            timer.scheduleAtFixedRate(new TimerTask()
            {
                int totalSeconds = secondsToStart + minutesToStart * 60;
                int secondsPassed = 0;

                @Override
                public void run()
                {
                    if(secondsPassed == totalSeconds)
                    {
                        timer.cancel();
                    }
                    final int currentProgress = (secondsPassed / totalSeconds) * 100;
                    secondsPassed++;
                    getActivity().runOnUiThread(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            mRingProgressBar.setProgress(currentProgress);
                        }
                    });

                }
            }, 0, 1000);

        }
    });
}

问题是直到时间到了才显示环的更新。例如,如果我将它设置为 5 秒,那么当片段加载时,环设置为 0,然后 5 秒内没有任何反应,然后环立即充满 100%..

如何正确启动这个动画?

我想问题出在

final int currentProgress = (secondsPassed / totalSeconds) * 100;

secondsPassed / totalSeconds return int 值,因此它只会是 0 或 1。然后乘以 100。

您必须改用 floatdouble

类似

final int currentProgress = Math.round(((float) secondsPassed)/((float) totalSeconds)*100f);

您可以使用 属性 动画器来代替处理程序,如下所示:

ObjectAnimator.ofInt(mRingProgressBar, "progress", 0, 100)
    .setDuration(totalSeconds * 1000)   //time is in miliseconds
    .start();

这将在您的 mRingProgressBar 中找到方法 setProgress() 并根据给定的限制设置值。在上面的示例中,0 到 100。 您可以阅读更多相关信息 here

这一行:

Handler handler = new Handler(handlerThread.getLooper());

您正在尝试从 handlerThread 中获取活套。但是你怎么确定循环器已经初始化了?

来自 getLooper()

的文档

This method returns the Looper associated with this thread. If this thread not been started or for any reason is isAlive() returns false, this method will return null. If this thread has been started, this method will block until the looper has been initialized.

onLooperPrepared() 是回调,你可以确定 Looper 已经初始化,因此你可以在上面构造逻辑。

因此,您需要做的是子class HandlerThread 并从onLooperPrepared() 回调中创建适当的逻辑。

Here's a nice post 这将帮助你。请参阅此处 MyWorkerThread class 的实现。

因为你想 运行 在不同的线程上,你可以在 class 的顶部使用这个处理程序:

     private int progress = 0;
     Handler timerHandler = new Handler();
     Runnable timerRunnable = new Runnable() {
            @Override
            public void run() {

            ringProgress.setProgress(progress);
            progress += 20;
            if (progress == 100) { //clear??
            }
            timerHandler.postDelayed(this, 1000);
        }
    };

inCreate 中设置最大值:

ringProgress.setMax(100);

这将在5秒内完成动画,然后您可以清除动画。如果您想要更小的增量,请更改下面的行(每十分之一秒更新一次),然后更改步骤

timerHandler.postDelayed(this, 100);