Android Time Picker

ユーザーが時刻を選択する方法を提供するには、TimePicker を使用できます。 Android アプリでは、通常、時刻値を選択するために TimePickerTimePickerDialog を合わせて使用します。これは複数のデバイスやアプリケーションにわたって一貫性のあるインターフェイスを確保するのに役立ちます。 TimePicker を使用すると、ユーザーは 24 時間制または 12 時間制の AM/PM モードで 1 日の中の時刻を選択できます。 TimePickerDialog は、TimePicker をダイアログ内にカプセル化するヘルパー クラスです。

動作しているタイム ピッカー ダイアログのスクリーンショットの例

概要

現代的な Android アプリケーションでは、TimePickerDialogDialogFragment 内に表示します。 これにより、アプリケーションは TimePicker をポップアップ ダイアログとして表示したり、アクティビティ内に埋め込んだりできます。 さらに、DialogFragment はダイアログのライフサイクルと表示を管理し、実装する必要があるコードの量を減らします。

このガイドでは、TimePickerDialogDialogFragment の中にラップされた状態で使用する方法を示します。 サンプル アプリケーションでは、ユーザーがアクティビティ上のボタンをクリックすると、モーダル ダイアログとして TimePickerDialog が表示されます。 時刻がユーザーによって設定されると、ダイアログが終了し、ハンドラーは選択された時刻を使用してアクティビティ画面上の TextView を更新します。

要件

このガイドのサンプル アプリケーションは Android 4.1 (API レベル 16) 以上を対象としていますが、Android 3.0 (API レベル 11 以上) でも使用できます。 Android Support Library v4 をプロジェクトに追加し、いくつかのコードを変更することで、古いバージョンの Android をサポートすることが可能です。

TimePicker の使用

この例では DialogFragment を拡張し、DialogFragment のサブクラス実装 (以下では TimePickerFragment と呼びます) で TimePickerDialog をホストし表示します。 サンプル アプリは、初期起動時に、選択された時刻を表示するために使用される TextView の上に、以下のように [PICK TIME] ボタンを表示します。

サンプル アプリの初期画面

[PICK TIME] ボタンをクリックすると、次のスクリーンショットに示すようにサンプル アプリが TimePickerDialog を起動します。

アプリによって表示される既定のタイム ピッカー ダイアログのスクリーンショット

TimePickerDialog において、時刻を選択し [OK] ボタンをクリックすると、TimePickerDialog によって IOnTimeSetListener.OnTimeSet というメソッドが起動されます。 このインターフェイスは、ホスティング DialogFragment (以下で説明される TimePickerFragment) によって実装されます。 [キャンセル] ボタンをクリックすると、フラグメントとダイアログが閉じます。

DialogFragment は、以下の 3 つの方法のいずれかで、選択された時刻をホスティング アクティビティに返します。

  1. メソッドの呼び出しまたはプロパティの設定 – アクティビティは、この値を設定するためのプロパティまたはメソッドを提供できます。

  2. イベントの発生DialogFragmentOnTimeSet が呼び出されたときに発生するイベントを定義できます。

  3. Action の使用DialogFragmentAction<DateTime> を呼び出して、アクティビティに時刻を表示できます。 アクティビティは、DialogFragment のインスタンスを作成するときに Action<DateTime を提供します。

このサンプルでは、3 番目の手法を使用します。これには、アクティビティが DialogFragment に対して Action<DateTime> ハンドラーを提供することが必要です。

アプリ プロジェクトを開始する

TimePickerDemo という名前の新しい Android プロジェクトを開始します (Xamarin.Android プロジェクトの作成に慣れていない場合は、「Hello, Android」を参照して、新しいプロジェクトを作成する方法を確認してください)。

Resources/layout/Main.axml を編集し、その内容を次の XML に置き換えます。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center_horizontal"
    android:padding="16dp">
    <Button
        android:id="@+id/select_button"
        android:paddingLeft="24dp"
        android:paddingRight="24dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="PICK TIME"
        android:textSize="20dp" />
    <TextView
        android:id="@+id/time_display"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:paddingTop="22dp"
        android:text="Picked time will be displayed here"
        android:textSize="24dp" />
</LinearLayout>

