Как установить таймер в Android?


326

Может кто-нибудь привести простой пример обновления текстового поля каждую секунду или около того?

Я хочу сделать летающий шар и мне нужно вычислять / обновлять координаты шара каждую секунду, поэтому мне нужен какой-то таймер.

Я ничего не получаю отсюда .



Ответы:


464

хорошо, так как это еще не выяснено, есть 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");
    }
}


главное помнить, что пользовательский интерфейс может быть изменен только из основного потока пользовательского интерфейса, поэтому используйте обработчик или activity.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");
    }

}



1
@ Dave.B, спасибо за отличный пример. Есть ли какие-либо преимущества / недостатки использования одного метода по сравнению с другими, которые вы наметили?
Гаутам

2
@ Гаутам Я считаю, что все методы, описанные выше, работают примерно одинаково. Я лично предпочитаю метод обработчика, описанный выше, с использованием Runnable и h2 Handler, так как он предписан сайтом для разработчиков Android и, на мой взгляд, также самый элегантный.
Dave.B

6
Было бы хорошо, чтобы ваш предпочтительный метод был отделен от остальной части кода. Как будто у вас может быть один пример, показывающий ваш предпочтительный путь, и другой, показывающий альтернативы. Объединение всех трех методов затрудняет понимание происходящего (особенно для новичка-андроида, такого как я). Вероятно, прося слишком много, хотя :)
Джесси Олдридж

4
@JesseAldridge Хорошая идея. Я пошел дальше и добавил код только с предпочтительным методом.
Dave.B

1
@ bluesm Я, честно говоря, просто не думал об этом, но да, это бы сработало.
Dave.B

84

Это просто! Вы создаете новый таймер.

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 рекомендуется использовать класс Handler (в принятом ответе есть пример).


1
Если вы прочитаете пост выше, вы поймете, почему это не идеальное решение
Dave.B

2
Конечно. ОП хотел сделать это с TimerTask, который я не рекомендую использовать в игре.
фантастика

3
А? ОП не уточнил, как они этого хотели. Они ссылались на статью, в которой использовалась TimerTask, но не просили, чтобы это делалось таким образом.
ToolmakerSteve

1
Много помог, Спасибо @fiction
Навид Ахмад

1
отличный ответ простой для подражания.
JMASTER B

64

Если вам также необходимо запустить код в потоке пользовательского интерфейса (а не в потоке таймера), загляните в блог: 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

    }
};
}

Для полноты картины вы могли бы упомянуть, что нужно сделать, чтобы остановить таймер, и, возможно, перезапустить его. (Я нашел необходимую информацию здесь: stackoverflow.com/questions/11550561/… )
RenniePet

3
Есть ли какая-то причина, по которой вы не можете просто вызвать runOnUIThread напрямую из метода запуска TimerTask? Кажется, работает нормально и удаляет другой уровень вложенности.
RichieHH

Конечно, это только дидактический метод, чтобы понять все шаги. Я предлагаю этот стандарт иметь читаемый код.
Меир Геренштадт

41

Если вы просто хотите запланировать обратный отсчет до определенного времени в будущем с регулярными уведомлениями через определенные промежутки времени, вы можете использовать класс CountDownTimer, который доступен начиная с уровня API 1.

new CountDownTimer(30000, 1000) {
    public void onTick(long millisUntilFinished) {
        editText.setText("Seconds remaining: " + millisUntilFinished / 1000);
    }

    public void onFinish() {
        editText.setText("Done");
    }
}.start();

2
CountDownTimer имеет смысл, только если вы знаете, что хотите, чтобы он исчез после нескольких казней. Это не типичный и не особенно гибкий подход. Более распространенным является таймер, который повторяется навсегда (который вы отменяете, когда он больше не нужен), или обработчик, который запускается один раз, а затем запускается снова, если потребуется снова. Смотрите другие ответы.
ToolmakerSteve

1
Вы совершенно правы. От имени класса он обеспечивает один таймер обратного отсчета до окончания и, конечно, использует в своей реализации Handler.
Ахмед Хегазы

Как показать также миллисекунды? В формате SS:MiMi? Спасибо
Ruchir Baronia

27

Это простой код для таймера:

Timer timer = new Timer();
TimerTask t = new TimerTask() {       
    @Override
    public void run() {

        System.out.println("1");
    }
};
timer.scheduleAtFixedRate(t,1000,1000);

12

Я думаю, что вы можете сделать это в 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 Timer http://reactivex.io/documentation/operators/timer.html


10

Поскольку этот вопрос по-прежнему привлекает множество пользователей из поиска в Google (о таймере Android), я хотел бы вставить свои две монеты.

Прежде всего, класс Timer будет устаревшим в Java 9 (прочитайте принятый ответ) .

Официальный предложил способ заключается в использовании ScheduledThreadPoolExecutor , который является более эффективным и показывает , что богатые могут дополнительно команды расписания для запуска после данной задержки, или периодически выполнять. Кроме того, это дает дополнительную гибкость и возможности ThreadPoolExecutor.

Вот пример использования простых функций.

  1. Создать службу исполнителя:

    final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
  2. Просто график вы работаете:

    final Future<?> future = SCHEDULER.schedule(Runnable task, long delay,TimeUnit unit);
  3. Теперь вы можете использовать, 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.
}

8

Я удивлен, что нет ответа, который упомянул бы решение с 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...
        });

Для ПОВТОРНОЙ ЗАДАЧИ вы можете использовать Observableаналогичным образом:

Observable.interval(2, TimeUnit.SECONDS, Schedulers.computation())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(tick -> {
            // called every 2 seconds, do something...
        }, throwable -> {
            // handle error
        });

