くやしいのでAndroidStudioでストップウォッチ作る

スポンサーリンク
スポンサーリンク

リバーシの時間表示の時、訳わかんなくて悔しかったので、ストップウォッチ単体で作ってみる。つっても大体コピペコード。

スポンサーリンク

ストップウォッチ色々

Handlerだけ

動いてるとこ

ソース

public class StopWatch extends AppCompatActivity {
    Button btStart;
    Button btStop;
    TextView disp;
    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            count++;
            disp.setText(dataFormat.format(count*period));
            handler.postDelayed(this, period);
        }
    };
    int count, period;
    private SimpleDateFormat dataFormat =
            new SimpleDateFormat("mm:ss.S", Locale.US);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stop_watch);

        btStart = findViewById(R.id.button);
        btStop = findViewById(R.id.button2);
        disp = findViewById(R.id.textView);

        count = 0;
        period = 100;

    }

    public void onClickStart(View v){
        handler.post(runnable);
    }

    public void onClickStop(View v){
        handler.removeCallbacks(runnable);
        disp.setText(dataFormat.format(0));
        count = 0;
    }

所感

処理の順序が全然わからんかったのがちょっと分かった。

参考

[Android] カウントアップするタイマー、ストップウォッチをTimerTaskで作る
タイマーやストップウォッチはchronometerを使えば簡単にできますが、自由度があまりありません。カウントアップ専用のTimerTaskを使えば100msec刻みのタイマーが作れます。
[Android]指定した時間後にちょっとした処理を行う方法 | Developers.IO
◯秒後に処理を行いたい 最近ひさびさにAndroidにさわり、タイトルのような処理を行う必要があったので備忘録。 Alarmmanagerを使えば指定した時間に処理を行うこともできますが、そういう大げさなものでなく、 「 …

Timer・TimerTask使う

動いてるとこ

そーす

public class StopWatch extends AppCompatActivity {
    Button btStart;
    Button btStop;
    TextView disp;
    Handler handler = new Handler();
    int count;
    MyTimerTask timerTask = null;
    Timer mTimer = null;
    private SimpleDateFormat dataFormat = new SimpleDateFormat("mm:ss.S", Locale.US);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stop_watch);

        btStart = findViewById(R.id.button);
        btStop = findViewById(R.id.button2);
        disp = findViewById(R.id.textView);

        count = 0;

    }

    public void onClickStart(View v) {
        if (mTimer == null) {
            timerTask = new MyTimerTask();
            count = 0;
            mTimer = new Timer(true);
            mTimer.schedule(timerTask, 100, 100);
        }
    }

    public void onClickStop(View v) {
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
            disp.setText(dataFormat.format(0));
        }
    }

    class MyTimerTask extends TimerTask {

        @Override
        public void run() {
            handler.post(new Runnable() {
                public void run() {

                    count++;
                    disp.setText(dataFormat.format(count * 100));
                }
            });
        }
    }
}

所感

スタートボタン押すとmTimerが空かどうかチェック。mTimerにTimerをnewする時にtrueでデーモンスレッドにする。mTimer.schedule(timerTask, delay, period)でスケジュールをセット。timerTaskの中身は、handlerでメインスレッドに投げる処理(1個目のrun)、カウントアップしてUIに表示させる処理(2個目のrun)、でこの2つをdelay, periodを100msの設定でセットしてるので、その間隔で2つの処理が実行される。この時本当は、scheduleではなく、scheduleAtFixedRateを使った方が良いみたい。

scheduleメソッド:前回のタスク実行後を基準にperiodミリ秒後が次のタイミング
scheduleAtFixedRateメソッド:初回のタスク実行時間を基準にperiodミリ秒 x n回後が次のタイミング

なので、scheduleだとタスク処理の時間分、次回タスク処理の開始が遅れるからかな?そうすると多分ちょっとずつ時間が遅れるんだろう。他にscheduleWithFixedDelayっていうのがあるみたい。

これ使った場合でも、Pauseした時にその時の値を保持しておいて、次回実行時にその値から始める、みたいにすれば一時停止ができるのかなぁ…なんとなく。

