Таймер для Андроид? Как это сделать?
может ли кто-нибудь привести простой пример обновления текстового поля каждую секунду или около того?
Я хочу сделать летающий шар и нужно вычислять/обновлять координаты шара каждую секунду, поэтому мне нужен какой-то таймер.
Я ничего не получаю от здесь.
18 ответов:
хорошо, так как это еще не прояснено, есть 3 простых способа справиться с этим. Ниже приведен пример, показывающий все 3, а внизу-пример, показывающий только метод, который я считаю предпочтительным. Также не забудьте очистить ваши задачи в onPause, сохраняя состояние, если это необходимо.
import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Handler.Callback; import android.view.View; import android.widget.Button; import android.widget.TextView; public class main extends Activity { TextView text, text2, text3; long starttime = 0; //this posts a message to the main thread from our timertask //and updates the textfield final Handler h = new Handler(new Callback() { @Override public boolean handleMessage(Message msg) { long millis = System.currentTimeMillis() - starttime; int seconds = (int) (millis / 1000); int minutes = seconds / 60; seconds = seconds % 60; text.setText(String.format("%d:%02d", minutes, seconds)); return false; } }); //runs without timer be reposting self Handler h2 = new Handler(); Runnable run = new Runnable() { @Override public void run() { long millis = System.currentTimeMillis() - starttime; int seconds = (int) (millis / 1000); int minutes = seconds / 60; seconds = seconds % 60; text3.setText(String.format("%d:%02d", minutes, seconds)); h2.postDelayed(this, 500); } }; //tells handler to send a message class firstTask extends TimerTask { @Override public void run() { h.sendEmptyMessage(0); } }; //tells activity to run on ui thread class secondTask extends TimerTask { @Override public void run() { main.this.runOnUiThread(new Runnable() { @Override public void run() { long millis = System.currentTimeMillis() - starttime; int seconds = (int) (millis / 1000); int minutes = seconds / 60; seconds = seconds % 60; text2.setText(String.format("%d:%02d", minutes, seconds)); } }); } }; Timer timer = new Timer(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); text = (TextView)findViewById(R.id.text); text2 = (TextView)findViewById(R.id.text2); text3 = (TextView)findViewById(R.id.text3); Button b = (Button)findViewById(R.id.button); b.setText("start"); b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Button b = (Button)v; if(b.getText().equals("stop")){ timer.cancel(); timer.purge(); h2.removeCallbacks(run); b.setText("start"); }else{ starttime = System.currentTimeMillis(); timer = new Timer(); timer.schedule(new firstTask(), 0,500); timer.schedule(new secondTask(), 0,500); h2.postDelayed(run, 0); b.setText("stop"); } } }); } @Override public void onPause() { super.onPause(); timer.cancel(); timer.purge(); h2.removeCallbacks(run); Button b = (Button)findViewById(R.id.button); b.setText("start"); } }
главное помнить, что пользовательский интерфейс может быть изменен только из основного потока пользовательского интерфейса, поэтому используйте обработчик или действие.runOnUIThread(Runnable r);
вот что я считаю предпочтительный способ.
import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.widget.Button; import android.widget.TextView; public class TestActivity extends Activity { TextView timerTextView; long startTime = 0; //runs without a timer by reposting this handler at the end of the runnable Handler timerHandler = new Handler(); Runnable timerRunnable = new Runnable() { @Override public void run() { long millis = System.currentTimeMillis() - startTime; int seconds = (int) (millis / 1000); int minutes = seconds / 60; seconds = seconds % 60; timerTextView.setText(String.format("%d:%02d", minutes, seconds)); timerHandler.postDelayed(this, 500); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_activity); timerTextView = (TextView) findViewById(R.id.timerTextView); Button b = (Button) findViewById(R.id.button); b.setText("start"); b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Button b = (Button) v; if (b.getText().equals("stop")) { timerHandler.removeCallbacks(timerRunnable); b.setText("start"); } else { startTime = System.currentTimeMillis(); timerHandler.postDelayed(timerRunnable, 0); b.setText("stop"); } } }); } @Override public void onPause() { super.onPause(); timerHandler.removeCallbacks(timerRunnable); Button b = (Button)findViewById(R.id.button); b.setText("start"); } }
Это просто! Вы создаете новый таймер.
Timer timer = new Timer();
затем вы расширяете задачу таймера
class UpdateBallTask extends TimerTask { Ball myBall; public void run() { //calculate the new position of myBall } }
а затем добавить новую задачу в таймер с некоторым интервалом обновления
final int FPS = 40; TimerTask updateBall = new UpdateBallTask(); timer.scheduleAtFixedRate(updateBall, 0, 1000/FPS);
отказ от ответственности: это не идеальное решение. Это решение с использованием класса Timer (как задано OP). В Android SDK рекомендуется использовать класс обработчика (есть пример в принятом ответе).
Если Вам также нужно запустить свой код в потоке пользовательского интерфейса (а не в потоке таймера), загляните в блог:http://steve.odyfamily.com/?p=12
public class myActivity extends Activity { private Timer myTimer; /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.main); myTimer = new Timer(); myTimer.schedule(new TimerTask() { @Override public void run() { TimerMethod(); } }, 0, 1000); } 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() { //This method runs in the same thread as the UI. //Do something to the UI thread here } }; }
Если вы просто хотите запланировать обратный отсчет до времени в будущем с регулярными уведомлениями о интервалах по пути, вы можете использовать CountDownTimer класс, который доступен с уровня API 1.
new CountDownTimer(30000, 1000) { public void onTick(long millisUntilFinished) { mTextField.setText("Seconds remaining: " + millisUntilFinished / 1000); } public void onFinish() { mTextField.setText("Done"); } }.start();
Это простой код для таймера:
final int sekundi = 0 ; Timer timer = new Timer(); TimerTask t = new TimerTask() { @Override public void run() { System.out.println("1"); } }; timer.scheduleAtFixedRate(t,1000,1000);
Я думаю, что вы можете сделать это в RX как:
timerSubscribe = Observable.interval(1, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<Long>() { @Override public void call(Long aLong) { //TODO do your stuff } });
и отменить это как:
timerSubscribe.unsubscribe();
таймер Rx http://reactivex.io/documentation/operators/timer.html
потому что этот вопрос по-прежнему привлекает много пользователей из Google search(о Android timer) я хотел бы вставить мои две монеты.
прежде всего,таймер класс будет устаревшим в Java 9 (прочитайте принятый ответ).
The официальный предлагаемый способ-использовать ScheduledThreadPoolExecutor который более эффективен и богат функциями, которые могут дополнительно планировать команды для запуска после учитывая задержку, или выполнять периодически. Кроме того, это дает дополнительную гибкость и возможности ThreadPoolExecutor.
вот пример использования простых функций.
создать службу исполнитель:
final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
просто запланируйте запуск:
final Future<?> future = SCHEDULER.schedule(Runnable task, long delay,TimeUnit unit);
теперь вы можете использовать
future
чтобы отменить задачу или проверить, если это сделано, например:future.isDone();
Надежда вы найдете это полезным для создания задач в Android.
пример:
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); Future<?> sampleFutureTimer = scheduler.schedule(new Runnable(), 120, TimeUnit.SECONDS); if (sampleFutureTimer.isDone()){ // Do something which will save world. }
Он является более простым решением, отлично работает в моем приложении.
public class MyActivity extends Acitivity { TextView myTextView; boolean someCondition=true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.my_activity); myTextView = (TextView) findViewById(R.id.refreshing_field); //starting our task which update textview every 1000 ms new RefreshTask().execute(); } //class which updates our textview every second class RefreshTask extends AsyncTask { @Override protected void onProgressUpdate(Object... values) { super.onProgressUpdate(values); String text = String.valueOf(System.currentTimeMillis()); myTextView.setText(text); } @Override protected Object doInBackground(Object... params) { while(someCondition) { try { //sleep for 1s in background... Thread.sleep(1000); //and update textview in ui thread publishProgress(); } catch (InterruptedException e) { e.printStackTrace(); }; return null; } } }
вы хотите, чтобы ваши обновления пользовательского интерфейса происходили в уже существующем потоке пользовательского интерфейса.
лучший способ-использовать обработчик, который использует postDelayed для запуска Runnable после задержки (каждый запуск планирует следующий); снимите обратный вызов с removeCallbacks.
вы уже смотрите в нужном месте, поэтому посмотрите на него еще раз, возможно, проясните, почему этот пример кода не то, что вы хотите. (См. также идентичную статью в обновление пользовательского интерфейса от таймера).
вот простой надежный способ...
поместите следующий код в свою активность, и метод tick() будет вызываться каждую секунду в потоке пользовательского интерфейса, пока ваша активность находится в состоянии "возобновлено". Конечно, вы можете изменить метод tick (), чтобы делать то, что вы хотите, или вызываться более или менее часто.
@Override public void onPause() { _handler = null; super.onPause(); } private Handler _handler; @Override public void onResume() { super.onResume(); _handler = new Handler(); Runnable r = new Runnable() { public void run() { if (_handler == _h0) { tick(); _handler.postDelayed(this, 1000); } } private final Handler _h0 = _handler; }; r.run(); } private void tick() { System.out.println("Tick " + System.currentTimeMillis()); }
для тех, кто заинтересован, код "_h0=_handler" необходим, чтобы избежать одновременного запуска двух таймеров, если ваша деятельность приостановлена и возобновлена в течение тиковый период.
Я удивлен, что нет ответа, который бы отнес решение с RxJava2. Это очень просто и обеспечивает простой способ установки таймера в Android.
сначала вам нужно настроить зависимость Gradle, если вы этого еще не сделали:
implementation "io.reactivex.rxjava2:rxjava:2.x.y"
(вместо
x
иy
С текущий номер версии)поскольку у нас есть только простая, неповторяющаяся задача, мы можем использовать
Completable
объект:Completable.timer(2, TimeUnit.SECONDS, Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(() -> { // Timer finished, do something... });
Schedulers.computation()
гарантирует, что наш таймер работает в фоновом потоке и.observeOn(AndroidSchedulers.mainThread())
означает, что код, который мы запускаем после завершения таймера, будет выполнен в основном потоке.
вам нужно создать поток для обработки цикла обновления и использовать его для обновления текстовой области. Однако сложная часть заключается в том, что только основной поток может фактически изменять пользовательский интерфейс, поэтому поток цикла обновления должен сигнализировать основному потоку о выполнении обновления. Это делается с помощью обработчика.
проверьте эту ссылку:http://developer.android.com/guide/topics/ui/dialogs.html# Нажмите на раздел под названием "пример ProgressDialog со вторым потоком". Это пример именно то, что вам нужно сделать, за исключением диалога прогресса вместо текстового поля.
void method(boolean u,int max) { uu=u; maxi=max; if (uu==true) { CountDownTimer uy = new CountDownTimer(maxi, 1000) { public void onFinish() { text.setText("Finish"); } @Override public void onTick(long l) { String currentTimeString=DateFormat.getTimeInstance().format(new Date()); text.setText(currentTimeString); } }.start(); } else{text.setText("Stop "); }
Если кто-то заинтересован, я начал играть с созданием стандартного объекта для запуска в потоке пользовательского интерфейса activities. Кажется, работает нормально. Комментарии приветствуются. Я хотел бы, чтобы это было доступно в конструкторе макетов в качестве компонента для перетаскивания на действие. Не могу поверить, что такого еще не существует.
package com.example.util.timer; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; public class ActivityTimer { private Activity m_Activity; private boolean m_Enabled; private Timer m_Timer; private long m_Delay; private long m_Period; private ActivityTimerListener m_Listener; private ActivityTimer _self; private boolean m_FireOnce; public ActivityTimer() { m_Delay = 0; m_Period = 100; m_Listener = null; m_FireOnce = false; _self = this; } public boolean isEnabled() { return m_Enabled; } public void setEnabled(boolean enabled) { if (m_Enabled == enabled) return; // Disable any existing timer before we enable a new one Disable(); if (enabled) { Enable(); } } private void Enable() { if (m_Enabled) return; m_Enabled = true; m_Timer = new Timer(); if (m_FireOnce) { m_Timer.schedule(new TimerTask() { @Override public void run() { OnTick(); } }, m_Delay); } else { m_Timer.schedule(new TimerTask() { @Override public void run() { OnTick(); } }, m_Delay, m_Period); } } private void Disable() { if (!m_Enabled) return; m_Enabled = false; if (m_Timer == null) return; m_Timer.cancel(); m_Timer.purge(); m_Timer = null; } private void OnTick() { if (m_Activity != null && m_Listener != null) { m_Activity.runOnUiThread(new Runnable() { @Override public void run() { m_Listener.OnTimerTick(m_Activity, _self); } }); } if (m_FireOnce) Disable(); } public long getDelay() { return m_Delay; } public void setDelay(long delay) { m_Delay = delay; } public long getPeriod() { return m_Period; } public void setPeriod(long period) { if (m_Period == period) return; m_Period = period; } public Activity getActivity() { return m_Activity; } public void setActivity(Activity activity) { if (m_Activity == activity) return; m_Activity = activity; } public ActivityTimerListener getActionListener() { return m_Listener; } public void setActionListener(ActivityTimerListener listener) { m_Listener = listener; } public void start() { if (m_Enabled) return; Enable(); } public boolean isFireOnlyOnce() { return m_FireOnce; } public void setFireOnlyOnce(boolean fireOnce) { m_FireOnce = fireOnce; } }
в деятельности, у меня есть этот onStart:
@Override protected void onStart() { super.onStart(); m_Timer = new ActivityTimer(); m_Timer.setFireOnlyOnce(true); m_Timer.setActivity(this); m_Timer.setActionListener(this); m_Timer.setDelay(3000); m_Timer.start(); }
import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Timer; import java.util.TimerTask; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.TextView; import android.app.Activity; public class MainActivity extends Activity { CheckBox optSingleShot; Button btnStart, btnCancel; TextView textCounter; Timer timer; MyTimerTask myTimerTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); optSingleShot = (CheckBox)findViewById(R.id.singleshot); btnStart = (Button)findViewById(R.id.start); btnCancel = (Button)findViewById(R.id.cancel); textCounter = (TextView)findViewById(R.id.counter); btnStart.setOnClickListener(new OnClickListener(){ @Override public void onClick(View arg0) { if(timer != null){ timer.cancel(); } //re-schedule timer here //otherwise, IllegalStateException of //"TimerTask is scheduled already" //will be thrown timer = new Timer(); myTimerTask = new MyTimerTask(); if(optSingleShot.isChecked()){ //singleshot delay 1000 ms timer.schedule(myTimerTask, 1000); }else{ //delay 1000ms, repeat in 5000ms timer.schedule(myTimerTask, 1000, 5000); } }}); btnCancel.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { if (timer!=null){ timer.cancel(); timer = null; } } }); } class MyTimerTask extends TimerTask { @Override public void run() { Calendar calendar = Calendar.getInstance(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a"); final String strDate = simpleDateFormat.format(calendar.getTime()); runOnUiThread(new Runnable(){ @Override public void run() { textCounter.setText(strDate); }}); } } }
.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:autoLink="web" android:text="http://android-er.blogspot.com/" android:textStyle="bold" /> <CheckBox android:id="@+id/singleshot" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Single Shot"/>
вы также можете использовать аниматор для этого:
int secondsToRun = 999; ValueAnimator timer = ValueAnimator.ofInt(secondsToRun); timer.setDuration(secondsToRun * 1000).setInterpolator(new LinearInterpolator()); timer.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int elapsedSeconds = (int) animation.getAnimatedValue(); int minutes = elapsedSeconds / 60; int seconds = elapsedSeconds % 60; textView.setText(String.format("%d:%02d", minutes, seconds)); } }); timer.start();
для тех, кто не может положиться на секундомер, Я сделал класс полезности из одного из предложений:
public class TimerTextHelper implements Runnable { private final Handler handler = new Handler(); private final TextView textView; private volatile long startTime; private volatile long elapsedTime; public TimerTextHelper(TextView textView) { this.textView = textView; } @Override public void run() { long millis = System.currentTimeMillis() - startTime; int seconds = (int) (millis / 1000); int minutes = seconds / 60; seconds = seconds % 60; textView.setText(String.format("%d:%02d", minutes, seconds)); if (elapsedTime == -1) { handler.postDelayed(this, 500); } } public void start() { this.startTime = System.currentTimeMillis(); this.elapsedTime = -1; handler.post(this); } public void stop() { this.elapsedTime = System.currentTimeMillis() - startTime; handler.removeCallbacks(this); } public long getElapsedTime() { return elapsedTime; }
}
использовать..просто сделайте:
TimerTextHelper timerTextHelper = new TimerTextHelper(textView); timerTextHelper.start();
.....
timerTextHelper.stop(); long elapsedTime = timerTextHelper.getElapsedTime();
Если у вас уже есть дельта-время.
public class Timer { private float lastFrameChanged; private float frameDuration; private Runnable r; public Timer(float frameDuration, Runnable r) { this.frameDuration = frameDuration; this.lastFrameChanged = 0; this.r = r; } public void update(float dt) { lastFrameChanged += dt; if (lastFrameChanged > frameDuration) { lastFrameChanged = 0; r.run(); } } }