Поделиться через


Календарь Xamarin.Android

API календаря

Новый набор API календаря, представленный в Android 4, поддерживает приложения, предназначенные для чтения или записи данных в поставщик календаря. Эти 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 для привязки данных к пользовательскому интерфейсу, как мы увидим в ближайшее время. Используя URI содержимого и проекцию, мы создадим экземпляр CursorLoaderCursorLoader.LoadInBackground метода для возврата курсора с данными календаря, как показано ниже:

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

Пользовательский интерфейс для этого примера содержит 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>

Кроме того, необходимо указать пользовательский интерфейс для каждого элемента списка, который мы помещаем в отдельный 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>

С этого момента просто обычный код 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

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Действия, передав идентификатор календаря в намерении. Нам потребуется идентификатор, чтобы узнать, какой календарь будет запрашивать события. В методе EventListActivityOnCreate можно получить идентификатор из приведенного 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();

В этом коде сначала мы получаем содержимое Uri для событий из CalendarContract.Events.ContentUri свойства. Затем мы указываем столбцы событий, которые мы хотим получить в массиве eventsProjection. Наконец, мы создадим экземпляр CursorLoader с этой информацией и вызовем метод загрузчика LoadInBackground для возврата Cursor с данными события.

Чтобы отобразить данные событий в пользовательском интерфейсе, можно использовать разметку и код, как и раньше, чтобы отобразить список календарей. Опять же, мы используем 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 будет создано. Так как значения времени события должны быть выражены в миллисекундах с эпохи, мы создадим GetDateTimeMS метод (в 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;
}

Если мы добавим кнопку в пользовательский интерфейс списка событий и запустите приведенный выше код в обработчике событий нажатия кнопки, событие добавляется в календарь и обновляется в нашем списке, как показано ниже:

Снимок экрана: пример приложения с событиями календаря, за которым следует кнопка

Если мы открываем приложение календаря, то мы увидим, что событие записывается там, а также:

Снимок экрана: приложение календаря с выбранным событием календаря

Как видно, Android позволяет эффективно и легко получать и сохранять данные календаря, позволяя приложениям легко интегрировать возможности календаря.