Comment régler la minuterie dans Android?


326

Quelqu'un peut-il donner un exemple simple de mise à jour d'un champ de texte toutes les secondes environ?

Je veux faire une balle volante et j'ai besoin de calculer / mettre à jour les coordonnées de la balle chaque seconde, c'est pourquoi j'ai besoin d'une sorte de minuterie.

Je ne reçois rien d' ici .



Réponses:


464

ok car ce n'est pas encore clarifié, il y a 3 façons simples de gérer cela. Ci-dessous est un exemple montrant les 3 et en bas est un exemple montrant juste la méthode que je crois préférable. Pensez également à nettoyer vos tâches dans onPause, en sauvegardant l'état si nécessaire.


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


la principale chose à retenir est que l'interface utilisateur ne peut être modifiée qu'à partir du thread d'interface utilisateur principal, utilisez donc un gestionnaire ou activity.runOnUIThread (Runnable r);

Voici ce que je considère comme la méthode préférée.


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, merci pour le bon exemple. Y a-t-il des avantages / inconvénients à utiliser une méthode par rapport aux autres que vous avez décrites?
Gautam

2
@Gautam Je crois que toutes les méthodes ci-dessus fonctionnent de la même manière. Personnellement, je préfère la méthode du gestionnaire décrite ci-dessus avec les run run Runable et h2 Handler car c'est celle prescrite par le site de développement Android et à mon avis aussi la plus élégante.
Dave.B

6
Ce serait bien d'avoir votre méthode préférée séparée du reste du code. Comme vous pourriez avoir un exemple montrant votre voie préférée et un autre montrant les alternatives. Le fait d'avoir les trois méthodes ensemble rend plus difficile la compréhension de ce qui se passe (spécialement pour un débutant Android comme moi). Probablement trop demander cependant :)
Jesse Aldridge

4
@JesseAldridge Bonne idée. J'ai continué et ajouté du code avec la méthode préférée uniquement.
Dave.B

1
@bluesm Honnêtement, je n'y ai tout simplement pas pensé, mais oui, cela fonctionnerait bien.
Dave.B

84

C'est simple! Vous créez une nouvelle minuterie.

Timer timer = new Timer();

Ensuite, vous étendez la tâche du minuteur

class UpdateBallTask extends TimerTask {
   Ball myBall;

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

Et puis ajoutez la nouvelle tâche au minuteur avec un certain intervalle de mise à jour

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

Avertissement: Ce n'est pas la solution idéale. Il s'agit d'une solution utilisant la classe Timer (comme demandé par OP). Dans Android SDK, il est recommandé d'utiliser la classe Handler (il y a un exemple dans la réponse acceptée).


1
si vous lisez le post ci-dessus, vous verrez pourquoi ce n'est pas une solution idéale
Dave.B

2
Bien sûr. L'OP voulait le faire avec TimerTask, que je ne recommanderai pas d'utiliser dans le jeu.
fiction

3
Hein? Le PO n'a pas précisé comment il voulait que cela se fasse. Ils ont lié à un article qui utilisait TimerTask, mais ils n'ont pas demandé que ce soit fait de cette façon.
ToolmakerSteve

1
Beaucoup aidé, merci @ fiction
Naveed Ahmad

1
grande réponse simple à suivre.
JMASTER B du

64

Si vous devez également exécuter votre code sur le thread d'interface utilisateur (et non sur le thread du minuteur), jetez un œil sur le 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

    }
};
}

