Xamarin.Android Kalender

Kalender-API

Eine neue Reihe von Kalender-APIs, die in Android 4 eingeführt wurden, unterstützt Anwendungen, die zum Lesen oder Schreiben von Daten in den Kalenderanbieter konzipiert sind. Diese APIs unterstützen eine Vielzahl von Interaktionsoptionen mit Kalenderdaten, einschließlich der Möglichkeit zum Lesen und Schreiben von Ereignissen, Teilnehmern und Erinnerungen. Wenn Sie den Kalenderanbieter in Ihrer Anwendung verwenden, werden Daten, die Sie über die API hinzufügen, in der integrierten Kalender-App angezeigt, die mit Android 4 enthalten ist.

Hinzufügen von Berechtigungen

Wenn Sie mit den neuen Kalender-APIs in Ihrer Anwendung arbeiten, müssen Sie zunächst dem Android-Manifest die entsprechenden Berechtigungen hinzufügen. Die Berechtigungen, die Sie hinzufügen müssen, sind android.permisson.READ_CALENDAR und android.permission.WRITE_CALENDAR, je nachdem, ob Sie Kalenderdaten lesen und/oder schreiben.

Verwenden des Kalendervertrags

Nachdem Sie die Berechtigungen festgelegt haben, können Sie mithilfe der -Klasse mit Kalenderdaten CalendarContract interagieren. Diese Klasse stellt ein Datenmodell bereit, das Anwendungen verwenden können, wenn sie mit dem Kalenderanbieter interagieren. Mit CalendarContract können Anwendungen die Uris in Kalenderentitäten auflösen, z. B. Kalender und Ereignisse. Es bietet auch eine Möglichkeit, mit verschiedenen Feldern in jeder Entität zu interagieren, z. B. dem Namen und der ID eines Kalenders oder dem Start- und Enddatum eines Ereignisses.

Sehen wir uns ein Beispiel an, das die Kalender-API verwendet. In diesem Beispiel wird untersucht, wie Sie Kalender und deren Ereignisse auflisten und einem Kalender ein neues Ereignis hinzufügen.

Auflisten von Kalendern

Zunächst untersuchen wir, wie Sie die Kalender auflisten, die in der Kalender-App registriert wurden. Dazu können wir eine instanziieren CursorLoader. In Android 3.0 (API 11) eingeführt, CursorLoader ist die bevorzugte Methode zum Verwenden von ContentProvider. Mindestens müssen wir den Inhalts-URI für Kalender und die Spalten angeben, die wir zurückgeben möchten. Diese Spaltenspezifikation wird als Projektion bezeichnet.

Das Aufrufen der CursorLoader.LoadInBackground -Methode ermöglicht es uns, einen Inhaltsanbieter nach Daten abzufragen, z. B. den Kalenderanbieter. LoadInBackground führt den tatsächlichen Ladevorgang aus und gibt einen Cursor mit den Ergebnissen der Abfrage zurück.

Der CalendarContract hilft uns dabei, sowohl den Inhalt Uri als auch die Projektion anzugeben. Um den Inhalt Uri zum Abfragen von Kalendern abzurufen, können wir einfach die CalendarContract.Calendars.ContentUri -Eigenschaft wie folgt verwenden:

var calendarsUri = CalendarContract.Calendars.ContentUri;

Die Verwendung von CalendarContract zum Angeben der gewünschten Kalenderspalten ist ebenso einfach. Wir fügen einfach Felder in der CalendarContract.Calendars.InterfaceConsts -Klasse zu einem Array hinzu. Der folgende Code enthält beispielsweise die ID, den Anzeigenamen und den Kontonamen des Kalenders:

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

Es Id ist wichtig, dass Sie einschließen, wenn Sie ein SimpleCursorAdapter verwenden, um die Daten an die Benutzeroberfläche zu binden, wie wir in Kürze sehen werden. Wenn der Inhalts-URI und die Projektion vorhanden sind, instanziieren wir die CursorLoader -Methode und rufen die CursorLoader.LoadInBackground -Methode auf, um einen Cursor mit den Kalenderdaten zurückzugeben, wie unten gezeigt:

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

Die Benutzeroberfläche für dieses Beispiel enthält eine ListView, wobei jedes Element in der Liste einen einzelnen Kalender darstellt. Im folgenden XML-Code wird das Markup angezeigt, das folgendes ListViewenthält:

<?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>

Außerdem müssen wir die Benutzeroberfläche für jedes Listenelement angeben, das wir wie folgt in einer separaten XML-Datei platzieren:

<?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>

Ab diesem Punkt ist es nur noch normaler Android-Code, die Daten vom Cursor an die Benutzeroberfläche zu binden. Wir verwenden ein SimpleCursorAdapter wie folgt:

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;

Im obigen Code übernimmt der Adapter die im sourceColumns Array angegebenen Spalten und schreibt sie für jeden Kalendereintrag im Cursor in die targetResources Benutzeroberflächenelemente im Array. Die hier verwendete Aktivität ist eine Unterklasse von ListActivity. Sie enthält die ListAdapter Eigenschaft, auf die wir den Adapter festlegen.

