次の方法で共有


Xamarin.Android カレンダー

カレンダー API

Android 4 で導入された新しいセットのカレンダー API は、カレンダー プロバイダーに対するデータの読み取りまたは書き込みを行うように設計されたアプリケーションをサポートします。 これらの API は、イベント、出席者、リマインダーの読み取りと書き込みを行う機能など、カレンダー データとの豊富な対話オプションをサポートしています。 アプリケーションでカレンダー プロバイダーを使用すると、API を使用して追加したデータが、Android 4 に付属する組み込みのカレンダー アプリに表示されます。

アクセス許可の追加

アプリケーションで新しいカレンダー API を使用する場合、最初に行う必要があるのは、Android マニフェストに適切なアクセス許可を追加することです。 追加する必要があるアクセス許可は android.permisson.READ_CALENDARandroid.permission.WRITE_CALENDAR で、これは、カレンダー データの読み取りと書き込みのどちらを行っているか (あるいは両方) によって決まります。

カレンダー コントラクトの使用

アクセス許可を設定したら、CalendarContract クラスを使用してカレンダー データを操作できます。 このクラスは、アプリケーションがカレンダー プロバイダーと対話するときに使用できるデータ モデルを提供します。 CalendarContract により、アプリケーションは、URI をカレンダーやイベントなどのカレンダー エンティティに解決できます。 また、カレンダーの名前と ID、イベントの開始日と終了日など、各エンティティのさまざまなフィールドを操作する方法も提供されます。

Calendar API の使用例を見てみましょう。 この例では、カレンダーとそのイベントを列挙する方法と、カレンダーに新しいイベントを追加する方法について確認します。

カレンダーの一覧表示

まず、カレンダー アプリに登録されているカレンダーを列挙する方法を確認しましょう。 これを行うために、CursorLoader のインスタンスを作成できます。 CursorLoader は、Android 3.0 (API 11) で導入され、ContentProvider を利用するための推奨される方法です。 少なくとも、カレンダーのコンテンツ URI と返す必要がある列を指定する必要があります。この列の指定は "プロジェクション" と呼ばれます。

CursorLoader.LoadInBackground メソッドを呼び出すことにより、カレンダー プロバイダーなど、コンテンツ プロバイダーにデータに関するクエリを実行できます。 LoadInBackground は、実際の読み込み操作を実行し、クエリの結果を含む Cursor を返します。

CalendarContract は、コンテンツ Uri とプロジェクションの両方を指定する際に役立ちます。 カレンダーへのクエリを実行するためのコンテンツ Uri を取得するには、CalendarContract.Calendars.ContentUri プロパティを単に次のように使用するだけです。

var calendarsUri = CalendarContract.Calendars.ContentUri;

CalendarContract を使用して、必要なカレンダー列を指定することも、同様に単純です。 CalendarContract.Calendars.InterfaceConsts クラスのフィールドを配列に追加するだけです。 たとえば、次のコードには、カレンダーの ID、表示名、アカウント名が含まれています。

string[] calendarsProjection = {
    CalendarContract.Calendars.InterfaceConsts.Id,
    CalendarContract.Calendars.InterfaceConsts.CalendarDisplayName,
    CalendarContract.Calendars.InterfaceConsts.AccountName
};

後で説明するように、データを UI にバインドするために SimpleCursorAdapter を使用している場合は、Id を含めることが重要です。 以下に示すように、コンテンツ URI とプロジェクションが設定された状態で、CursorLoader をインスタンス化し、CursorLoader.LoadInBackground メソッドを呼び出して、カレンダー データを含むカーソルを返します。

var loader = new CursorLoader(this, calendarsUri, calendarsProjection, null, null, null);
var cursor = (ICursor)loader.LoadInBackground();

この例の UI には ListView が含まれており、リスト内の各項目は単一のカレンダーを表します。 次の XML は、ListView を含むマークアップを示しています。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
  <ListView
    android:id="@android:id/android:list"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />
</LinearLayout>

また、各リスト項目の UI を指定する必要があります。これは、次のように個別の XML ファイルに配置します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
  <TextView android:id="@+id/calDisplayName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="16dip" />
  <TextView android:id="@+id/calAccountName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="12dip" />
