Jak ustawić zegar w Androidzie?


325

Czy ktoś może podać prosty przykład aktualizacji pola tekstowego co sekundę?

Chcę zrobić latającą piłkę i muszę obliczać / aktualizować współrzędne piłki co sekundę, dlatego potrzebuję jakiegoś timera.

Nie rozumiem nic z tutaj .



Odpowiedzi:


463

ok, ponieważ nie zostało to jeszcze wyjaśnione, istnieją 3 proste sposoby radzenia sobie z tym. Poniżej znajduje się przykład pokazujący wszystkie 3, a na dole przykład pokazujący tylko metodę, która moim zdaniem jest lepsza. Pamiętaj także o wyczyszczeniu zadań w trybie onPause, w razie potrzeby zapisując stan.


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


najważniejszą rzeczą do zapamiętania jest to, że interfejs użytkownika można modyfikować tylko z głównego wątku interfejsu użytkownika, więc użyj programu obsługi lub activity.runOnUIThread (Runnable r);

Oto, co uważam za preferowaną metodę.


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, dzięki za świetny przykład. Czy są jakieś zalety / wady korzystania z jednej metody w porównaniu z innymi, które przedstawiłeś?
Gautam

2
@Gautam Uważam, że wszystkie powyższe metody działają mniej więcej tak samo. Osobiście wolę opisaną powyżej metodę obsługi z uruchomieniem Runnable i h2 Handler, ponieważ jest to metoda zalecana przez witrynę dla programistów Androida i moim zdaniem również najbardziej elegancka.
Dave.B

6
Byłoby miło oddzielić preferowaną metodę od reszty kodu. Tak jak możesz mieć jeden przykład pokazujący preferowany sposób, a drugi pokazujący alternatywy. Posiadanie wszystkich trzech metod razem sprawia, że ​​trudniej jest zrozumieć, co się dzieje (szczególnie dla nowicjuszy z Androidem, takiego jak ja). Prawdopodobnie jednak zbyt wiele pyta :)
Jesse Aldridge

4
@JesseAldridge Dobry pomysł. Poszedłem dalej i dodałem kod tylko za pomocą preferowanej metody.
Dave.B

1
@bluesm Szczerze mówiąc, po prostu o tym nie myślałem, ale tak, to by działało dobrze.
Dave.B

84

To jest proste! Tworzysz nowy licznik czasu.

Timer timer = new Timer();

Następnie rozszerzasz zadanie timera

class UpdateBallTask extends TimerTask {
   Ball myBall;

   public void run() {
       //calculate the new position of myBall
   }
}

A następnie dodaj nowe zadanie do Timera z pewnym interwałem aktualizacji

final int FPS = 40;
TimerTask updateBall = new UpdateBallTask();
timer.scheduleAtFixedRate(updateBall, 0, 1000/FPS);

Oświadczenie: To nie jest idealne rozwiązanie. Jest to rozwiązanie wykorzystujące klasę Timer (zgodnie z zapytaniem OP). W zestawie Android SDK zaleca się użycie klasy Handler (w zaakceptowanej odpowiedzi znajduje się przykład).


1
jeśli przeczytasz powyższy post, zobaczysz, dlaczego nie jest to idealne rozwiązanie
Dave.B

2
Oczywiście. OP chciał to zrobić za pomocą TimerTask, którego nie polecam do użycia w grze.
fikcja

3
Co? OP nie określił, jak tego chcą. Odsyłali do artykułu, który korzystał z TimerTask, ale nie prosili, aby zrobić to w ten sposób.
ToolmakerSteve

1
Pomógł bardzo, Dzięki @fiction
Naveed Ahmad

1
świetna odpowiedź prosta do naśladowania.
JMASTER B,

64

Jeśli musisz także uruchomić swój kod w wątku interfejsu użytkownika (a nie w wątku licznika czasu), zajrzyj na blog: 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

    }
};
}

Dla kompletności możesz wspomnieć, co zrobić, aby zatrzymać stoper, a może zrestartować go. (Potrzebne informacje znalazłem tutaj: stackoverflow.com/questions/11550561/... )
RenniePet

3
Czy jest jakiś powód, dla którego nie można po prostu wywołać runOnUIThread bezpośrednio z metody uruchamiania TimerTask? Wydaje się, że działa dobrze i usuwa kolejny poziom zagnieżdżania.
RichieHH,

Jasne, to tylko metoda dydaktyczna do zrozumienia wszystkich kroków. Sugeruję, aby ten standard miał czytelny kod.
Meir Gerenstadt

41

