2013年3月13日水曜日

Xamarin タイマーで表示を定期更新

タイマーを使ってみる単純な例として、0.5秒ごとに数を加算して表示するプログラムを作ってみます。

xamarin で使えるタイマーのクラスには何種類かあるようなのですが、System.Timers.timer クラスが簡単に使えそうだったので、これを使うことにします。
まず、文字を表示するために  TextView を一つ貼りつけたアクティビティを作成しておきます。(やり方は Xamarin でとても簡単な android アプリを作る なんかを参照)

コードはこんな感じ。

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.Timers;    // タイマーを使うので追加

namespace timer2
{
    [Activity (Label = "timer2", MainLauncher = true)]
    public class Activity1 : Activity
    {
        int count = 1;
        private System.Timers.Timer timer1;
        private TextView text1;
        Handler mHandler = new Handler ();

        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);

            // Set our view from the "main" layout resource
            SetContentView (Resource.Layout.Main);

            text1 = FindViewById<TextView> (Resource.Id.textView1);
            text1.TextSize = 64;    // さみしいので字を大きく
            
            timer1 = new System.Timers.Timer ();
            timer1.Enabled = true;
            timer1.AutoReset = true;    // 繰り返し
            timer1.Interval = 500;      // 500ms

            timer1.Elapsed += new ElapsedEventHandler (OnTimerEvent);

            timer1.Start ();    // タイマースタート

        }

        private void OnTimerEvent(object source, ElapsedEventArgs e)
        {
            count++;

            mHandler.Post (delegate {
                text1.Text = count.ToString ();
            });
        }
    }
}

けっこう簡単に書けました。

下のように書いても同じ。

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.Timers;    // タイマーを使うので追加

namespace timer2
{
    [Activity (Label = "timer2", MainLauncher = true)]
    public class Activity1 : Activity
    {
        int count = 1;
        private System.Timers.Timer timer1;
        private TextView text1;
        Handler mHandler = new Handler ();

        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);

            // Set our view from the "main" layout resource
            SetContentView (Resource.Layout.Main);

            text1 = FindViewById<textview> (Resource.Id.textView1);
            text1.TextSize = 64;    // さみしいので字を大きく
            
            timer1 = new System.Timers.Timer ();
            timer1.Enabled = true;
            timer1.AutoReset = true;    // 繰り返し
            timer1.Interval = 500;      // 500ms

            timer1.Elapsed += delegate {
                count++;
                
                mHandler.Post (delegate {
                    text1.Text = count.ToString ();
                });
            };

            timer1.Start ();    // タイマースタート
        }
    }
}



実行してみた様子。ちゃんと 0.5秒毎に数が増えて、表示されます。


ポイントは

    mHandler.Post (delegate {
        text1.Text = count.ToString ();
    });

の所。ここでタイマーイベントで起動したスレッドから、メインのスレッドに Text を更新してもらうようにお願いしています。

ここを普通に

    text1.Text = count.ToString ();

と書くと、メインのスレッドとは別のスレッドから text を更新する事になって表示が更新されません。
自分もよくわかっていないのですが、そういう事のようです(笑)。