web-dev-qa-db-fra.com

Android minuterie? Comment?

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 je dois calculer/mettre à jour les coordonnées de la balle toutes les secondes, c'est pourquoi j'ai besoin d'une sorte de chronomètre.

Je ne reçois rien de ici .

306
SERG

ok car ce n'est pas encore éclairci, il y a 3 façons simples de gérer ça. Vous trouverez ci-dessous un exemple illustrant les trois critères et en bas, un exemple indiquant uniquement la méthode que j'estime 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 principal d'interface utilisateur; utilisez donc un gestionnaire ou activity.runOnUIThread (Runnable r);

Voici ce que je considère être 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");
    }

}

443
Dave.B

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 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 l'OP). Dans Android SDK, il est recommandé d'utiliser la classe Handler (il existe un exemple dans la réponse acceptée).

81
fiction

Si vous devez également exécuter votre code sur le fil de l'interface utilisateur (et non sur le fil de la minuterie), consultez 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

    }
};
}
62
Meir Gerenstadt

Si vous souhaitez simplement planifier un compte à rebours dans le futur avec des notifications régulières sur les intervalles, vous pouvez utiliser la classe CountDownTimer 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();
36
Ahmed Hegazy

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);
23
Jevgenij Kononov

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 annuler ceci comme:

timerSubscribe.unsubscribe();

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

10
Will

Parce que cette question attire toujours beaucoup d'utilisateurs de la recherche Google (à propos de Android timer), j'aimerais insérer mes deux pièces.

Tout d'abord, la classe Timer sera obsolète dans Java 9 (lit la réponse acceptée) .

La méthode suggérée par officielle consiste à utiliser ScheduledThreadPoolExecutor qui est plus efficace et riche en fonctionnalités que vous pouvez également planifier des commandes à exécuter après un délai donné, 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 exécuteur:

    final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
    
  2. Il suffit de planifier votre runnable:

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

    future.isDone();
    

J'espère que vous trouverez cela 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
szholdiyarov

Je suis surpris qu'il n'y ait pas de réponse qui mentionne la solution avec RxJava2 . C'est très 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"

(remplacez x et y par numéro de version actuel )

Puisque nous avons juste un simple TÂCHE NON RÉPÉTITIVE , nous pouvons utiliser Completable objet:

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

Pour RÉPÉTITION DE TÂCHE , vous pouvez utiliser Observable de la même manière:

Observable(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 thread d'arrière-plan et .observeOn(AndroidSchedulers.mainThread()) signifie que le code que nous exécutons une fois que la minuterie est terminée sera créé 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.

5
Micer

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

Le meilleur moyen consiste à utiliser un gestionnaire utilisant postDelayed pour exécuter un Runnable après un délai (chaque exécution planifie la suivante); effacer le rappel avec removeCallbacks.

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

2
Liudvikas Bukys

Voici une solution plus simple, fonctionne 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
Rodion Altshuler

Voici un moyen simple et fiable ...

Mettez le code suivant dans votre activité, et la méthode tick () sera appelée toutes les secondes dans le fil de l'interface utilisateur tant que votre activité est à l'état "repris". Bien sûr, vous pouvez changer la méthode tick () pour faire ce que vous voulez ou pour ê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 fonctionnent simultanément si votre activité est suspendue et reprise au cours de la période de tick.

1
Adam Gawne-Cain

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

Vous devez créer un thread pour gérer la boucle de mise à jour et l'utiliser pour mettre à jour la zone de texte. La difficulté réside cependant dans le fait que seul le thread principal peut réellement modifier l'interface utilisateur. Le thread de la boucle de mise à jour doit donc signaler au thread principal d'effectuer la mise à jour. Ceci est fait en utilisant un gestionnaire.

Cliquez sur ce lien: http://developer.Android.com/guide/topics/ui/dialogs.html# Cliquez sur la section intitulée "Example ProgressDialog avec un second thread". 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.

0
Nick

Si vous avez déjà le 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
Matthew Hooker

Pour ceux qui ne peuvent pas compter sur Chronometer , j'ai créé une classe utilitaire à partir de l'une des suggestions suivantes:

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..à faire:

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

.....

 timerTextHelper.stop();
 long elapsedTime = timerTextHelper.getElapsedTime();
0
Alécio Carvalho

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 travailler ok. Commentaires bienvenus. J'adorerais que cette option soit disponible sur le maquettiste 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 sur Start:

@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();
}
0
James Barwick
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"/>
0
Xar E Ahmer
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 ");
}
0
kamilia jaber