【问题标题】:Android view doesn't update when trying to set values over time尝试随着时间的推移设置值时,Android 视图不会更新
【发布时间】:2017-03-02 08:42:03
【问题描述】:

我得到一个片段,它有一个名为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%..

如何正确启动此动画?

【问题讨论】:

  • 您是否应用了日志记录/断点来查看是否调用了所有内容?
  • ringProgress.setProgress(20); 仅当您设置 ringProgress.setMax(100) 时才会为 20%;
  • 是否必须通过处理程序执行?
  • 不,它不是强制性的。它只是我试图在不同的线程中制作动画
  • @dumazy 是的,一切都被称为

标签: android android-animation android-runonuithread


【解决方案1】:

我想问题出在

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

secondsPassed / totalSeconds 返回 int 值,所以它只会是 0 或 1。然后乘以 100。

您必须改用floatdouble

类似

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

【讨论】:

  • 这可以解释为什么进度只在最后更新
  • 也可以做secondsPassed * 100 / totalSeconds,这将导致同样的事情,而不需要强制转换所有数字。此外,如果其中一个值是 float,则不需要转换其他值,这意味着如果您已经拥有 (float) secondsPassed,则不需要 (float) totalSeconds100f
  • @Yonatan Nir,虽然这似乎是确切的问题,但您的代码不可靠,因为处理程序的使用不正确。看我的回答。
  • 这确实是当前实现的问题。我只是竖起大拇指并没有被接受,因为埃德森给我的答案让一切变得不那么复杂
【解决方案2】:

在这一行:

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

您正试图从handlerThread 获取looper。但是你怎么确定looper已经被初始化了?

来自getLooper()的文档

此方法返回与此线程关联的 Looper。如果这个线程没有被启动或者由于任何原因 isAlive() 返回 false,这个方法将返回 null。如果这个线程已经启动,这个方法会一直阻塞,直到looper被初始化。

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

因此,您要做的就是继承HandlerThread 并从onLooperPrepared() 回调创建适当的逻辑。

Here's a nice post 会帮助你。请参阅那里的MyWorkerThread 类的实现。

【讨论】:

  • 虽然这不是这里的问题,但我给了它一个大拇指,因为这是一件很重要的事情
【解决方案3】:

您可以使用属性动画器,而不是使用处理程序,如下所示:

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

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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多