Красивый таймер для Android
В разработке приложения, выполняющего что-то периодически по времени, есть несколько важных моментов.
Все большие вычисления нельзя проводить в главном потоке(UI), т.к. пользователь будет сильно страдать от зависаний. Для реализации числового таймера, нам нужно раз в секунду обновлять компонент TextView
Запускать обновление будем по кнопке, также сделаем паузу и сброс отчета.
Вот код разметки главной активности activity_main.xml:
[cce lang=»xml» tab_size=»2″ no_links=»false»]
android:id=»@+id/tv_digital_clock»
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:layout_alignParentTop=»true»
android:layout_centerHorizontal=»true»
android:layout_marginTop=»95dp»
android:text=»00:00″
android:textAppearance=»?android:attr/textAppearanceMedium»
android:textColor=»@android:color/holo_red_dark»
android:textSize=»100sp»/>
На ней у нас присутствуют 3 Button и один TextView.
Чтобы выводить цифры красивым шрифтом, его нужно зашить во внутрь приложения и при запуске установить на нужный элемент.
Мы будем использовать шрифт DS-Digital (TrueType Fonts).
Файл со шрифтом нужно положить в папку assets.
При запуске приложения установим шрифт (Typeface) на элемент tv_digital_clock (TextView).
[cce lang=»java» tab_size=»2″ no_links=»false»]
@Override
protected void onCreate(Bundle savedInstanceState) <
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
digital_clock = (TextView) findViewById(R.id.tv_digital_clock);
tf = Typeface.createFromAsset(getAssets(), «DS-DIGI.TTF»);
digital_clock.setTypeface(tf);
>
Как работать с классом Timer я подсмотрел на этом сайте. Тут все доходчиво разъяснено, хоть и не на русском.
Он по смыслу очень похож на дельфийский, т.е. задаем интервал для выполнения одних и тех же действий.
Если нужно выключить таймер вызываем Timer.cancel().
Вот код кнопки запуска:
[cce lang=»java» tab_size=»2″ no_links=»false»]
public void onStartButtonClick(View v) <
myTimer = new Timer();
myTimer.schedule(new TimerTask() <
@Override
public void run() <
TimerMethod();
>
>, 0, 1000);
>;
Создали новый класс Timer и задали ему расписание (schedule). Первый параметр означает задержку запуска, второй параметр (1000) означает период в миллисекундах, т.е. одна секунда.
Вот метод и дочерний поток:
[cce lang=»java» tab_size=»2″ no_links=»false»]
private void TimerMethod() <
// This method is called directly by the timer
// and runs in the same thread as the timer.
// We call the method that will work with the UI
// through the runOnUiThread method.
this.runOnUiThread(Timer_Tick);
>
private Runnable Timer_Tick = new Runnable() <
public void run() <
mCurrentPeriod++;
String temp = (new SimpleDateFormat(«mm:ss»)).format(new Date(
mCurrentPeriod * 1000));
digital_clock.setText(temp);
// This method runs in the same thread as the UI.
// Do something to the UI thread here
Тут интересен способ получения форматированной сточки обозначающей время и исходного числа секунд.
Мы создаем новый тип Date, на вход подаем количество миллисекунд. Потом применяем форматирование даты при помощи SimpleDateFormat. Нужный шаблон указан как минуты и секунды «mm:ss».
Вот код двух других кнопок:
[cce lang=»java» tab_size=»2″ no_links=»false»]
public void onPauseButtonClick(View v) <
if (myTimer != null)
myTimer.cancel();
>;
public void onResetButtonClick(View v) <
mCurrentPeriod = 0;
if (myTimer != null)
myTimer.cancel();
digital_clock.setText(«00:00»);
>;
А при выходе из приложения мы обязательно его спросим:
[cce lang=»java» tab_size=»2″ no_links=»false»]
public void onBackPressed() <
new AlertDialog.Builder(this)
.setMessage(«Вы действительно хотите покинуть программу?»)
.setCancelable(false)
.setPositiveButton(«Да», new DialogInterface.OnClickListener() <
public void onClick(DialogInterface dialog, int id) <
finish();
>
>).setNegativeButton(«Нет», null).show();
>;
На всякий случай здесь весь класс MainActivity целиком:
[cce lang=»java» tab_size=»2″ no_links=»false»]
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity <
TextView digital_clock;
Typeface tf;
int mCurrentPeriod = 0;
private Timer myTimer;
@Override
protected void onCreate(Bundle savedInstanceState) <
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
digital_clock = (TextView) findViewById(R.id.tv_digital_clock);
tf = Typeface.createFromAsset(getAssets(), «DS-DIGI.TTF»);
digital_clock.setTypeface(tf);
>
public void onBackPressed() <
new AlertDialog.Builder(this)
.setMessage(«Вы действительно хотите покинуть программу?»)
.setCancelable(false)
.setPositiveButton(«Да», new DialogInterface.OnClickListener() <
public void onClick(DialogInterface dialog, int id) <
finish();
>
>).setNegativeButton(«Нет», null).show();
>;
public void onStartButtonClick(View v) <
myTimer = new Timer();
myTimer.schedule(new TimerTask() <
@Override
public void run() <
TimerMethod();
>
>, 0, 1000);
>;
public void onPauseButtonClick(View v) <
if (myTimer != null)
myTimer.cancel();
>;
public void onResetButtonClick(View v) <
mCurrentPeriod = 0;
if (myTimer != null)
myTimer.cancel();
digital_clock.setText(«00:00»);
>;
private void TimerMethod() <
// This method is called directly by the timer
// and runs in the same thread as the timer.
// We call the method that will work with the UI
// through the runOnUiThread method.
this.runOnUiThread(Timer_Tick);
>
private Runnable Timer_Tick = new Runnable() <
public void run() <
mCurrentPeriod++;
String temp = (new SimpleDateFormat(«mm:ss»)).format(new Date(
mCurrentPeriod * 1000));
digital_clock.setText(temp);
// This method runs in the same thread as the UI.
// Do something to the UI thread here
Красивый таймер для Android : 12 комментариев
Здравствуйте! А не могли бы вы в объяснении показать как выглядит базовый шаблон класса таймер? Мне как новичку не все понятно.
Например, обработчик запуска таймера содержит код:
public void onStart(View view) <
mTimer = new Timer();
mTimer.schedule(new TimerTask() <
@Override
public void run() <
TimerMethod();
>
>);
>
я понимаю это базовый шаблон, где мы инициализируем наш таймер,
затем вызываем метод schedule( ), в котором как условие создаем новый метод new TimerTask, в котором в свою очередь переопределяем метод run ( ); и в нем уже запускаем свой код.
В нашем случае этим кодом является метод(?) — TimerMethod ();
В этом методе есть строка:
this.runOnUiThread(Timer_Tick);
Тут я уже начинаю терять суть. хотелось бы пояснить эту строку кода. Судя по названию, это запуск в основном потоке. Ведь таймер работает в своем собственном потоке.
И далее Timer_Tick это переменная интерфейса Runnable, в котором я уже совсем потерялся.
Хотелось бы поподробнее что этот Runnable умеет и как выглядит его базовый шаблон. То есть для чего его вызывают и какие команды в нем обычно используют.
Доступ ко всем элементам интерфейса должен проходить в главном потоке. Потому мы и говорим runOnUiThread, т.е. работай в главном потоке.Вот же все по-русски написано:
// This method is called directly by the timer
// and runs in the same thread as the timer.
// We call the method that will work with the UI
// through the runOnUiThread method.
Этот метод вызывается прямо из таймера и работает в том же потоке что и таймер. А потом нам нужно его переслать в главный поток.
Иными словами, нам нужен поток, который периодически стучится в главный поток с UI, чтобы обновить интерфейс.
Не по русски, а по английски ;))
То есть это шаблонная структура? Уже сам код внутри Runnable > Run ()
Runnable это interface, и у него один абстрактный класс run, который нужно реализовать самому. Вот же пацаны пишут Often used to run code in a different Thread. И без русско-болгарского разговорника все понятно )
Окей) буду пилить этот вариант реализации таймера))
Я прочитал описание Runnable по ссылке из примера, но хотелось бы живой пример и лучше на нашем примере все разобрать))
Очень понятная статья. Спасибо.
Нашел дефект: если нажать еще раз на кнопку старта (что очень вероятно со смартфонами), то создается еще один поток и время начинает идти быстрее.
С потоками побоялся, что-то делать, так что починил путем добавления:
Button startButton = (Button) findViewById(R.id.start_button);
startButton.setClickable(false);
В метод запуска таймера и такую же разблокировку в метод для кнопки паузы.
Решил эту проблему тем, что повесил на кнопку условие с переменными, которые контролируют работу потока, типа если раз нажал, переменную изменил, и если переменная не та, что нужна, то не выполнять код повторного запуска потока)
Вам нужно работу по отсчету времени вынести в отдельный сервис, и из него отправлять широковещательные сообщения в активити.
Не запустилось.
FATAL EXCEPTION: main
01-03 15:12:54.106 1358-1358/? E/AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo
Привет, сделал по вашему примеру таймер, использовал не весь код, кусками и многое переделал под себя. Но нетронутыми оставил работу процесса таймера в пользовательском потоке. Протестировал приложение, и выявил проблему в том, что таймер работает чуть больше одной минуты, в случае если приложение свернуто или экран телефона заблокирован. Полагаю, что пользовательский процесс имеет свойство останавливаться, если устройство не используется. По этой причине у меня возник вопрос, в каком потоке, в таком случае, нужно запускать код таймера, чтобы он работал до тех пор пока время не выйдет? Понимаю, что статья уже достаточно старая, но вдруг случится чудо и мне ответят, не автор, так может кто-то из пользователей кто сталкивался с такой же проблемой)) з.ы. спасибо автору за урок, несколько новых фишек для себя взял на вооружение
Здравствуйте.
Не могли бы вы выложить ссылку на проект ?
Источник
Таймер — классы Timer и TimerTask
Классы Timer и TimerTask из пакета java.util позволяют планировать запуск задания на определённое время в будущем. Вы можете создать поток, выполняющий в фоновом режиме и ожидающий заданное время. Когда время истечёт, задача, связанная с этим потоком, будет запущена. С помощью параметров можно запланировать задачу на повторяющий запуск либо на запуск по определённой дате. Вам не нужно создавать поток с помощью класса Thread, так как таймер упрощает эту задачу.
Учитывайте обстоятельство, что таймер выполняется в своём потоке и не должнен задействовать UI-элементы, которые выполняются в своём потоке. Для решения этой проблемы можете использовать метод runOnUiThread() для обновления данных у компонентов.
Классы Timer и TimerTask работают в связке. Класс Timer используется для планирования выполнения задачи. Запланированная к выполнению задача должна быть экземпляром класса TimerTask. Вы сначала создаёте объект класса TimerTask, а затем планируете его запуск с помощью класса Timer.
Класс TimerTask реализует интерфейс Runnable и может быть использован для создания потока выполнения.
В классе TimerTask имеется абстрактный метод run(), который следует переопределить. Метод должен содержать исполняемый код.
Метод cancel() прерывает задание и возвращает значение true, если выполнение задания прервано.
Метод scheduleExecutionTime() возвращает время, на которое последний раз планировался запуск задания.
Как только задача создана, она планируется для выполнения объектом класса Timer.
Методы класса Timer:
- void cancel() — прерывает поток таймера
- int purge() — удаляет прерванные задания из очереди таймера
- void schedule (TimerTask task, long delay) — задание task планируется к выполнению через период в миллисекундах, переданный в параметре delay
- void schedule (TimerTask task, long delay, long period) — задание task планируется к выполнению через период в миллисекундах, переданный в параметре delay. Затем задание повторяется повторно периодически — каждые period миллисекунд
- void schedule (TimerTask task, Date when) — задание task планируется на время, указанное в параметре when
- void schedule(TimerTask task, Date when, long period) — задание task планируется на время, указанное в параметре when. Затем задание выполняется повторно периодически — каждые period миллисекунд
- void scheduleAtFixedRate (TimerTask task, long delay, long period) — задание task планируется к выполнению через период в миллисекундах, переданный в параметре delay. Затем задание выполняется повторно периодически — каждые period миллисекунд. Время каждого повтора задаётся относительно первого запуска.
- void scheduleAtFixedRate (TimerTask task, Date when, long period) — задание task планируется к выполнению на время, указанное в параметре when. Задание затем выполняется повторно периодически — каждые period миллисекунд. Время каждого повтора задаётся относительно первого запуска.
Между методами schedule() и scheduleAtFixedRate() есть небольшая разница, которая заключается в разном поведении, которое зависит от стартовой точки запуска. Так второй метод работает как startTime + iterationNumber * delayTime и помнит время запуска. А обычный метод schedule() помнит последнее время выполнения и работает по формуле lastExecutionTime + delayTime. Для быстрых операций это не сильно отличается, а при ресурсоёмких задач разница будет заметна, например, при работе сборщика мусора приложение может притормозить и следующая задача может запуститься чуть позже.
Как только объект класса Timer создан, запуск планируется вызовом его метода schedule() и его родственника (см. выше).
Запускаем таймер
Напишем простой пример. Подготовим разметку.
Будем использовать два варианта таймера — одиночное и периодическое срабатывание.
Генерируем случайные показания
Допустим нам нужны ежедневные показания термометра. Но мы не можем ждать весь день при написании программ. Поэтому мы можем случайным образом создавать показания с небольшим интервалом и проверить поведение приложения.
Для удобства создадим отдельный класс-утилиту.
Создадим в классе активности метод для генерации значений и вызовем в onCreate().
Источник