CodeMonkey
CodeMonkey

Reputation: 12434

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

I got a fragment which got a control called RingProgress which is simply a ring that fills itself according to a percentage value given. For example, if I do:

ringProgress.setProgress(20);

It means that 20% of the ring will now be filled.

What I'm trying to do is to animate the ring being filled over a few seconds. So what I've tried to do is this:

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

        }
    });
}

The problem is that the update of the ring is not shown until the time is up. For example, if I set it for 5 seconds then when the fragment loads the ring is set to 0, then nothing happens for 5 seconds and then the ring is full with 100% all at once..

How can I start this animation properly?

Upvotes: 0

Views: 254

Answers (4)

Idan
Idan

Reputation: 5450

Since you want to run on a different thread, you can use this handler in the top of the 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);
        }
    };

In inCreate set the max:

ringProgress.setMax(100);

This will complete the animation within 5 seconds, then you can clear the animation. If you want smaller increments, change the line below (update every tenth of a second), and change the steps

timerHandler.postDelayed(this, 100);

Upvotes: 0

Edson Menegatti
Edson Menegatti

Reputation: 4036

Instead of using a handler, you could use a property animator as follows:

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

This will find a method setProgress() in your mRingProgressBarand set the value according to the limits given. In the example above, 0 to 100. You can read more about it here

Upvotes: 1

azizbekian
azizbekian

Reputation: 62189

On this line:

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

You are trying to get the looper from a handlerThread. But how sure you are the looper has already been initialized?

From the documentation of 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() is the callback, where you can be sure, that the Looper has been initialized, and therefore you can construct logics on that.

Thus, what you have to do, is to subclass HandlerThread and create appropriate logics from onLooperPrepared() callback.

Here's a nice post which will help you out. See implementation of MyWorkerThread class there.

Upvotes: 2

Andrey Danilov
Andrey Danilov

Reputation: 6602

I guess the problem is with

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

secondsPassed / totalSeconds return int value so it will be 0 or 1 only. And you multiply it to 100.

You have to use float or double instead

something like

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

Upvotes: 3

Related Questions