</LinearLayout>

これ以降は、カーソルから UI にデータをバインドするのは通常の Android コードです。 次のように SimpleCursorAdapter を使用します。

string[] sourceColumns = {
    CalendarContract.Calendars.InterfaceConsts.CalendarDisplayName,
    CalendarContract.Calendars.InterfaceConsts.AccountName };

int[] targetResources = {
    Resource.Id.calDisplayName, Resource.Id.calAccountName };      

SimpleCursorAdapter adapter = new SimpleCursorAdapter (this,
    Resource.Layout.CalListItem, cursor, sourceColumns, targetResources);

ListAdapter = adapter;

上記のコードでは、アダプターは sourceColumns 配列で指定された列を受け取り、カーソル内のカレンダー エントリごとにそれらを targetResources 配列のユーザー インターフェイス要素に書き込みます。 ここで使用されるアクティビティは、ListActivity のサブクラスです。これには、アダプターを設定する ListAdapter プロパティが含まれています。

以下は最終的な結果を示すスクリーンショットです。カレンダー情報が ListView に表示されています。

エミュレーターで実行され、2 つのカレンダー エントリを表示する CalendarDemo

カレンダー イベントの一覧表示

次に、特定のカレンダーのイベントを列挙する方法を見てみましょう。 上記の例に基づいて、ユーザーがいずれかのカレンダーを選択したときにイベントの一覧を表示します。 そのため、前のコードで項目の選択を処理する必要があります。

ListView.ItemClick += (sender, e) => {
    int i = (e as ItemEventArgs).Position;

    cursor.MoveToPosition(i);
    int calId =
        cursor.GetInt (cursor.GetColumnIndex (calendarsProjection [0]));

    var showEvents = new Intent(this, typeof(EventListActivity));
    showEvents.PutExtra("calId", calId);
    StartActivity(showEvents);
};

このコードでは、型 EventListActivity のアクティビティを開くための意図を作成し、その意図にカレンダーの ID を渡します。 どのカレンダーにイベントに関するクエリを実行するかを知るために ID が必要になります。 EventListActivityOnCreate メソッドで、次のように Intent から ID を取得できます。

_calId = Intent.GetIntExtra ("calId", -1);

次に、このカレンダー ID のイベントについてクエリを実行してみましょう。 イベントに関するクエリを実行するプロセスは、先ほどカレンダーの一覧についてクエリを実行した方法と同様ですが、今回は CalendarContract.Events クラスを使用します。 次のコードは、イベントを取得するクエリを作成します。

var eventsUri = CalendarContract.Events.ContentUri;

string[] eventsProjection = {
    CalendarContract.Events.InterfaceConsts.Id,
    CalendarContract.Events.InterfaceConsts.Title,
    CalendarContract.Events.InterfaceConsts.Dtstart
};

var loader = new CursorLoader(this, eventsUri, eventsProjection,
                   String.Format ("calendar_id={0}", _calId), null, "dtstart ASC");
var cursor = (ICursor)loader.LoadInBackground();

このコードでは、最初に CalendarContract.Events.ContentUri プロパティからイベントのコンテンツ Uri を取得します。 次に、eventsProjection 配列で取得するイベント列を指定します。 最後に、この情報を使用して CursorLoader のインスタンスを作成し、ローダーの LoadInBackground メソッドを呼び出して、イベント データを含む Cursor を返します。

UI にイベント データを表示するには、前にカレンダーの一覧を表示したときと同じように、マークアップとコードを使用できます。 ここでも、次のコードに示すように、SimpleCursorAdapter を使用してデータを ListView にバインドします。

string[] sourceColumns = {
    CalendarContract.Events.InterfaceConsts.Title,
    CalendarContract.Events.InterfaceConsts.Dtstart };

int[] targetResources = {
    Resource.Id.eventTitle,
    Resource.Id.eventStartDate };

var adapter = new SimpleCursorAdapter (this, Resource.Layout.EventListItem,
    cursor, sourceColumns, targetResources);

adapter.ViewBinder = new ViewBinder ();       
ListAdapter = adapter;

このコードと前にカレンダー リストを表示するために使用したコードの主な違いは、行に設定されている ViewBinder の使用です。