Schedulers.computation()гарантирует, что наш таймер работает в фоновом потоке и .observeOn(AndroidSchedulers.mainThread())означает, что код, который мы запускаем после завершения таймера, будет выполнен в основном потоке.

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


4
Это самый чистый подход!
Константин

как можно отменить это? то есть когда пользователь нажимает кнопку [STOP] на интерфейсе пользователя и Completable отменяется перед выполнением.
Кто-то где-то

@SomeoneSomewhere Просто сохраните Subscriptionвозвращенный .subscribe()метод в переменной и затем вызовите, subscription.unsubscribe()когда вы хотите остановить таймер.
Майкер

2

Он более простое решение, прекрасно работает в моем приложении.

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

2

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

Лучший способ - использовать Обработчик, который использует postDelayed для запуска Runnable после задержки (каждый запуск планирует следующий); очистить обратный вызов с помощью removeCallbacks.

Вы уже ищете правильное место, поэтому посмотрите на него еще раз, возможно, объясните, почему этот пример кода не тот, который вам нужен. (См. Также ту же статью в разделе Обновление интерфейса от таймера ).


К сожалению, ваша ссылка мертва. Я не могу быстро найти нужную статью обратно.
Лекенштейн


1

Вот простой надежный способ ...

Поместите следующий код в свою активность, и метод 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" необходим, чтобы избежать одновременного запуска двух таймеров, если ваша деятельность была приостановлена ​​и возобновлена ​​в течение периода времени.


2
Почему этот неуклюжий _h0подход, а не removeCallbacksв onPause, как все остальные?
ToolmakerSteve

1

Вы также можете использовать аниматор для этого:

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

0

Вам необходимо создать поток для обработки цикла обновления и использовать его для обновления текстовой области. Сложность заключается в том, что только основной поток может фактически изменять пользовательский интерфейс, поэтому поток цикла обновления должен сигнализировать основному потоку о необходимости обновления. Это делается с помощью обработчика.

Проверьте эту ссылку: http://developer.android.com/guide/topics/ui/dialogs.html# Нажмите на раздел под названием «Пример ProgressDialog со вторым потоком». Это пример того, что вам нужно сделать, за исключением диалогового окна прогресса вместо текстового поля.


Не делай этого. Существует простой класс таймера, который делает все это для вас. И этот вопрос не имеет никакого отношения к диалогам или диалогам.
Falmarri

Вы смотрели на раздел ссылки, которую я разместил, или вы просто увидели слово диалог и предположили? Код там на 100% актуален. Также, к вашему сведению, если вы используете таймер, вы все равно создаете поток для обработки цикла обновления. Вам все равно нужно будет использовать Обработчик, как описано в ссылке, которую я разместил.
Ник

К сожалению, связанная страница больше не содержит раздел с упомянутым заголовком. При связывании с кодом всегда следует включать фрагмент кода непосредственно в ответ.
ToolmakerSteve

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

2
Может быть, некоторые отступы кода и объяснения кода будут полезны.
Рауль Рене

0

Если кому-то интересно, я начал играть с созданием стандартного объекта для запуска в потоке пользовательского интерфейса. Кажется, работает нормально. Комментарии приветствуются. Я хотел бы, чтобы это было доступно на дизайнере макетов в качестве компонента для перетаскивания на Activity. Не могу поверить, что такого не существует.

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

1
чувак, что не так с ActivityTimerListener? Мой ADT Bundle сказал, что такого класса нет.
Сорокин Андрей

0
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"/>


Я вижу, что вы добавили это через несколько лет после первоначального вопроса и ответов. Пожалуйста, добавьте объяснение того, как этот ответ сравнивается с другими ответами, которые уже были там. Почему вы добавили еще одну - какую пользу / когда она была полезна / какой недостаток вы видели в других ответах?
ToolmakerSteve

Я просто делюсь кодом, который выполняет ту же работу с другим подходом. Но всякий раз, когда вы хотите обновить данные любого представления. Вы должны использовать обработчик для этого. потому что много раз я замечал, что использование таймерной задачи для обновления представления не работает .. Насколько мне известно, метод @ Dave.B более правильный.
Зар Э Ахмер

0

Если у вас уже есть дельта время.

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

0

Я удалил абстрактный таймер и сделал его отдельным классом:

Timer.java

import android.os.Handler;

public class Timer {

    IAction action;
    Handler timerHandler = new Handler();
    int delayMS = 1000;

    public Timer(IAction action, int delayMS) {
        this.action = action;
        this.delayMS = delayMS;
    }

    public Timer(IAction action) {
        this(action, 1000);
    }

    public Timer() {
        this(null);
    }

    Runnable timerRunnable = new Runnable() {

        @Override
        public void run() {
            if (action != null)
                action.Task();
            timerHandler.postDelayed(this, delayMS);
        }
    };

    public void start() {
        timerHandler.postDelayed(timerRunnable, 0);
    }

    public void stop() {
        timerHandler.removeCallbacks(timerRunnable);
    }
}

И извлечь основное действие из Timerкласса как

IAction.java

public interface IAction {
    void Task();
}

И я использовал это так:

MainActivity.java

public class MainActivity extends Activity implements IAction{
...
Timer timerClass;
@Override
protected void onCreate(Bundle savedInstanceState) {
        ...
        timerClass = new Timer(this,1000);
        timerClass.start();
        ...
}
...
int i = 1;
@Override
public void Task() {
    runOnUiThread(new Runnable() {

        @Override
        public void run() {
            timer.setText(i + "");
            i++;
        }
    });
}
...
}

Я надеюсь, что это поможет 😊👌


0

Я использую этот способ:

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

тогда ниже 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();

это простой способ решить эту проблему.


0

Для тех, кто не может положиться на Хронометр , я сделал полезный класс из одного из предложений:

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();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.