Календарь Xamarin.Android
API календаря
Новый набор API календаря, представленный в Android 4, поддерживает приложения, предназначенные для чтения или записи данных в поставщик календаря. Эти API поддерживают множество вариантов взаимодействия с данными календаря, включая возможность чтения и записи событий, участников и напоминаний. С помощью поставщика календаря в приложении данные, добавленные через API, будут отображаться в встроенном приложении календаря, которое поставляется с Android 4.
Добавление разрешений
При работе с новыми API календаря в приложении сначала необходимо добавить соответствующие разрешения в манифест Android. Необходимые разрешения зависят android.permisson.READ_CALENDAR
android.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 содержимого и проекцию, мы создадим экземпляр CursorLoader
CursorLoader.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
Перечисление событий календаря
Далее рассмотрим, как перечислить события для заданного календаря. На основе приведенного выше примера мы представим список событий, когда пользователь выбирает один из календарей. Поэтому нам потребуется обработать выбор элемента в предыдущем коде:
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();
В этом коде сначала мы получаем содержимое 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
разрешение, упоминание ранее. Чтобы добавить событие в календарь, мы будем:
- Создайте экземпляр
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
будет создано. Так как значения времени события должны быть выражены в миллисекундах с эпохи, мы создадим 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 позволяет эффективно и легко получать и сохранять данные календаря, позволяя приложениям легко интегрировать возможности календаря.