adapter.ViewBinder = new ViewBinder ();

この ViewBinder クラスを使用すると、値をビューにバインドする方法をさらに詳細に制御できます。 ここでは、次の実装に示すように、イベントの開始時刻をミリ秒から日付文字列に変換するためにこれを使用します。

class ViewBinder : Java.Lang.Object, SimpleCursorAdapter.IViewBinder
{    
    public bool SetViewValue (View view, Android.Database.ICursor cursor,
        int columnIndex)
    {
        if (columnIndex == 2) {
            long ms = cursor.GetLong (columnIndex);

            DateTime date = new DateTime (1970, 1, 1, 0, 0, 0,
                DateTimeKind.Utc).AddMilliseconds (ms).ToLocalTime ();

            TextView textView = (TextView)view;
            textView.Text = date.ToLongDateString ();

            return true;
        }
        return false;
    }    
}

これにより、以下のようにイベントの一覧が表示されます。

3 つの予定表イベントを表示するアプリの例のスクリーンショット

カレンダー イベントの追加

カレンダー データを読み取る方法について確認しました。 次に、カレンダーにイベントを追加する方法を見てみましょう。 これを行うには、先ほど説明した android.permission.WRITE_CALENDAR アクセス許可を必ず含めます。 カレンダーにイベントを追加するには、次のようにします。

  1. ContentValues インスタンスを作成します。
  2. CalendarContract.Events.InterfaceConsts クラスのキーを使用して、ContentValues インスタンスを設定します。
  3. イベントの開始および終了時刻のタイム ゾーンを設定します。
  4. ContentResolver を使用して、カレンダーにイベント データを挿入します。

以下のコードは、これらの手順を示しています。

ContentValues eventValues = new ContentValues ();

eventValues.Put (CalendarContract.Events.InterfaceConsts.CalendarId,
    _calId);
eventValues.Put (CalendarContract.Events.InterfaceConsts.Title,
    "Test Event from M4A");
eventValues.Put (CalendarContract.Events.InterfaceConsts.Description,
    "This is an event created from Xamarin.Android");
eventValues.Put (CalendarContract.Events.InterfaceConsts.Dtstart,
    GetDateTimeMS (2011, 12, 15, 10, 0));
eventValues.Put (CalendarContract.Events.InterfaceConsts.Dtend,
    GetDateTimeMS (2011, 12, 15, 11, 0));

eventValues.Put(CalendarContract.Events.InterfaceConsts.EventTimezone,
    "UTC");
eventValues.Put(CalendarContract.Events.InterfaceConsts.EventEndTimezone,
    "UTC");

var uri = ContentResolver.Insert (CalendarContract.Events.ContentUri,
    eventValues);

タイム ゾーンを設定しないと、型 Java.Lang.IllegalArgumentException の例外がスローされることに注意してください。 イベントの時刻値はエポックからのミリ秒単位で表す必要があるため、(EventListActivity の) GetDateTimeMS メソッドを作成して、日付指定をミリ秒形式に変換する必要があります。

long GetDateTimeMS (int yr, int month, int day, int hr, int min)
{
    Calendar c = Calendar.GetInstance (Java.Util.TimeZone.Default);

    c.Set (Java.Util.CalendarField.DayOfMonth, 15);
    c.Set (Java.Util.CalendarField.HourOfDay, hr);
    c.Set (Java.Util.CalendarField.Minute, min);
    c.Set (Java.Util.CalendarField.Month, Calendar.December);
    c.Set (Java.Util.CalendarField.Year, 2011);

    return c.TimeInMillis;
}

イベント リスト UI にボタンを追加し、ボタンのクリック イベント ハンドラーで上記のコードを実行すると、以下に示すように、イベントがカレンダーに追加され、一覧で更新されます。

カレンダー イベントの後に [サンプル イベントの追加] ボタンが続くアプリの例のスクリーンショット

カレンダー アプリを開くと、イベントもそこに書き込まれていることがわかります。

選択したカレンダー イベントが表示されているカレンダー アプリのスクリーンショット

お分かりのように、Android を使用すると、カレンダー データを取得して保持するための強力で簡単なアクセスが可能になるため、アプリケーションではカレンダー機能をシームレスに統合できます。