Hier ist ein Screenshot, der das Endergebnis zeigt, wobei die Kalenderinformationen im ListViewangezeigt werden:

CalendarDemo, das im Emulator ausgeführt wird und zwei Kalendereinträge anzeigt

Auflisten von Kalenderereignissen

Als Nächstes sehen wir uns an, wie sie die Ereignisse für einen bestimmten Kalender auflisten. Aufbauend auf dem obigen Beispiel wird eine Liste mit Ereignissen angezeigt, wenn der Benutzer einen der Kalender auswählt. Daher müssen wir die Elementauswahl im vorherigen Code behandeln:

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);
};

In diesem Code erstellen wir eine Absicht, um eine Aktivität vom Typ EventListActivityzu öffnen und die ID des Kalenders in der Absicht zu übergeben. Wir benötigen die ID, um zu wissen, welcher Kalender nach Ereignissen abfragt werden soll. In der EventListActivity's-Methode OnCreate können wir die ID wie unten gezeigt aus der Intent abrufen:

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

Nun fragen wir Ereignisse für diese Kalender-ID ab. Der Vorgang zum Abfragen von Ereignissen ähnelt der Art, wie wir zuvor nach einer Liste von Kalendern abgefragt haben. Nur dieses Mal arbeiten wir mit der CalendarContract.Events -Klasse. Der folgende Code erstellt eine Abfrage zum Abrufen von Ereignissen:

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();

In diesem Code wird zuerst der Inhalt Uri für Ereignisse aus der CalendarContract.Events.ContentUri -Eigenschaft abgerufen. Anschließend geben wir die Ereignisspalten an, die im array eventsProjection abgerufen werden sollen. Schließlich instanziieren wir ein CursorLoader mit diesen Informationen und rufen die Methode des LoadInBackground Ladeprogramms auf, um eine Cursor mit den Ereignisdaten zurückzugeben.

Um die Ereignisdaten auf der Benutzeroberfläche anzuzeigen, können wir wie zuvor Markup und Code verwenden, um die Liste der Kalender anzuzeigen. Auch hier wird verwendet SimpleCursorAdapter , um die Daten an ein ListView zu binden, wie im folgenden Code gezeigt:

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;

Der Standard Unterschied zwischen diesem Code und dem Code, den wir zuvor zum Anzeigen der Kalenderliste verwendet haben, ist die Verwendung eines ViewBinder, das in der Zeile festgelegt ist:

adapter.ViewBinder = new ViewBinder ();

Die ViewBinder -Klasse ermöglicht es uns, weiter zu steuern, wie wir Werte an Ansichten binden. In diesem Fall wird es verwendet, um die Startzeit des Ereignisses von Millisekunden in eine Datumszeichenfolge zu konvertieren, wie in der folgenden Implementierung gezeigt:

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;
    }    
}

Dadurch wird eine Liste der Ereignisse angezeigt, wie unten gezeigt:

Screenshot: Beispiel-App mit drei Kalenderereignissen

Hinzufügen eines Kalenderereignisses

Wir haben erfahren, wie Kalenderdaten gelesen werden. Nun sehen wir uns an, wie sie einem Kalender ein Ereignis hinzufügen. Damit dies funktioniert, stellen Sie sicher, dass Sie die android.permission.WRITE_CALENDAR zuvor erwähnte Berechtigung einschließen. Um einem Kalender ein Ereignis hinzuzufügen, gehen wir wie folgt vor:

  1. Erstellen Sie eine ContentValues-Instanz.
  2. Verwenden Sie Schlüssel aus der CalendarContract.Events.InterfaceConsts -Klasse, um die ContentValues instance aufzufüllen.
  3. Legen Sie die Zeitzonen für die Start- und Endzeiten des Ereignisses fest.
  4. Verwenden Sie ein ContentResolver , um die Ereignisdaten in den Kalender einzufügen.

Der folgende Code veranschaulicht die folgenden Schritte:

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);

Beachten Sie, dass eine Ausnahme vom Typ Java.Lang.IllegalArgumentException ausgelöst wird, wenn wir die Zeitzone nicht festlegen. Da Ereigniszeitwerte seit der Epoche in Millisekunden ausgedrückt werden müssen, erstellen wir eine GetDateTimeMS Methode (in EventListActivity), um unsere Datumsspezifikationen in das Millisekundenformat zu konvertieren:

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;
}

Wenn wir der Benutzeroberfläche der Ereignisliste eine Schaltfläche hinzufügen und den obigen Code im Klickereignishandler der Schaltfläche ausführen, wird das Ereignis dem Kalender hinzugefügt und wie unten dargestellt in unserer Liste aktualisiert:

Screenshot der Beispiel-App mit Kalenderereignissen gefolgt von der Schaltfläche

Wenn wir die Kalender-App öffnen, sehen wir, dass das Ereignis auch dort geschrieben ist:

Screenshot: Kalender-App mit dem ausgewählten Kalenderereignis

Wie Sie sehen, ermöglicht Android einen leistungsstarken und einfachen Zugriff zum Abrufen und Beibehalten von Kalenderdaten, sodass Anwendungen Kalenderfunktionen nahtlos integrieren können.