简体   繁体   中英

Android timer updating a textview (UI)

I'm using a timer to create a stop watch. The timer works by increasing a integer value. I want to then display this value in the activity by constantly updating a textview.

Here's my code from the service where I try and update the activity's textview:

protected static void startTimer() {
    isTimerRunning = true; 
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            elapsedTime += 1; //increase every sec
            StopWatch.time.setText(formatIntoHHMMSS(elapsedTime)); //this is the textview
        }
    }, 0, 1000);
}

I got some kind of error about updating the UI in the wrong thread.

How can I adapt my code to accomplish this task of constantly updating the textview?

protected static void startTimer() {
    isTimerRunning = true; 
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            elapsedTime += 1; //increase every sec
            mHandler.obtainMessage(1).sendToTarget();
        }
    }, 0, 1000);
}

public Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        StopWatch.time.setText(formatIntoHHMMSS(elapsedTime)); //this is the textview
    }
};

Above code will work...

Note: Handlers must be created in your main thread so that you can modify UI content.

You should use Handler instead to update UI every X seconds. Here is another question that show an example: Repeat a task with a time delay?

Your approach doesn't work because you are trying to update UI from non-UI thread. This is not allowed.

StopWatch.time.post(new Runnable() {
    StopWatch.time.setText(formatIntoHHMMSS(elapsedTime));
});

this code block is based on Handler but you don't need to create your own Handler instance.

You can use the following utility:

/**
 * Created by Ofek on 19/08/2015.
 */
public class TaskScheduler extends Handler {
    private ArrayMap<Runnable,Runnable> tasks = new ArrayMap<>();
    public void scheduleAtFixedRate(final Runnable task,long delay,final long period) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.run();
                postDelayed(this, period);
            }
        };
        tasks.put(task, runnable);
        postDelayed(runnable, delay);
    }
    public void scheduleAtFixedRate(final Runnable task,final long period) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.run();
                postDelayed(this, period);
            }
        };
        tasks.put(task, runnable);
        runnable.run();
    }
    public void stop(Runnable task) {
        Runnable removed = tasks.remove(task);
        if (removed!=null) removeCallbacks(removed);
    }

}

Then anywhere in code that runs by the UI Thread you can use it simply like this:

TaskScheduler timer = new TaskScheduler();
        timer.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                time.setText(simpleDateFormat.format(GamePlay.instance().getLevelTime()));
            }
        },1000);

you can use Handler .

this code increase a counter every one second and show and update counter value on a textView .

public class MainActivity extends Activity {


    private Handler handler = new Handler();
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        startTimer();
    }


    int i = 0;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            i++;
            textView.setText("counter:" + i);
            startTimer();
        }
    };

    public void startTimer() {
        handler.postDelayed(runnable, 1000);
    }

    public void cancelTimer() {
        handler.removeCallbacks(runnable);
    }

    @Override
    protected void onStop() {
        super.onStop();
        handler.removeCallbacks(runnable);
    }
}

TimerTask implements Runnable, which would make it a thread. You can not update the main UI thread directly from a different thread without some work. One thing you could do is use Async Task to create the timer and publish an update every second that will update the UI.

I'm assuming StopWatch.time is some static or public reference to your TextView. Instead of doing this, you should implement a BroadcastReceiver to communicate between your timer (which runs from a separate thread) and your TextView.

I use this way:

String[] array = {"man", "for", "think"}; 
int j;

Then add more onCreate :

TextView t = findViewById(R.id.textView);

new CountDownTimer(5000,1000) {

    @Override
    public void onTick(long millisUntilFinished) {}

    @Override
    public void onFinish() {
        t.setText("I " + array[j] + " You");
        j++;
        if (j == array.length - 1) j = 0;
        start();
    }
}.start();
 timer.schedule(new TimerTask() {
            @Override
            public void run() {

                //your actions

            }
        },1*1000);//1 sec

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM