編輯:關於Android編程
Calendar Provider是一個用於提供用戶標記在日歷上事件的數據倉庫。Calendar Provider 的API提供了包括增刪改查在內的一系列操作日歷事件的方法。
您可以在自己的應用程序或Adapter中使用Calendar Provider 的API。Adapter的相關用法將在本文的最後介紹。
通常來說,要訪問日歷應用程序中的數據,需要您在manifest中聲明權限;為了簡化操作, Calendar Provider 提供了一系列的Intents,使用這些Intents可以方便添加、查看、編輯 日歷應用程序中的事件(take users to the Calendar application to insert, view, and edit events),這樣一來,您可以與日歷應用程序交互以獲取日歷應用的數據信息,而且不需要您聲明權限,也不需要自己建立日歷事件的視圖。
本文將介紹Calendar Provider的相關內容,如需訪問官方原文,您可以點擊這個鏈接:《Calendar Provider》。
Content providers存儲了數據,並提供了API以便應用程序可以訪問Content providers管理的數據。Content providers管理的數據通常是關系型數據庫中的一系列表結構,表中的每一行表示一條記錄(record),每一列表示該記錄的一個特定屬性。通過Calendar Provider API,您可以方便地讀寫日歷應用程序中由Calendar Provider管理的一系列表中的記錄。
每一個content provider都向外界暴露了一個獨一無二的Uri
地址,通過這個地址,您可以訪問到這個content provider,以讀寫它所管轄的數據。所有的Uri都以content://
開頭。為了能訪問到Calendar Provider中表的數據,應當采用
的Uri格式,其中class就表示表名,如Events.CONTENT_URI
。
下圖展示了Calendar Provider管轄的數據模型,該模型由一個叫做Events
的表作為主表,通過該表可以鏈接至其他表:
CalendarContract
類中定義了Calendar Provider中各種表所對應的模型,並存儲相關信息,如下表所示:
CalendarContract.Calendars
該表中包含了具體的日歷信息(calendar-specific information),每一行表示一個日期中的具體內容,如名字,顏色,同步信息 等
CalendarContract.Events
該表中包含了具體的事件信息(event-specific information),每一行表示一個獨立的事件,如,事件標題、發生地點、起始時間、結束時間 等(event title, location, start time, end time, and so on)。可以將事件設置為一次性事件或重復事件,而參加者(Attendees)、提醒信息(reminders)、其他屬性信息(extended properties)都被存儲於相應的表中,這些表中都各自包含一個EVENT_ID
字段,作為Events
表的外鍵(一對多:一個事件可以有多個提醒信息 等。但一個提醒信息只能針對一個事件)
CalendarContract.Instances
該表記錄了事件的起始和結束時間。每一行表示一個事件的發生情況。對於一次性事件,該表和Events表是一對一關系(一對一:一個事件只有一對起訖時間,而一對起訖時間也只對應一個事件) ;對於重復事件,該表和Events表是多對多關系(多對多:一個事件可以有多對起訖時間,而一對起訖時間也可以應用於多個事件)
CalendarContract.Attendees
該表記錄了事件的參加者信息(holds the event attendee (guest) information),每一行表示了一個事件中的一個參加者信息。
CalendarContract.Reminders
該表記錄了警告和通知信息(alert/notification data),每一行表示一個事件的警告,一個事件可以有多個警告,但有最大警告數的限制,用MAX_REMINDERS
指定。提醒信息會在設定提醒時間之前的若干分鐘進行預提醒,以通知用戶做好准備
使用Calendar Provider API需要注意的事:
插入、更新、查看日歷事件(Inserting, updating, and viewing calendar events):為了讀寫Calendar Provider中的數據,需要您添加讀寫權限。若您並不打算開發一款功能齊全的日歷應用程序或者同步adapter,那麼您無需申請權限(, if you’re not building a full-fledged calendar application or sync adapter, requesting these permissions isn’t necessary),您可以使用Intents訪問系統自帶的日歷應用程序,並返回結果。有關Calendar Intents的內容,我將在後續翻譯。
同步adapter(Sync adapters):同步adapter可以從用戶的設備中同步日歷數據(A sync adapter synchronizes the calendar data on a user’s device with another server or data source)。CalendarContract.Calendars
和CalendarContract.Events
表中都提供了同步adapter的字段。Calendar Provider和其他應用程序不可修改這個字段中的內容。事實上,這對於外部應用是隱藏的。
為了查詢Calendar Provider中的數據,您應在manifest文件中添加READ_CALENDAR
權限,為了增、刪、改 Calendar Provider中的數據,您應在manifest文件中添加WRITE_CALENDAR
權限,如下所示:
...
CalendarContract.Calendars
表中包含了日歷的詳細信息。下面介紹了表中的字段信息。
NAME
日歷的名字
CALENDAR_DISPLAY_NAME
向用戶展示的日歷名字(The name of this calendar that is displayed to the user)
VISIBLE
boolean類型,用於決定是否將日歷展示給用戶。若值為0表示與該日歷綁定的事件不予顯示。值為1則相反。該值直接影響了CalendarContract.Instances
表中的內容
SYNC_EVENTS
boolean類型,表示是否同步設備中的日歷數據。
下面演示了查詢的示例,需要注意的是,您需要在異步線程中執行查詢操作:
// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCOUNT_NAME, // 1
Calendars.CALENDAR_DISPLAY_NAME, // 2
Calendars.OWNER_ACCOUNT // 3
};
// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
// Run query
Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
+ Calendars.ACCOUNT_TYPE + " = ?) AND ("
+ Calendars.OWNER_ACCOUNT + " = ?))";
String[] selectionArgs = new String[] {"[email protected]", "com.google",
"[email protected]"};
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
// Use the cursor to step through the returned records
while (cur.moveToNext()) {
long calID = 0;
String displayName = null;
String accountName = null;
String ownerName = null;
// Get the field values
calID = cur.getLong(PROJECTION_ID_INDEX);
displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
// Do something with the values...
...
}
修改日歷表實際上是修改表中的某一行或若干行數據,您可以直接在Uri中追加需要修改的行(調用withAppendedId()
),或使用selection 篩選行,selection 應為_id=?
形式,而selectionArg 是calendar表的_ID
,示例如下:
private static final String DEBUG_TAG = "MyActivity";
...
long calID = 2;
ContentValues values = new ContentValues();
// The new display name for the calendar
values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);
CalendarContract.Events
表記錄了事件的具體信息,為了操作該表,您同樣需要添加相應權限。
下面是Events 表中的字段信息:
CALENDAR_ID
Calender表的外鍵
ORGANIZER
事件的Email
TITLE
事件的標題
EVENT_LOCATION
事件的發生地點
DESCRIPTION
事件描述
DTSTART
事件起始時間
DTEND
事件結束時間
EVENT_TIMEZONE
事件發生的時區
EVENT_END_TIMEZONE
結束時間的時區
DURATION
事件持續時間,使用RFC5545 格式,如PT1H
表示事件將持續一小時,P2W
表示事件將持續兩周
ALL_DAY
boolean類型,表示是否為全天候事件,0表示“是全天候事件”,1表示“不是全天候事件”
RRULE
循環觸發事件的條件,如:FREQ=WEEKLY;COUNT=10;WKST=SU
,您可以參考這個例子
RDATE
事件循環的時間,您可以與RRULE一並使用
AVAILABILITY
將此事件視為忙碌時間還是可調度的空閒時間
GUESTS_CAN_MODIFY
來賓是否可修改事件
GUESTS_CAN_INVITE_OTHERS
是否可以邀請其他來賓
GUESTS_CAN_SEE_GUESTS
來賓是否可查看參加者列表
當您打算插入一條新的事件時,推薦使用INSERT Intent,當然您也可以直接插入事件,需要注意的事項如下:
必須包含CALENDAR_ID
和 DTSTART
字段;
必須包含EVENT_TIMEZONE
字段,調用getAvailableIDs()
方法獲取時區的ID。如使用Intent插入事件,則系統會自動包含時區信息;
若是一次性事件,則必須包含DTEND
字段;
對於一次性事件,應包含DURATION
字段,而不應包含RRULE 或 RDATE
字段,如使用Intent插入事件,則系統會自動包含事件的持續時間;
下面通過一個例子演示插入事件,您同樣需要把操作放在異步線程中執行:
long calID = 3;
long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 9, 14, 7, 30);
startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 9, 14, 8, 45);
endMillis = endTime.getTimeInMillis();
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, startMillis);
values.put(Events.DTEND, endMillis);
values.put(Events.TITLE, "Jazzercise");
values.put(Events.DESCRIPTION, "Group workout");
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
Uri uri = cr.insert(Events.CONTENT_URI, values);
// get the event ID that is the last element in the Uri
long eventID = Long.parseLong(uri.getLastPathSegment());
//
// ... do something with event ID
//
//
您可以通過返回的事件ID執行其他日歷操作,如在實踐中加入參加者或提醒(to add attendees or reminders to an event)。
推薦使用EDIT Intent
修改事件,但也可以手動修改,示例如下:
private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 188;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri updateUri = null;
// The new title for the event
values.put(Events.TITLE, "Kickboxing");
updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);
示例如下:
private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 201;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri deleteUri = null;
deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().delete(deleteUri, null, null);
Log.i(DEBUG_TAG, "Rows deleted: " + rows);
CalendarContract.Attendees
表的一行表示一個事件的參加者信息。調用query()
方法將返回某個事件(表中的EVENT_ID
字段為Event表的外鍵,該字段的值決定了參加者所屬的事件)的參加者列表。
下面介紹了參加者表的字段信息:
EVENT_ID
Event表的外鍵
ATTENDEE_NAME
參加者的姓名
ATTENDEE_EMAIL
參加者的Email
ATTENDEE_RELATIONSHIP
參加者的職責,下列值之一:RELATIONSHIP_ATTENDEE
、RELATIONSHIP_NONE
、RELATIONSHIP_ORGANIZER
、RELATIONSHIP_PERFORMER
、RELATIONSHIP_SPEAKER
ATTENDEE_TYPE
參加者的重要程度,下列值之一:TYPE_REQUIRED
、TYPE_OPTIONAL
ATTENDEE_STATUS
參加者的狀態,下列值之一:ATTENDEE_STATUS_ACCEPTED
、ATTENDEE_STATUS_DECLINED
、ATTENDEE_STATUS_INVITED
、ATTENDEE_STATUS_NONE
、ATTENDEE_STATUS_TENTATIVE
下面是一個添加參加者的示例,需要注意的是必須包含EVENT_ID
字段:
long eventID = 202;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Attendees.ATTENDEE_NAME, "Trevor");
values.put(Attendees.ATTENDEE_EMAIL, "[email protected]");
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
values.put(Attendees.EVENT_ID, eventID);
Uri uri = cr.insert(Attendees.CONTENT_URI, values);
CalendarContract.Reminders
表中的一行表示了某個事件的提醒事項,下面介紹該表的字段信息:
EVENT_ID
Event表的外鍵
MINUTES
事件發生前的分鐘數,應在達到該時間時發出提醒
METHOD
服務器上設置的提醒方法。下列值之一:METHOD_ALERT
、METHOD_DEFAULT
、METHOD_EMAIL
、METHOD_SMS
long eventID = 221;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Reminders.MINUTES, 15);
values.put(Reminders.EVENT_ID, eventID);
values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
Uri uri = cr.insert(Reminders.CONTENT_URI, values);
CalendarContract.Instances
表中的每一行記錄了一個事件的起訖時間,該表中的內容不可修改,只能查詢:
BEGIN
實例的開始時間,以協調世界時毫秒數表示
END
實例的結束時間,以協調世界時毫秒數表示
END_DAY
與日歷時區相應的實例儒略歷結束日(The Julian end day of the instance, relative to the Calendar’s time zone)
END_MINUTE
從日歷時區午夜開始計算的實例結束時間(分鐘)
EVENT_ID
Event表的主鍵
START_DAY
與日歷時區相應的實例儒略歷開始日
START_MINUTE
從日歷時區午夜開始計算的實例開始時間(分鐘)
private static final String DEBUG_TAG = "MyActivity";
public static final String[] INSTANCE_PROJECTION = new String[] {
Instances.EVENT_ID, // 0
Instances.BEGIN, // 1
Instances.TITLE // 2
};
// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_BEGIN_INDEX = 1;
private static final int PROJECTION_TITLE_INDEX = 2;
...
// Specify the date range you want to search for recurring
// event instances
Calendar beginTime = Calendar.getInstance();
beginTime.set(2011, 9, 23, 8, 0);
long startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2011, 10, 24, 8, 0);
long endMillis = endTime.getTimeInMillis();
Cursor cur = null;
ContentResolver cr = getContentResolver();
// The ID of the recurring event whose instances you are searching
// for in the Instances table
String selection = Instances.EVENT_ID + " = ?";
String[] selectionArgs = new String[] {"207"};
// Construct the query with the desired date range.
Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);
// Submit the query
cur = cr.query(builder.build(),
INSTANCE_PROJECTION,
selection,
selectionArgs,
null);
while (cur.moveToNext()) {
String title = null;
long eventID = 0;
long beginVal = 0;
// Get the field values
eventID = cur.getLong(PROJECTION_ID_INDEX);
beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
title = cur.getString(PROJECTION_TITLE_INDEX);
// Do something with the values.
Log.i(DEBUG_TAG, "Event: " + title);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(beginVal);
DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));
}
}
使用Calendar Intents,您無需添加權限,就能訪問Calendar Provider管理的表數據。
下面介紹了由Calendar Provider支持的Intent:
VIEW
content://com.android.calendar/time/
打開日歷後定位到
指定的時間
無
VIEW
content://com.android.calendar/events/
查看
指定的事件
CalendarContract.EXTRA_EVENT_BEGIN_TIME
CalendarContract.EXTRA_EVENT_END_TIME
EDIT
content://com.android.calendar/events/
編輯
指定的事件
CalendarContract.EXTRA_EVENT_BEGIN_TIME
CalendarContract.EXTRA_EVENT_END_TIME
EDIT
INSERT
content://com.android.calendar/events
創建事件
下表列出的任一 Extra
Events.TITLE
事件的名字
CalendarContract.EXTRA_EVENT_BEGIN_TIME
事件開始時間,以從公元紀年開始計算的毫秒數表示
CalendarContract.EXTRA_EVENT_END_TIME
事件結束時間,以從公元紀年開始計算的毫秒數表示
CalendarContract.EXTRA_EVENT_ALL_DAY
boolean類型,0表示全天候事件,1表示非全天候事件
Events.EVENT_LOCATION
事件的地點
Events.DESCRIPTION
事件描述
Intent.EXTRA_EMAIL
受邀者Email地址列表,以逗號分隔
Events.RRULE
事件的重復發生規則
Events.ACCESS_LEVEL
事件是私人性質還是公共性質
Events.AVAILABILITY
將此事件視為忙碌時間還是可調度的空閒時間
當您在自己的應用中使用Intent啟動日歷應用程序時,應用會轉到日歷來完成事件添加操作,可以將預填充日歷事件的詳細信息附加在INSERT
Intent中的extra 字段裡,待程序切換至日歷應用後,這些信息會自動填充至相應的欄目中,用戶只需選擇保存、編輯、取消 等選項即可。
下面通過一段代碼演示了添加一條發生於2012年1月19日的待辦事件,該事件的起始時間為上午7:30 ,結束時間為上午8:30:
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 0, 19, 7, 30);
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 0, 19, 8, 30);
Intent intent = new Intent(Intent.ACTION_INSERT)
.setData(Events.CONTENT_URI)
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
.putExtra(Events.TITLE, "Yoga")
.putExtra(Events.DESCRIPTION, "Group class")
.putExtra(Events.EVENT_LOCATION, "The gym")
.putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
.putExtra(Intent.EXTRA_EMAIL, "[email protected],[email protected]");
startActivity(intent);
long eventID = 208;
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_EDIT)
.setData(uri)
.putExtra(Events.TITLE, "My New Title");
startActivity(intent);
查看日歷信息,有兩種查看結果:
查詢指定日期的日歷信息;
查詢指定日期的事件。
查看指定日期的日歷信息如下:
// A date-time specified in milliseconds since the epoch.
long startMillis;
...
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
ContentUris.appendId(builder, startMillis);
Intent intent = new Intent(Intent.ACTION_VIEW)
.setData(builder.build());
startActivity(intent);
查看具體事件如下:
long eventID = 208;
...
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_VIEW)
.setData(uri);
startActivity(intent);
使用同步Adapters訪問Calendar Provider中數據的方式 與上述方式差別不大:
同步Adapter需要通過將 CALLER_IS_SYNCADAPTER
設置為 true
來表明它是同步適配器;
需提供 ACCOUNT_NAME
和 ACCOUNT_TYPE
作為 URI
中的查詢參數;
與應用或小工具相比,同步適配器擁有寫入權限的列更多。 例如,應用只能修改日歷的少數幾種特性, 例如其名稱、顯示名稱、能見度設置以及是否同步日歷。 相比之下,同步適配器不僅可以訪問這些列,還能訪問許多其他列, 例如日歷顏色、時區、訪問級別、地點等等。不過,同步適配器受限於它指定的 ACCOUNT_NAME
和 ACCOUNT_TYPE
。
同步Adapter的示例如下:
static Uri asSyncAdapter(Uri uri, String account, String accountType) {
return uri.buildUpon()
.appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
.appendQueryParameter(Calendars.ACCOUNT_NAME, account)
.appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
}
我個人感覺安卓自帶的數據庫用的不是太多的,畢竟現在很多應用都直接和服務器數據庫進行交互,或者直接API獲取一些接口的數據,但是不可否認自帶的數據庫還是有一些作用的,所以我
本文實例講述了Android編程之基於Log演示一個activity生命周期。分享給大家供大家參考,具體如下:利用Android的Log 演示一個activity的生命周
項目地址:RingProgressBar簡介:一個簡單實現的自定義圓環進度條,可使用於文件的上傳下載圖片加載等地方.A material design circle th
SearchView是搜索框組件,它可以讓用戶在文本框裡輸入文字,通過監聽器取得用戶的輸入,當用戶點擊搜索時,監聽器執行實際的搜索。本文就為大家分享了SearchView