web-dev-qa-db-fra.com

Minuteur Android mettant à jour une vue de texte (UI)

J'utilise une minuterie pour créer un chronomètre. Le minuteur fonctionne en augmentant une valeur entière. Je souhaite ensuite afficher cette valeur dans l'activité en mettant constamment à jour une vue de texte. 

Voici le code du service où j'essaie de mettre à jour la vue de texte de l'activité: 

protected static void startTimer() {
    isTimerRunning = true; 
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            elapsedTime += 1; //increase every sec
            StopWatch.time.setText(formatIntoHHMMSS(elapsedTime)); //this is the textview
        }
    }, 0, 1000);
}

J'ai eu une sorte d'erreur sur la mise à jour de l'interface utilisateur dans le mauvais fil. 

Comment puis-je adapter mon code pour accomplir cette tâche de mise à jour constante de la vue texte? 

45
JDS
protected static void startTimer() {
    isTimerRunning = true; 
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            elapsedTime += 1; //increase every sec
            mHandler.obtainMessage(1).sendToTarget();
        }
    }, 0, 1000);
}

public Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        StopWatch.time.setText(formatIntoHHMMSS(elapsedTime)); //this is the textview
    }
};

Le code ci-dessus fonctionnera ...

Remarque: Les gestionnaires doivent être créés dans votre thread principal pour que vous puissiez modifier le contenu de l'interface utilisateur.

88
Nirav

Utilisez plutôt Handler pour mettre à jour l'interface utilisateur toutes les X secondes. Voici une autre question qui montre un exemple: Répéter une tâche avec un délai?

Votre approche ne fonctionne pas car vous essayez de mettre à jour une interface utilisateur à partir d'un thread non-UI. Ceci n'est pas autorisé.

8
inazaruk
StopWatch.time.post(new Runnable() {
    StopWatch.time.setText(formatIntoHHMMSS(elapsedTime));
});

ce bloc de code est basé sur Handler mais vous n'avez pas besoin de créer votre propre instance Handler.

6
DongXu

TimerTask implémente Runnable, ce qui en ferait un thread. Vous ne pouvez pas mettre à jour le thread d'interface utilisateur principal directement à partir d'un thread différent sans aucun travail. Une chose à faire est d’utiliser Async Task pour créer le minuteur et publier une mise à jour toutes les secondes pour mettre à jour l’UI.

2
Ashterothi

Je suppose que StopWatch.time est une référence statique ou publique à votre TextView. Au lieu de cela, vous devez implémenter un BroadcastReceiver pour communiquer entre votre minuterie (qui s'exécute à partir d'un thread séparé) et votre TextView.

2
John Leehey

Vous pouvez utiliser l'utilitaire suivant:

/**
 * Created by Ofek on 19/08/2015.
 */
public class TaskScheduler extends Handler {
    private ArrayMap<Runnable,Runnable> tasks = new ArrayMap<>();
    public void scheduleAtFixedRate(final Runnable task,long delay,final long period) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.run();
                postDelayed(this, period);
            }
        };
        tasks.put(task, runnable);
        postDelayed(runnable, delay);
    }
    public void scheduleAtFixedRate(final Runnable task,final long period) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.run();
                postDelayed(this, period);
            }
        };
        tasks.put(task, runnable);
        runnable.run();
    }
    public void stop(Runnable task) {
        Runnable removed = tasks.remove(task);
        if (removed!=null) removeCallbacks(removed);
    }

}

Ensuite, n'importe où dans le code exécuté par le thread d'interface utilisateur, vous pouvez l'utiliser simplement comme ceci:

TaskScheduler timer = new TaskScheduler();
        timer.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                time.setText(simpleDateFormat.format(GamePlay.instance().getLevelTime()));
            }
        },1000);
2
Ofek Ron

vous pouvez utiliser Handler

ce code augmente un compteur toutes les secondes et show et met à jour la valeur du compteur sur un textView

public class MainActivity extends Activity {


    private Handler handler = new Handler();
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        startTimer();
    }


    int i = 0;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            i++;
            textView.setText("counter:" + i);
            startTimer();
        }
    };

    public void startTimer() {
        handler.postDelayed(runnable, 1000);
    }

    public void cancelTimer() {
        handler.removeCallbacks(runnable);
    }

    @Override
    protected void onStop() {
        super.onStop();
        handler.removeCallbacks(runnable);
    }
}
1
faraz khonsari