Таймер Android, обновляющий текстовое представление (UI)

Я использую таймер для создания секундомера. Таймер работает, увеличивая целочисленное значение. Затем я хочу отобразить это значение в активности, постоянно обновляя текстовое представление.

Здесь мой код из службы, где я пытаюсь обновить текстовое представление активности:

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

Я получил некоторую ошибку об обновлении пользовательского интерфейса в неправильном потоке.

Как я могу настроить свой код для выполнения этой задачи, постоянно обновляя текстовое представление?

Спасибо.

Ответ 1

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

Над кодом будет работать...

Примечание. Обработчики должны быть созданы в основном потоке, чтобы вы могли изменять содержимое пользовательского интерфейса.

Ответ 2

Вместо обновления Handler следует обновлять интерфейс каждые X секунд. Вот еще один вопрос, который показывает пример: Повторить задачу с задержкой по времени?

Ваш подход не работает, потому что вы пытаетесь обновить пользовательский интерфейс из потока, отличного от UI. Это не разрешено.

Ответ 3

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

этот блок кода основан на Handler, но вам не нужно создавать свой собственный экземпляр Handler.

Ответ 4

TimerTask реализует Runnable, что сделает его потоком. Вы не можете обновлять основной поток пользовательского интерфейса непосредственно из другого потока без какой-либо работы. Одна вещь, которую вы можете сделать, это использовать Async Task для создания таймера и публиковать обновления каждую секунду, которые будут обновлять пользовательский интерфейс.

Ответ 5

Я предполагаю, что StopWatch.time - это статическая или общедоступная ссылка на ваш TextView. Вместо этого вы должны реализовать BroadcastReceiver для связи между вашим таймером (который выполняется из отдельного потока) и вашим TextView.

Ответ 6

Вы можете использовать следующую утилиту:

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

}

Затем в любом месте кода, который запускается в потоке пользовательского интерфейса, вы можете использовать его просто так:

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

Ответ 7

вы можете использовать Handler.

этот код увеличивать счетчик каждую секунду и показывать и обновлять значение счетчика на 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);
    }
}

Ответ 8

 timer.schedule(new TimerTask() {
            @Override
            public void run() {

                //your actions

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