Xamarin.Android カレンダー
カレンダー API
Android 4 で導入された新しいセットのカレンダー API は、カレンダー プロバイダーに対するデータの読み取りまたは書き込みを行うように設計されたアプリケーションをサポートします。 これらの API は、イベント、出席者、リマインダーの読み取りと書き込みを行う機能など、カレンダー データとの豊富な対話オプションをサポートしています。 アプリケーションでカレンダー プロバイダーを使用すると、API を使用して追加したデータが、Android 4 に付属する組み込みのカレンダー アプリに表示されます。
アクセス許可の追加
アプリケーションで新しいカレンダー API を使用する場合、最初に行う必要があるのは、Android マニフェストに適切なアクセス許可を追加することです。 追加する必要があるアクセス許可は android.permisson.READ_CALENDAR
と android.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
に表示されています。
カレンダー イベントの一覧表示
次に、特定のカレンダーのイベントを列挙する方法を見てみましょう。 上記の例に基づいて、ユーザーがいずれかのカレンダーを選択したときにイベントの一覧を表示します。 そのため、前のコードで項目の選択を処理する必要があります。
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 が必要になります。 EventListActivity
の OnCreate
メソッドで、次のように 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;
}
}
これにより、以下のようにイベントの一覧が表示されます。
カレンダー イベントの追加
カレンダー データを読み取る方法について確認しました。 次に、カレンダーにイベントを追加する方法を見てみましょう。 これを行うには、先ほど説明した android.permission.WRITE_CALENDAR
アクセス許可を必ず含めます。 カレンダーにイベントを追加するには、次のようにします。
ContentValues
インスタンスを作成します。CalendarContract.Events.InterfaceConsts
クラスのキーを使用して、ContentValues
インスタンスを設定します。- イベントの開始および終了時刻のタイム ゾーンを設定します。
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 を使用すると、カレンダー データを取得して保持するための強力で簡単なアクセスが可能になるため、アプリケーションではカレンダー機能をシームレスに統合できます。