Par souci d'exhaustivité, vous pouvez peut-être mentionner ce qu'il faut faire pour arrêter le chronomètre et peut-être le redémarrer. (J'ai trouvé les informations nécessaires ici: stackoverflow.com/questions/11550561/… )
RenniePet

3
Y a-t-il une raison pour laquelle vous ne pouvez pas simplement appeler runOnUIThread directement à partir de la méthode d'exécution TimerTask? Semble fonctionner correctement et supprime un autre niveau d'imbrication.
RichieHH

Bien sûr, ce n'est qu'une méthode didactique pour comprendre toutes les étapes. Je suggère à cette norme d'avoir un code lisible.
Meir Gerenstadt

41

Si vous souhaitez simplement planifier un compte à rebours jusqu'à une date ultérieure avec des notifications régulières à intervalles réguliers, vous pouvez utiliser la classe CountDownTimer qui est disponible depuis le niveau 1 de l'API.

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

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

2
CountDownTimer n'a de sens que si vous savez que vous souhaitez qu'il disparaisse après plusieurs exécutions. Ce n'est pas une approche typique, ni particulièrement flexible. Le plus courant est le temporisateur qui se répète pour toujours (que vous annulez lorsqu'il n'est plus nécessaire), ou le gestionnaire qui s'exécute une fois, puis se redémarre s'il est nécessaire à nouveau. Voir d'autres réponses.
ToolmakerSteve

1
Tu as complètement raison. À partir du nom de la classe, il fournit un compte à rebours une fois jusqu'à la fin et, bien sûr, il utilise Handler dans son implémentation.
Ahmed Hegazy

Comment afficher également les millisecondes? Dans le format SS:MiMi? Merci
Ruchir Baronia

27

Voici un code simple pour une minuterie:

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

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

12

Je pense que vous pouvez le faire de manière Rx comme:

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

Et annulez ceci comme:

timerSubscribe.unsubscribe();

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


10

Parce que cette question attire toujours beaucoup d'utilisateurs de la recherche Google (sur le minuteur Android), je voudrais insérer mes deux pièces.

Tout d'abord, la classe Timer sera déconseillée en Java 9 (lire la réponse acceptée) .

La manière officielle suggérée est d'utiliser ScheduledThreadPoolExecutor qui est plus efficace et riche en fonctionnalités qui peut en outre planifier des commandes à exécuter après un certain délai ou à exécuter périodiquement. De plus, il offre une flexibilité et des capacités supplémentaires à ThreadPoolExecutor.

Voici un exemple d'utilisation de fonctionnalités simples.

  1. Créer un service d'exécuteur:

    final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
  2. Planifiez simplement votre runnable:

    final Future<?> future = SCHEDULER.schedule(Runnable task, long delay,TimeUnit unit);
  3. Vous pouvez maintenant utiliser futurepour annuler la tâche ou vérifier si cela est fait par exemple:

    future.isDone();

J'espère que cela vous sera utile pour créer des tâches dans Android.

Exemple complet:

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

8

Je suis surpris qu'aucune réponse ne mentionne une solution avec RxJava2 . C'est vraiment simple et fournit un moyen facile de configurer la minuterie dans Android.

Vous devez d'abord configurer la dépendance Gradle, si vous ne l'avez pas déjà fait:

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

(remplacer xet ypar le numéro de version actuel )

Puisque nous avons juste une TÂCHE simple, NON RÉPÉTABLE , nous pouvons utiliser Completableobjet:

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

Pour RÉPÉTER LA TÂCHE , vous pouvez utiliser Observablede la même manière:

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

Schedulers.computation() s'assure que notre minuterie fonctionne sur le fil de fond et .observeOn(AndroidSchedulers.mainThread()) signifie que le code que nous exécutons une fois le minuteur terminé sera exécuté sur le thread principal.

Pour éviter les fuites de mémoire indésirables, vous devez vous assurer de vous désabonner lorsque l'activité / le fragment est détruit.


4
C'est l'approche la plus propre!
Constantin

comment peut-on les annuler? c'est-à-dire lorsque l'utilisateur appuie sur le bouton [STOP] de l'interface utilisateur et que le Completable est annulé avant l'exécution.
Someone Somewhere

@SomeoneSomewhere Enregistrez simplement la méthode Subscriptionretournée par .subscribe()dans la variable, puis appelez subscription.unsubscribe()lorsque vous souhaitez arrêter le chronomètre.
Micer

2

Il est une solution plus simple, fonctionne très bien dans mon application.

  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

Vous souhaitez que vos mises à jour d'interface utilisateur se produisent dans le thread d'interface utilisateur déjà existant.

La meilleure façon est d'utiliser un gestionnaire qui utilise postDelayed pour exécuter un Runnable après un délai (chaque exécution planifie la suivante); effacez le rappel avec removeCallbacks.

Vous cherchez déjà au bon endroit, alors revoyez-le, peut-être clarifiez pourquoi cet exemple de code n'est pas ce que vous voulez. (Voir également l'article identique dans Mise à jour de l'interface utilisateur à partir d'un minuteur ).


Malheureusement, votre lien est mort. Je ne peux pas retrouver rapidement le bon article.
Lekensteyn


1

Voici un moyen simple et fiable ...

