Xamarin.Android 行事曆

行事曆 API

Android 4 中引進的新一組行事曆 API 支援設計來讀取或寫入行事曆提供者的應用程式。 這些 API 支援與行事曆數據的豐富互動選項,包括讀取和寫入事件、出席者和提醒的能力。 藉由在應用程式中使用行事曆提供者,您透過 API 新增的數據會出現在 Android 4 隨附的內建行事曆應用程式中。

新增許可權

在應用程式中使用新的行事曆 API 時,您需要做的第一件事就是將適當的許可權新增至 Android 指令清單。 您需要新增的許可權是 android.permisson.READ_CALENDARandroid.permission.WRITE_CALENDAR,視您是要讀取和/或寫入行事曆數據而定。

使用行事曆合約

設定許可權之後,您可以使用 類別與行事曆資料 CalendarContract 互動。 這個類別提供數據模型,應用程式可以在與行事曆提供者互動時使用。 CalendarContract可讓應用程式將 URI 解析為行事曆實體,例如行事曆和事件。 它也提供一種方式來與每個實體中的各種欄位互動,例如行事曆的名稱和標識碼,或事件的開始和結束日期。

讓我們看看使用行事曆 API 的範例。 在此範例中,我們將檢查如何列舉行事曆及其事件,以及如何將新事件新增至行事曆。

列出行事曆

首先,讓我們檢查如何列舉已在行事曆應用程式中註冊的行事曆。 若要這樣做,我們可以具現化 CursorLoader。 在Android 3.0中引進(API 11), CursorLoader 是取用的 ContentProvider慣用方式。 我們至少必須指定行事曆的內容 URI,以及我們想要傳回的數據行;此數據行規格稱為 投影

CursorLoader.LoadInBackground呼叫 方法可讓我們查詢內容提供者的數據,例如行事曆提供者。 LoadInBackground 會執行實際的載入作業,並傳回 Cursor 具有查詢結果的 。

CalendarContract 協助我們同時指定內容 Uri 和投影。 若要取得查詢行事曆的內容 Uri ,我們只需要使用 CalendarContract.Calendars.ContentUri 如下的 屬性:

var calendarsUri = CalendarContract.Calendars.ContentUri;

CalendarContract使用 來指定我們想要的行事曆數據行同樣簡單。 我們只會將類別中的 CalendarContract.Calendars.InterfaceConsts 欄位新增至數位。 例如,下列程式代碼包含行事曆的識別碼、顯示名稱和帳戶名稱:

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

Id如果您使用 SimpleCursorAdapter 將數據系結至UI,請務必包含 ,因為我們很快就會看到。 在內容 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 使用者介面元素。 此處使用的 Activity 是 的 ListActivity子類別;它包含 ListAdapter 我們設定配接器的 屬性。

以下是顯示結束結果的螢幕快照,其中顯示行事曆資訊:ListView

在模擬器中執行的 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活動,並在意圖中傳遞行事曆的標識碼。 我們需要標識符,才能知道要查詢事件的行事曆。 EventListActivity在 的 方法中OnCreate,我們可以從 Intent 擷取標識碼,如下所示:

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

現在讓我們查詢此行事曆標識碼的事件。 查詢事件的程式類似於我們稍早查詢行事曆清單的方式,但這次我們將使用 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 我們稍早所述的許可權。 若要將事件新增至行事曆,我們將:

  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 例外狀況。 因為事件時間值必須以毫秒表示,因為自 epoch 之後,我們會建立 GetDateTimeMS 方法 (in EventListActivity) 將日期規格轉換成毫秒格式:

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,並在按鈕的 Click 事件處理程式中執行上述程式代碼,事件就會新增至行事曆,並在清單中更新,如下所示:

包含行事曆事件的範例應用程式螢幕快照,後面接著 [新增範例事件] 按鈕

如果我們開啟行事曆應用程式,我們也會看到事件也會寫入該處:

顯示所選取行事曆事件的行事曆應用程式螢幕快照

如您所見,Android 可讓您強大且輕鬆地存取擷取和保存行事曆數據,讓應用程式能夠順暢地整合行事歷功能。