2012年05月18日

Handlerを使ったカウントアップタイマー(ストップウォッチ)


Androidで定期的に処理を実行する方法にHandlerのsendMessageDelayed()を使う方法があります
これを利用してカウントアップタイマー(ストップウォッチ)を作成します

Androidはシングル・スレッド モデルを基本としています
シングル・スレッド モデルとはアプリケーションが一度に 1 つの仕事しかできないようになっています
つまり例えば画面描写中にほかのプログラムで画面描写を行うことができないという感じです
これを利用してのカウントダウンタイマー作成を行いたいと思います

少々難しいことを書きましたがHandlerを使ってカウントアップタイマーを作ろうと思ったのは
カウントアップには前回Chronometerを使ったものを紹介しました
Chronometerを使ったカウントアップタイマー
これで簡単にstart/stop/clearが使用できますがこれでは1秒以下の表記ができないからです
(0.5秒とか)
そのためにストップウォッチのようなカウントアップを作成するためにHandlerの選択になりました

Handler sendMessageDelayed()を使う
まずはHandlerを拡張した独自クラスLoopEngineを作成します
その中にstart()メソッドstop()メソッドhandleMessageを作成します
class Loopengine
    //一定時間後にupdateを呼ぶためのオブジェクト
    class LoopEngine extends Handler {
        private boolean isUpdate;
        public void start(){
                this.isUpdate = true;
                handleMessage(new Message());
        }
        public void stop(){
                this.isUpdate = false;
        }
        @Override
        public void handleMessage(Message msg) {
                this.removeMessages(0);//既存のメッセージは削除
                if(this.isUpdate){
                	MainActivity.this.update();//自信が発したメッセージを取得してupdateを実行
                	sendMessageDelayed(obtainMessage(0), 100);//100ミリ秒後にメッセージを出力
                }
        } 
    };
まずはstart()を呼び出すとhandleMessageが呼び出されます
HandleMessageではsendMessageDelayedで一定時間ウェイトしてメッセージを出力しています
つまり (1)start()メソッド実行
(2)HandleMessage呼び出す
(3)MainActivity.this.update()を実行
(4)sendMessageDelayedにより一定時間待ってメッセージ発行
(5)自身が発したメッセージを受け取りsendMessageDelayedを実行
以下(3)〜(5)を繰り返し
この繰り返しはActivity内でその他の操作・動作にかかわらず行えるため
Handlerを使ったマルチスレッドが活きてきます

今回はsendMessageDelayed(obtainMessage(0), 100);としているので100ミリ秒ごとに
updateが呼ばれることになります
このupdateにタイマーを更新するプログラムを書いておけばストップウォッチが作成できます

カウントアップタイマー(ストップウォッチ)
startボタンとstopボタンを備えた0.1秒まで測定できるストップウォッチを作成しました
まずはmain.xmlの作成です
ボタン2つに(startとstop)テキストビューを5つ配置しました
(分・秒・1/10秒とそれぞれの区切り)
layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00" android:textSize="30sp"/>


        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=":" android:textSize="30sp"/>

        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00" android:textSize="30sp"/>

        <TextView
            android:id="@+id/textView4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="." android:textSize="30sp"/>

        <TextView
            android:id="@+id/textView5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="0" android:textSize="30sp"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="start" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="stop" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

    </LinearLayout>
    
</LinearLayout>

なお文字が見やすいように文字サイズを30spにしてあります

次にMainActivity.javaです
こちらは先ほどのLoopEngineと各種ボタン動作
また、updateによるタイムの更新を作成しています
MainActivity.java
package blog.test;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
 
public class MainActivity extends Activity {
	TextView timer_m,timer_s,timer_ms;
	Button start,stop,restart;
	int minute,second,m_second;
	private LoopEngine loopEngine = new LoopEngine();
	private long startDate;
	@Override
	  protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.main); 
	    timer_m = (TextView)findViewById(R.id.textView1);
	    timer_s = (TextView)findViewById(R.id.textView3);
	    timer_ms = (TextView)findViewById(R.id.textView5);
	    start =(Button)findViewById(R.id.button1);
	    stop =(Button)findViewById(R.id.button2);

	    
	    start.setOnClickListener(new View.OnClickListener() {

	        public void onClick(View v) {
        		startDate =System.currentTimeMillis();
	        	loopEngine.start();
	        }
	    });
	    

	    stop.setOnClickListener(new OnClickListener(){
	        public void onClick(View v) {
	        	loopEngine.stop();
	        }
	    });
	    

	}	
	
	
	 public void update(){
		 	minute =(int)((((System.currentTimeMillis()-startDate))/1000)/60);
	       	second =(int)((((System.currentTimeMillis()-startDate))/1000)%60);
	       	m_second =(int)(((System.currentTimeMillis()-startDate)/10)%10);
	       	timer_m.setText(String.format("%1$02d",minute));
	       	timer_s.setText(String.format("%1$02d",second));
	       	timer_ms.setText(String.format("%1$01d",m_second));
	 }
	 
	 
    //一定時間後にupdateを呼ぶためのオブジェクト
    class LoopEngine extends Handler {
        private boolean isUpdate;
        public void start(){
                this.isUpdate = true;
                handleMessage(new Message());
        }
        public void stop(){
                this.isUpdate = false;
        }
        @Override
        public void handleMessage(Message msg) {
                this.removeMessages(0);//既存のメッセージは削除
                if(this.isUpdate){
                	MainActivity.this.update();//自信が発したメッセージを取得してupdateを実行
                	sendMessageDelayed(obtainMessage(0), 100);//100ミリ秒後にメッセージを出力
                }
        } 
    };

}


System.currentTimeMillis();では
現在の時刻をアンドロイドシステムからミリ秒で取得しています
startボタンを押すとstartDateに現在の時刻を記録
update内で再び現在の時刻を取得しstartDateを引いて経過時間を測定しています

またtimer_m.setText(String.format("%1$02d",minute));
ではminuteを2ケタ表示させ、1桁目に何もない時は“0”を表示させる設定にさせています

こちらを実行すると
device113.png
startボタンでタイマーがスタートし、stopボタンで止まります
device114.png


・・・え?動きがカクついてる・・?
それはupdateを呼び出すのが100ミリ秒ごとだからだと思います
ためしにsendMessageDelayed(obtainMessage(0), 1);とかにすると
だいぶスムーズになりますが負荷的にどうなんだろうww

スポンサードリンク

posted by kenken at 17:31 | Comment(0) | TrackBack(0) | タイマー カウントダウン | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。

この記事へのトラックバック