参考

Timerを使って定期的に実行する方法 | TechBooster
AndroidでのTimerによる定期実行について解説します。ストップウォッチを例題にTimer処理のポイントを3つ、紹介します。
https://blog.mumei-himazin.info/?p=474
ScheduledExecutorService 使い方メモ - Qiita
図で比べるとこんなかんじ schedule() - 指定した時間待機してから処理を実行する import java.util.concurrent.Executors; import java.util.concurre...

ScheduledExecutorService使う

動いてるとこ

ソース

public class StopWatch extends AppCompatActivity {
    Button btStart;
    Button btStop;
    Button btPause;
    TextView disp;
    Handler handler = new Handler();
    int count;
    MyTimerTask timerTask = null;
    ScheduledExecutorService scheduler = null;
    ScheduledFuture<?> sf = null;
    private SimpleDateFormat dataFormat = new SimpleDateFormat("mm:ss.S", Locale.US);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stop_watch);

        btStart = findViewById(R.id.button);
        btStop = findViewById(R.id.button2);
        btPause = findViewById(R.id.button3);
        disp = findViewById(R.id.textView);

        count = 0;
    }

    public void onClickStart(View v) {
        if (scheduler == null) {
            scheduler = Executors.newScheduledThreadPool(1);
            timerTask = new MyTimerTask();
            sf = scheduler.scheduleAtFixedRate(timerTask, 100, 100, MILLISECONDS);
            count = 0;
        }
    }

    public void onClickPause(View v) {

        if (sf == null) {
            return;
        }

        if (sf.isCancelled()) {
            sf = scheduler.scheduleAtFixedRate(timerTask, 100, 100, MILLISECONDS);
            btPause.setText("Pause");
        } else {
            sf.cancel(true);
            btPause.setText("Resume");
        }
    }

    public void onClickStop(View v) {
        if (scheduler != null) {
            scheduler.shutdown();
            scheduler = null;
            sf = null;
            disp.setText(dataFormat.format(0));
        }
    }

    class MyTimerTask implements Runnable {

        @Override
        public void run() {
            handler.post(new Runnable() {
                public void run() {
                    count++;
                    disp.setText(dataFormat.format(count * 100));
                }
            });
        }
    }
}

所感

使い方は大体Timer・TimerTaskと似てるみたい。初期化する時にスレッド数を指定するのと、scheduleAtFixedRateの引数が、RunnableなのでMyTimerTaskをextends TimerTaskからimplements Runnableに変更、あとTimeUnitが必要なのと、止める時shutdownになるのとかが違う。shutdownするとやっぱり破棄されるのかな?

ScheduledFuture<?>が肝で、そういえばこの後ろの<?>ってなんだろう、scheduleAtFixedRateの戻り値がScheduledFuture<?>なので変数sfを用意して受けてあげる。そんでcancel(boolean)でスケジュールを保留状態にできる。保留状態の時、sf.isCancelledがtrueになるので、それで処理を分けてあげると。難しいのう。

参考

Timerを使う。周期的に実行したり、一定時間後に実行したり
ScheduledExecutorService  |  Android Developers
Future (Java Platform SE 8)
interface,extend,implementのちがい
お世話になります、Javaを勉強しているのですが、interface,extend,implementの使い分けがわかりません。私の解釈としては、(1)interfaceは、グローバル変数の定義、グローバルメソッドの定義(実装はしない)。(2)... - Java 解決済 | 教えて!goo

うぬぬ

これを1から自分で作れと言われた場合、絶対できないと思う。まだなんとなーく、ふわーっとした理解だなぁ。

補記:activity_stop_watch.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".StopWatch">

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="166dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:text="00:00.00"
        app:autoSizeTextType="uniform"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="12dp"
        android:layout_marginBottom="8dp"
        android:onClick="onClickStart"
        android:text="Start"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.141" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:onClick="onClickStop"
        android:text="Stop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.141" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:onClick="onClickPause"
        android:text="Pause"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintStart_toEndOf="@+id/button"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.153" />
</android.support.constraint.ConstraintLayout>

 

コメント

タイトルとURLをコピーしました