これは、時刻を表示する TextViewTimePickerDialog を開く Button を持つ基本的な LinearLayout です。 このレイアウトでは、アプリをより簡単かつ理解しやすくするために、ハードコーディングされた文字列とディメンションを使用しています。運用アプリでは通常、(DatePicker のコード例で確認できるように) これらの値にはリソースを使用します。

MainActivity.cs を編集し、内容を次のコードに置き換えます。

using Android.App;
using Android.Widget;
using Android.OS;
using System;
using Android.Util;
using Android.Text.Format;

namespace TimePickerDemo
{
    [Activity(Label = "TimePickerDemo", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        TextView timeDisplay;
        Button timeSelectButton;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.Main);
            timeDisplay = FindViewById<TextView>(Resource.Id.time_display);
            timeSelectButton = FindViewById<Button>(Resource.Id.select_button);
        }
    }
}

この例をビルドして実行すると、次のスクリーンショットのような初期画面が表示されるはずです。

アプリの初期画面

TimePicker を表示するための DialogFragment がまだ実装されていないため、[PICK TIME] ボタンをクリックしても何も起こりません。 次の手順では、この DialogFragment を作成します。

DialogFragment の拡張

TimePicker で使用するために DialogFragment を拡張するには、DialogFragment から派生した TimePickerDialog.IOnTimeSetListener を実装するサブクラスを作成することが必要です。 次のクラスを MainActivity.cs に追加します。

public class TimePickerFragment : DialogFragment, TimePickerDialog.IOnTimeSetListener
{
    public static readonly string TAG = "MyTimePickerFragment";
    Action<DateTime> timeSelectedHandler = delegate { };

    public static TimePickerFragment NewInstance(Action<DateTime> onTimeSelected)
    {
        TimePickerFragment frag = new TimePickerFragment();
        frag.timeSelectedHandler = onTimeSelected;
        return frag;
    }

    public override Dialog OnCreateDialog (Bundle savedInstanceState)
    {
        DateTime currentTime = DateTime.Now;
        bool is24HourFormat = DateFormat.Is24HourFormat(Activity);
        TimePickerDialog dialog = new TimePickerDialog
            (Activity, this, currentTime.Hour, currentTime.Minute, is24HourFormat);
        return dialog;
    }

    public void OnTimeSet(TimePicker view, int hourOfDay, int minute)
    {
        DateTime currentTime = DateTime.Now;
        DateTime selectedTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, hourOfDay, minute, 0);
        Log.Debug(TAG, selectedTime.ToLongTimeString());
        timeSelectedHandler (selectedTime);
    }
}

この TimePickerFragment クラスについては、次のセクションで小さいパーツに分けて説明します。

DialogFragment の実装

TimePickerFragment では、ファクトリ メソッド、Dialog インスタンス化メソッド、TimePickerDialog.IOnTimeSetListener が必要とする OnTimeSet ハンドラー メソッドなど、いくつかのメソッドを実装します。

  • TimePickerFragmentDialogFragment のサブクラスです。 また、以下のように TimePickerDialog.IOnTimeSetListener インターフェイスも実装します (つまり、必要な OnTimeSet メソッドを提供します)。

    public class TimePickerFragment : DialogFragment, TimePickerDialog.IOnTimeSetListener
    
  • TAG はログのために初期化されます (MyTimePickerFragment は使用したい任意の文字列に変更できます)。 timeSelectedHandler アクションは、以下のように、null 参照例外を防ぐための空のデリゲートに初期化されます。

    public static readonly string TAG = "MyTimePickerFragment";
    Action<DateTime> timeSelectedHandler = delegate { };
    
  • NewInstance ファクトリ メソッドは、新しい TimePickerFragment のインスタンスを作成するために呼び出されます。 このメソッドは、以下のように、TimePickerDialog 内でユーザーが [OK] ボタンをクリックしたときに呼び出される Action<DateTime> ハンドラーを受け取ります。

    public static TimePickerFragment NewInstance(Action<DateTime> onTimeSelected)
    {
        TimePickerFragment frag = new TimePickerFragment();
        frag.timeSelectedHandler = onTimeSelected;
        return frag;
    }
    
  • フラグメントを表示する必要がある場合、Android は DialogFragment のメソッドである OnCreateDialog を呼び出します。 このメソッドは、新しい TimePickerDialog オブジェクトを作成し、以下のように、それをアクティビティ、コールバック オブジェクト (TimePickerFragment の現在のインスタンス)、および現在の時刻を使用して初期化します。

    public override Dialog OnCreateDialog (Bundle savedInstanceState)
    {
        DateTime currentTime = DateTime.Now;
        bool is24HourFormat = DateFormat.Is24HourFormat(Activity);
        TimePickerDialog dialog = new TimePickerDialog
            (Activity, this, currentTime.Hour, currentTime.Minute, is24HourFormat);
        return dialog;
    }
    
  • ユーザーが TimePicker ダイアログの時刻設定を変更すると、OnTimeSet メソッドが呼び出されます。 OnTimeSet は、以下のように、現在の日付を使用して DateTime オブジェクトを作成し、ユーザーによって選択された時刻 (時間と分) にマージします。

    public void OnTimeSet(TimePicker view, int hourOfDay, int minute)
    {
        DateTime currentTime = DateTime.Now;
        DateTime selectedTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, hourOfDay, minute, 0);
    
  • DateTime オブジェクトは、作成時に TimePickerFragment オブジェクトに登録されている timeSelectedHandler に渡されます。 OnTimeSet は、以下のように、このハンドラーを呼び出して、アクティビティの時刻表示を選択された時刻に更新します (このハンドラーは次のセクションで実装されます)。

    timeSelectedHandler (selectedTime);
    