Jeśli po prostu chcesz zaplanować odliczanie do czasu w przyszłości z regularnymi powiadomieniami o odstępach czasu po drodze, możesz użyć klasy CountDownTimer, która jest dostępna od poziomu 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 ma sens tylko wtedy, gdy wiesz, że chcesz, aby zniknął po kilku egzekucjach. To nie jest typowe ani szczególnie elastyczne podejście. Częstszym jest licznik czasu, który powtarza się na zawsze (który anulujesz, gdy nie jest już potrzebny) lub moduł obsługi, który uruchamia się raz, a następnie uruchamia się ponownie, jeśli będzie ponownie potrzebny. Zobacz inne odpowiedzi.
ToolmakerSteve

1
Masz całkowitą rację. Od nazwy klasy zapewnia jednorazowe odliczanie timera aż do końca i oczywiście używa Handlera w swojej implementacji.
Ahmed Hegazy,

Jak również wyświetlać milisekundy? W formacie SS:MiMi? Dzięki
Ruchir Baronia,

26

Oto prosty kod dla timera:

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

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

12

Myślę, że możesz to zrobić w Rx sposób:

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

I anuluj to tak:

timerSubscribe.unsubscribe();

Rx Timer http://reactivex.io/documentation/operators/timer.html


10

Ponieważ to pytanie wciąż przyciąga wielu użytkowników z wyszukiwarki Google (o zegarze Androida), chciałbym wstawić dwie monety.

Przede wszystkim klasa Timer będzie przestarzała w Javie 9 (przeczytaj zaakceptowaną odpowiedź) .

Urzędnik zaproponował sposobem jest użycie ScheduledThreadPoolExecutor który jest bardziej skuteczny i dysponuje bogatym w które można dodatkowo komendy rozkład jazdy po danym opóźnienie lub okresowo wykonywać. Ponadto zapewnia dodatkową elastyczność i możliwości ThreadPoolExecutor.

Oto przykład użycia prostych funkcji.

  1. Utwórz usługę executora:

    final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
  2. Po prostu zaplanuj uruchomienie:

    final Future<?> future = SCHEDULER.schedule(Runnable task, long delay,TimeUnit unit);
  3. Możesz teraz użyć, futureaby anulować zadanie lub sprawdzić, czy zostało wykonane, na przykład:

    future.isDone();

Mam nadzieję, że okaże się to przydatne do tworzenia zadań w Androidzie.

Kompletny przykład:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Future<?> sampleFutureTimer = scheduler.schedule(new Runnable(), 120, TimeUnit.SECONDS);
if (sampleFutureTimer.isDone()){
    // Do something which will save world.
}

8

Dziwi mnie, że nie ma odpowiedzi, która wspomniałaby o rozwiązaniu z RxJava2 . To jest naprawdę proste i zapewnia łatwy sposób na ustawienie timera w Androidzie.

Najpierw musisz skonfigurować zależność Gradle, jeśli jeszcze tego nie zrobiłeś:

implementation "io.reactivex.rxjava2:rxjava:2.x.y"

(wymienić xi yz aktualnym numerem wersji )

Ponieważ mamy tylko proste, BEZ POWTARZANIA ZADANIE , możemy użyć Completableobiektu:

Completable.timer(2, TimeUnit.SECONDS, Schedulers.computation())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(() -> {
            // Timer finished, do something...
        });

W przypadku POWTARZANIA ZADANIA można użyć Observablew podobny sposób:

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

Schedulers.computation() zapewnia, że ​​nasz zegar działa na wątku w tle i .observeOn(AndroidSchedulers.mainThread()) oznacza, że ​​kod uruchamiany po zakończeniu timera zostanie wykonany w głównym wątku.

Aby uniknąć niechcianych wycieków pamięci, należy anulować subskrypcję, gdy Aktywność / Fragment zostanie zniszczona.


4
To najczystsze podejście!
Constantin

jak je anulować? tzn. gdy użytkownik naciśnie przycisk [STOP] na interfejsie użytkownika, a element Completable zostanie anulowany przed wykonaniem.
Ktoś gdzieś

@SomeoneSomewhere Wystarczy zapisać Subscriptionzwrócony przez .subscribe()metodę w zmiennej, a następnie wywołać, subscription.unsubscribe()gdy chcesz zatrzymać stoper.
Micer

2

Jest prostszym rozwiązaniem, działa dobrze w mojej aplikacji.

  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

Chcesz, aby aktualizacje interfejsu użytkownika miały miejsce w już istniejącym wątku interfejsu użytkownika.

Najlepszym sposobem jest użycie modułu obsługi, który korzysta z funkcji postDelayed, aby uruchomić Runnable po opóźnieniu (każde uruchomienie planuje następne); wyczyść połączenie zwrotne za pomocą removeCallbacks.

Patrzysz już we właściwe miejsce, więc spójrz na to jeszcze raz, być może wyjaśnij, dlaczego ta próbka kodu nie jest tym, czego chcesz. (Zobacz także identyczny artykuł na temat aktualizacji interfejsu użytkownika z timera ).