Mettez le code suivant dans votre activité, et la méthode tick () sera appelée chaque seconde dans le thread d'interface utilisateur pendant que votre activité est à l'état "repris". Bien sûr, vous pouvez changer la méthode tick () pour faire ce que vous voulez, ou être appelé plus ou moins fréquemment.

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

Pour les personnes intéressées, le code "_h0 = _handler" est nécessaire pour éviter que deux minuteries ne s'exécutent simultanément si votre activité est suspendue et reprise dans la période de tick.


2
Pourquoi cette _h0approche maladroite , au lieu de l' removeCallbacksintérieur onPause, comme tout le monde?
ToolmakerSteve

1

Vous pouvez également utiliser un animateur pour cela:

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

Vous devez créer un thread pour gérer la boucle de mise à jour et l'utiliser pour mettre à jour la zone de texte. Cependant, la partie délicate est que seul le thread principal peut réellement modifier l'interface utilisateur, de sorte que le thread de la boucle de mise à jour doit signaler au thread principal de faire la mise à jour. Cela se fait à l'aide d'un gestionnaire.

Consultez ce lien: http://developer.android.com/guide/topics/ui/dialogs.html# Cliquez sur la section intitulée "Exemple de ProgressDialog avec un deuxième fil". C'est un exemple de ce que vous devez faire exactement, sauf avec une boîte de dialogue de progression au lieu d'un champ de texte.


Ne fais pas ça. Il y a une classe de minuteur simple qui fait tout cela pour vous. Et cette question n'a rien à voir avec les dialogues de progression ou les dialogues.
Falmarri

Avez-vous regardé la section du lien que j'ai posté ou avez-vous simplement vu le mot dialogue et supposé? Le code est pertinent à 100%. Pour info aussi, si vous utilisez timer, vous créez toujours un thread pour gérer la boucle de mise à jour. Vous devrez toujours utiliser le gestionnaire comme décrit dans le lien que j'ai publié.
Nick

Malheureusement, la page liée ne contient plus de section avec le titre mentionné. Lors de la liaison au code, il faut toujours inclure l'extrait de clé directement dans votre réponse.
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
Peut-être qu'une indentation de code et des explications de code seraient utiles.
Raul Rene

0

Si quelqu'un est intéressé, j'ai commencé à jouer avec la création d'un objet standard à exécuter sur un thread d'interface utilisateur d'activités. Semble fonctionner correctement. Commentaires bienvenus. J'aimerais que cela soit disponible sur le concepteur de mise en page en tant que composant à faire glisser sur une activité. Je ne peux pas croire que quelque chose comme ça n'existe pas déjà.

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

Dans l'activité, j'ai ceci surStart:

@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
mec ce qui ne va pas avec l'ActivityTimerListener? Mon ADT Bundle a dit qu'il n'y a pas une telle classe.
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"/>


Je vois que vous avez ajouté cela plusieurs années après la question et les réponses d'origine. Veuillez ajouter des explications sur la façon dont cette réponse se compare aux autres réponses qui existaient déjà. Pourquoi en avez-vous ajouté un autre - quel avantage / quand utile / quelle lacune avez-vous vu dans d'autres réponses?
ToolmakerSteve

Je partage juste un code qui fait le même travail avec une approche différente. Mais chaque fois que vous souhaitez mettre à jour les données d'une vue. Vous devez utiliser le gestionnaire pour cela. car plusieurs fois j'ai remarqué que l'utilisation d'une tâche temporelle pour mettre à jour une vue ne fonctionne pas .. La méthode @ Dave.B est plus correcte à ma connaissance.
Zar E Ahmer

0

Si vous avez déjà du temps 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

J'ai résumé la minuterie et en ai fait une classe séparée:

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

Et extraire l'action principale de la Timerclasse comme

IAction.java

public interface IAction {
    void Task();
}

Et je l'ai utilisé comme ça:

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

J'espère que cela aide 😊👌


0

J'utilise de cette façon:

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

puis en dessous du 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();

c'est un moyen facile de résoudre ce problème.


0

Pour ceux qui ne peuvent pas compter sur Chronometer , j'ai fait une classe utilitaire à partir d'une des suggestions:

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

à utiliser .. il suffit de faire:

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

.....

 timerTextHelper.stop();
 long elapsedTime = timerTextHelper.getElapsedTime();
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.