TimePickerFragment の表示

DialogFragment の実装が完了したので、以下のように NewInstance ファクトリ メソッドを使用して DialogFragment のインスタンスを作成し、DialogFragment.Show を呼び出すことでそれを表示します。

次のメソッドを MainActivity に追加します。

void TimeSelectOnClick (object sender, EventArgs eventArgs)
{
    TimePickerFragment frag = TimePickerFragment.NewInstance (
        delegate (DateTime time)
        {
            timeDisplay.Text = time.ToShortTimeString();
        });

    frag.Show(FragmentManager, TimePickerFragment.TAG);
}

TimeSelectOnClickTimePickerFragment のインスタンスを作成した後、アクティビティの時刻表示を渡された時刻値で更新する匿名メソッドのデリゲートを作成して渡します。 最後に、(DialogFragment.Show を介して) TimePicker ダイアログ フラグメントを起動して、TimePicker をユーザーに表示します。

OnCreate メソッドの最後に、次の行を追加して、ダイアログを起動する [PICK TIME] ボタンにイベント ハンドラーをアタッチします。

timeSelectButton.Click += TimeSelectOnClick;

[PICK TIME] ボタンがクリックされると、TimePicker ダイアログ フラグメントをユーザーに表示するために TimeSelectOnClick が呼び出されます。

手順を次に示します。

アプリをビルドし、実行します。 [PICK TIME] ボタンをクリックすると、以下のようにアクティビティの既定の時刻形式 (この場合は 12 時間制の AM/PM モード) で TimePickerDialog が表示されます。

タイム ダイアログが AM/PM モードで表示されます

TimePicker ダイアログで [OK] をクリックすると、以下のように、ハンドラーは選択された時刻でアクティビティの TextView を更新した後に終了します。

A/M 時刻が Activity TextView に表示されます

次に、次のコード行を OnCreateDialogis24HourFormat の宣言と初期化の直後に追加します。

is24HourFormat = true;

この変更により、TimePickerDialog コンストラクターに渡されるフラグは強制的に true になり、ホスティング アクティビティの時刻形式ではなく 24 時間制モードが使用されるようになります。 再度アプリをビルドして実行するときに、[PICK TIME] ボタンをクリックすると、以下のように、TimePicker ダイアログが 24 時間制形式で表示されるようになります。

24 時間形式の TimePicker ダイアログ

ハンドラーは DateTime.ToShortTimeString を呼び出して時刻をアクティビティの TextView に出力するため、この時刻は引き続き既定の 12 時間制 AM/PM 形式で表示されます。

まとめ

この記事では、Android アクティビティからポップアップ モーダル ダイアログとして TimePicker ウィジェットを表示する方法について説明しました。 サンプルの DialogFragment の実装を提供し、IOnTimeSetListener インターフェイスについて説明しました。 このサンプルでは、DialogFragment がどのようにホスト アクティビティとやり取りして選択された時刻を表示するのかも示しました。