Niestety Twój link jest martwy. Nie mogę szybko znaleźć właściwego artykułu z powrotem.
Lekensteyn


1

Oto prosty, niezawodny sposób ...

Umieść następujący kod w swojej aktywności, a metoda tick () będzie wywoływana co sekundę w wątku interfejsu użytkownika, gdy twoja aktywność będzie w stanie „wznowionym”. Oczywiście możesz zmienić metodę tick (), aby robić to, co chcesz, lub być nazywanym mniej lub bardziej często.

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

Dla zainteresowanych kod „_h0 = _handler” jest niezbędny, aby uniknąć jednoczesnego działania dwóch timerów, jeśli aktywność zostanie wstrzymana i wznowiona w okresie tykania.


2
Dlaczego to niewygodne _h0podejście, zamiast removeCallbacksw onPause, jak wszyscy inni?
ToolmakerSteve

1

Możesz także użyć do tego animatora:

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

Musisz utworzyć wątek do obsługi pętli aktualizacji i użyć go do zaktualizowania obszaru tekstowego. Problem polega jednak na tym, że tylko główny wątek może tak naprawdę modyfikować interfejs użytkownika, więc wątek pętli aktualizacji musi zasygnalizować, że wątek główny wykona aktualizację. Odbywa się to za pomocą modułu obsługi.

Sprawdź ten link: http://developer.android.com/guide/topics/ui/dialogs.html# Kliknij sekcję „Przykład ProgressDialog z drugim wątkiem”. Jest to przykład tego, co musisz zrobić, z wyjątkiem okna dialogowego postępu zamiast pola tekstowego.


Nie rób tego Istnieje prosta klasa timerów, która robi to wszystko za Ciebie. To pytanie nie ma nic wspólnego z progresywnymi oknami dialogowymi lub dialogami.
Falmarri

Czy spojrzałeś na sekcję linku, który zamieściłem, czy widziałeś tylko okno dialogowe słowa i zakładasz? Kod jest w 100% odpowiedni. Również FYI, jeśli używasz timera, nadal tworzysz wątek do obsługi pętli aktualizacji. Nadal będziesz musiał korzystać z modułu obsługi opisanego w linku, który zamieściłem.
Nick

Niestety, linkowana strona nie zawiera już sekcji z wymienionym tytułem. Łącząc się z kodem, należy zawsze dołączać fragment klucza bezpośrednio w odpowiedzi.
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
Być może przydatne byłyby wcięcia i objaśnienia kodu.
Raul Rene,

0

Jeśli ktoś jest zainteresowany, zacząłem bawić się tworzeniem standardowego obiektu do uruchomienia w wątku interfejsu użytkownika działań. Wygląda na to, że działa dobrze. Komentarze mile widziane. Chciałbym, aby to było dostępne w projektancie układu jako komponent do przeciągnięcia na działanie. Nie mogę uwierzyć, że coś takiego już nie istnieje.

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

W tym działaniu mam 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
koleś, co jest nie tak z ActivityTimerListener? Mój pakiet ADT powiedział, że nie ma takiej klasy.
Sorokin Andrey

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


Widzę, że dodałeś to kilka lat po oryginalnym pytaniu i odpowiedziach. Dodaj wyjaśnienie, w jaki sposób ta odpowiedź różni się od innych odpowiedzi, które już tam były. Dlaczego dodałeś kolejną - jaką korzyść / kiedy jest użyteczna / jakie wady dostrzegłeś w innych odpowiedziach?
ToolmakerSteve

Po prostu udostępniam kod, który wykonuje tę samą pracę z innym podejściem. Ale ilekroć chcesz zaktualizować dane dowolnego widoku. Musisz użyć do tego programu obsługi. ponieważ wiele razy zauważyłem, że użycie timertask do aktualizacji widoku nie działa .. Metoda @ Dave.B jest bardziej poprawna według mojej wiedzy.
Zar E Ahmer,

0

Jeśli masz już czas delta.

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

I abstrakcyjny Timer odszedł i uczynił z niego osobną klasę:

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

I wyodrębnij główną akcję z Timerklasy jako

IAction.java

public interface IAction {
    void Task();
}

I użyłem tego tak:

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

Mam nadzieję, że to pomaga 😊👌


0

Używam w ten sposób:

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

następnie poniżej 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();

to prosty sposób na rozwiązanie tego problemu.


0

Dla tych, którzy nie mogą polegać na Chronometrze , stworzyłem klasę użyteczności na podstawie jednej z sugestii:

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

użyć… po prostu wykonaj:

 TimerTextHelper timerTextHelper = new TimerTextHelper(textView);
 timerTextHelper.start();

.....

 timerTextHelper.stop();
 long elapsedTime = timerTextHelper.getElapsedTime();
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.