リバーシの時間表示の時、訳わかんなくて悔しかったので、ストップウォッチ単体で作ってみる。つっても大体コピペコード。
ストップウォッチ色々
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;
}
所感
処理の順序が全然わからんかったのがちょっと分かった。
参考
https://akira-watson.com/android/timertask.html
https://dev.classmethod.jp/smartphone/android/android-tas/
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した時にその時の値を保持しておいて、次回実行時にその値から始める、みたいにすれば一時停止ができるのかなぁ…なんとなく。
参考
https://techbooster.org/android/application/934/
https://blog.mumei-himazin.info/?p=474
https://qiita.com/opengl-8080/items/ee8e926cf75e4d6058a2
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になるので、それで処理を分けてあげると。難しいのう。
参考
http://www.02.246.ne.jp/~torutk/javahow2/timer.html#doc1_id147
https://developer.android.com/reference/java/util/concurrent/ScheduledExecutorService
https://docs.oracle.com/javase/jp/8/docs/api/java/util/concurrent/Future.html
https://oshiete.goo.ne.jp/qa/111300.html
うぬぬ
これを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>
