android LiveFolder(活動資料夾) 完全解析
阿新 • • 發佈:2019-01-09
如果大家對 ContentProvider 還不熟悉的話 先看看這兩篇文章把 ContentProvider整的明白些 看起來會更順利。 【android 自定義 Content Provider示例】[url]http://byandby.iteye.com/blog/837466[/url] :!: 【android Content Provider的使用】[url]http://byandby.iteye.com/blog/836212[/url]
[b]探索活動資料夾[/b]
活動資料夾是在SDK1.5中引入的,支援開發人員在裝置的預設開啟螢幕(我們將其稱為裝置的主頁)上公開 ContentProvider,如聯絡人資訊、筆記和媒體。將ContentProvider(比如Android的 contactsContentProvider)在主頁上公開為活動資料夾之後,在聯絡人資料庫中新增、刪除或修改聯絡人時,此活動資料夾能夠重新整理自身所包含的內容。
Android中的活動資料夾對ContentProvider的作用就相當於RSS閱讀器對釋出網站的作用。ContentProvider也是類似於根據URI提供資訊的網站。隨著網站的迅速增加,每個網站都會以獨特的方式釋出自己的資訊,這就需要集中多個網站的資訊,以便使用者可以通過單一閱讀器瞭解最新發展動態。為此,RSS應運而生。RSS強制在不同的資訊集之間提供一種通用的使用模式。有了通用模式,你只需設計一次閱讀器,就可以使用它閱讀任何內容,只要該內容具有統一的結構即可。
活動資料夾在概念上也沒有什麼不同。就像 RSS閱讀器為所釋出的網站內容提桶通用的介面一樣,活動資料夾也為Android中的 ContentProvider定義一種通用介面。只要 ContentProvider遵守此協議,Android就能夠在裝置的主頁上建立活動資料夾圖示來表示該 ContentProvider。當用戶單擊此活動資料夾圖示時,系統將聯絡 ContentProvider。ContentProvider 應該會返回一個遊標。根據活動資料夾契約,此遊標必須具有一組預定義的列。此遊標通過 ListView 或 GridView直觀地顯示出來。
android活動資料夾 E文名 android LiveFolder live活 活動的意思 為什麼說它是活的,因為它可以根據我們後臺資料庫的變化更新自身 更新UI 這樣無論什麼時候顯示的內容都是最新的。 比如 我們刪除了一條聯絡人資訊,我們的 Live Foler馬上也會 更新。是馬上 而且你也不用做任何操作 它自己會更新。至於 具體的效果 大家還是下載原始碼執行一下 就明白了。
Live Folder的工作原理如下。
(1) 首先在主頁上建立一個圖示,表示來自ContentProvier的一組行。通過為圖示指定一個URI來進行連線。
(2) 當用戶單擊該圖示時,系統接受URI 並用它呼叫ContentProvider。ContentProvider通過遊標(Cursor)返回一組行。
(3) 只要此遊標包含Live Foler想要的列(比如名稱、描述和在單擊該行時呼叫的程式),系統就會以ListView或GridView的形式呈現這些行。
(4) 因為在基礎資料儲存更改時,ListView和GridView能夠更新自己的資料,所以這些檢視為 “活的”,“活動資料夾” 也因此而得名。
在活動資料夾中有兩個重要的原則。第一個原則是,列名稱在各個遊標中是相同的。此原則使Android能夠同等地對待面向活動資料夾的所有遊標。第二個原則是,Android檢視知道如何查詢基礎遊標資料中的更新,並相應地更改自身。第二個原則不是活動資料夾所特有的,適用於Android中所有的檢視,尤其是依賴於遊標的檢視。上面是一個活動資料夾的一些概念了,下面介紹使用者如何使用活動資料夾。
[b] 使用者如何使用活動資料夾[/b]
活動資料夾通過裝置的主頁向用戶公開。使用者可以按照下面的順序來使用活動資料夾。
(1) 開啟android模擬器 來到主頁 (預設螢幕)
(2) 轉到主頁的上下文選單。通過在主頁的空白處進行長單擊(按住不撒手 大約2秒鐘),就可以看到上下文選單了。
(3) 找到一個名為 Folders(中文名就叫資料夾) 的上下文選單選項,單擊可以檢視可能可用的活動資料夾。
(4) 從列表中選擇並單擊希望在主頁上公開的活動資料夾名稱。這會在主頁上建立一個圖示來表示所選的活動資料夾。
(5) 單擊在第4步中設定的活動資料夾圖示,調出 ListView或GridView中的資訊(該活動資料夾表示的資料)行。
(6) 單擊一行以呼叫知道如何顯示該行資料的應用程式。
(7) 使用該應用程式顯示的更多選單選項檢視或操作目標選項。也可以使用應用程式的選單選項建立它支援的任何新項。
(8) 請注意,活動資料夾顯示區域會自動反應對一個或多個項所做的更改。
下面針對上邊的每一步我們來看一些圖片
[img]http://dl.iteye.com/upload/attachment/485147/ebf19c0b-0c46-3090-aedd-71090061b908.jpg[/img]
[color=green]開啟android模擬器 來到主頁 (預設螢幕)[/color]
[img]http://dl.iteye.com/upload/attachment/485162/7e773b2c-d3f2-33b9-af79-601df20aa210.jpg[/img]
[color=green]轉到主頁的上下文選單。通過在主頁的空白處進行長單擊(按住不撒手 大約2秒鐘),就可以看到上下文選單了。
[/color]
[img]http://dl.iteye.com/upload/attachment/485166/7d110dcb-d09d-361c-a921-d037c26090f3.jpg[/img]
[color=green]找到一個名為 Folders(中文名就叫 資料夾) 的上下文選單選項,單擊可以檢視可能可用的活動資料夾。[/color]
[img]http://dl.iteye.com/upload/attachment/485168/cca80fd3-ff20-3c20-8835-59c6a4860b04.jpg[/img]
[color=green]從列表中選擇並單擊希望在主頁上公開的活動資料夾名稱。這會在主頁上建立一個圖示來表示所選的活動資料夾。[/color]
[img]http://dl.iteye.com/upload/attachment/485173/063fd940-6244-30a8-a12b-8ad229e0a176.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/485175/bf31c5a3-4d13-3a90-88bb-cd15d0c30c84.jpg[/img]
[color=green]單擊在第4步中設定的活動資料夾圖示,調出 ListView或GridView中的資訊(該活動資料夾表示的資料)行。[/color]
[img]http://dl.iteye.com/upload/attachment/485177/adf209d4-0eb3-379c-b133-abc8f77f41e9.jpg[/img]
[color=green]單擊一行以呼叫知道如何顯示該行資料的應用程式。[/color]
[img]http://dl.iteye.com/upload/attachment/485179/624af5fd-1276-3b8c-b15a-c61d3bdda789.jpg[/img]
[color=green]使用該應用程式顯示的更多選單選項檢視或操作目標選項。也可以使用應用程式的選單選項建立它支援的任何新項。[/color]
[img]http://dl.iteye.com/upload/attachment/485181/05e4ad5d-b990-3ddb-b0c6-9c2851b16a8e.jpg[/img]
[color=green]編輯聯絡人,請注意,活動資料夾顯示區域會自動反應對一個或多個項所做的更改。[/color]
[b] 構建活動資料夾[/b]
上邊瞭解了一些活動資料夾的基本概念之後,接下來介紹如何構建活動資料夾。構建了活動資料夾之後,可以在主頁上為該活動資料夾建立一個圖示。我們還將介紹活動資料夾 “活動” 的原理。
要構建活動資料夾,需要兩樣東西:一個活動和一個專門的ContentProvider。Android使用此活動的 “ 標籤” 來填充可用活動資料夾列表。Android還呼叫此活動來獲得一個URI,這個URI將被呼叫來顯示一組行。
活動提供的URI 應該指向負責返回行的專門的ContentProvider。該ContentProvider通過一個定義良好的遊標返回這些行。我們要求遊標 “定義良好”, 因為遊標應該具有一組已知的預定義列名稱。
通常,將這兩項打包到一個應用程式中,將後將該程式部署到裝置上。還需要一些支援檔案才能使所有的這一切生效。 我們將使用一個例子來演示和解釋這些概念,這個例子包含以下檔案。
AndroidManifest.xml:好像所有的android程式都需要這個檔案。。。。。。。。。
AllContactsLiveFolderCreatorActivity.java:此Activity 的功能簡單說 就是來建立一個 Live Folder。
MyContactsProvider.java:此ContentProvider將對返回聯絡人遊標的活動資料夾URI 進行響應。這個ContentProvider內部使用了 Android隨帶的聯絡人的 ContentProvider。
MyCursor.java:這個專門的遊標知道在基礎資料更改時如何執行 requery。
BetterCursorWrapper.java: MyCursor需要此檔案來編排 requery。
SimpleActivity.java:這是一個簡單的activity類 它是一個可選檔案,可用於在開發專案時進行測試。在最終的部署中不需要此檔案。
下面我們來分別介紹每一個檔案。
[color=red]1. AndroidManifest.xml[/color]
根據活動資料夾協議,CREATE_LIVE_FOLDER Intent 允許主頁的上下文選單將
AllContactsLiveFolderCreatorActivity 顯示為一個標題為 “My live foler” 的選項。單擊此選單選項將在主頁上建立一個圖示。AllContactsLiveFolderCreatorActivity 負責定義此圖示,其中將包含一個影象和一個標籤。在本例子中,AllContactsLiveFolderCreatorActivity 中的程式碼將此標籤指定為 Contacts LF。下面讓我們看看建立活動資料夾的類AllContactsLiveFolderCreatorActivity。
[color=red] 2. AllContactsLiveFolderCreatorActivity [/color]類只有一個功能:擔當活動資料夾的生成程式或建立程式。可以將它視為活動資料夾的模板。每次單擊此活動(通過主頁上下文選單的Folers選項)時,它都會在主頁上生成一個活動資料夾。此活動告訴呼叫方(在這個例子中為主頁或LiveFolder框架) 活動資料夾名稱、為活動資料夾圖示使用的影象、提供可用資料的 URI,以及顯示模式 (列表或網格)。然後,活動資料夾框架負責在主頁上建立活動資料夾的圖示。
API裡邊已經明確的說明 為我們的LiveFoler(活動資料夾) 提供內容的 ContentProvider 經過查詢返回的Cursor物件必須 包含以下 列名稱,也可以把它理解成使用LiveFolder的一個契約 我來是來張API 的截圖比較合適。
[img]http://dl.iteye.com/upload/attachment/485186/84e1b405-a474-3ea4-826b-2ceb82d02dbb.jpg[/img]
[color=green]大家仔細 看看 有些是必須的 有些是不必須的[/color]
createLiveFoler 方法主要設定呼叫它的Intent的值。當此 Intent返回到呼叫方時,呼叫方將知道以下資訊。
[color=green] 活動資料夾的名稱
活動資料夾所使用的圖示
顯示模式:列表或網格
資料或為資料呼叫的內容URI[/color]
這些資訊足夠用來建立我們的活動檔案夾了。當用戶單擊活動資料夾的圖示時,系統將呼叫 URI 來檢索資料。此 URI 所標識的 ContentProvider提供了標準化的遊標。接下來就介紹該ContentProvider的程式碼:MyContactsProvider類。
[color=red]3. MyContactsProvider.java類[/color]
MyContactsProvider 具有以下功能。
(1) 按此方式標識傳入的 URI: content:// com.ai.livefolders.contacts。
(2) 對Android提供的由content://com.android.contacts/contacts 標識的聯絡人 ContentProvider進行內部呼叫。
(3) 讀取遊標的所有行並將其映射回一個遊標(比如 MatrixCursor),後者具有活動資料夾框架所需的合適的列名稱。
(4) 將 MattixCursor包裝到另一個遊標中,以便經過包裝的遊標上的 requery 在需要時呼叫聯絡人 ContentProvider。
MyContactsProvider的程式碼清單如下
大部分欄位大家也都應該明白,可能INTENT項有點迷惑。先說一下 timesContacted 它是用來記錄 我們的聯絡人呼叫我我們幾次 比如你電話存著 媽媽的電話 timesContacted就記錄著 媽媽給你打了幾個電話,或者說呼叫了你幾次,還有就連這個也可以時時更新,所以說還是比較強大滴。
INTENT 欄位實際上是一個字串,指向 ContentProvider 中該項的URL。在使用者單擊該項時,Android將使用此URL 來呼叫VIEW操作。此字串欄位稱為 INTENT 欄位是因為,在內部,Android會從字串 URI 派生 INTENT。
另外請大家注意,上面的MyContactsProvider 包含了下面幾行程式碼
函式 loadNewData() 從聯絡人提供程式獲取一組聯絡人並建立 MatrixCursor,MatrixCursor物件包含我們需要的列。這段程式碼然後告訴 MatrixCursor 向 ContentResolver 註冊自身,以便在URI (content://com.android.contacts/contacts/) 所指向的資料以任何形式發生變化時,ContentResolver能夠提醒遊標也就是我們的Cursor物件。
將 MatrixCursor 包裝到我們自己的遊標中也很重要。
MyCursor物件是我們自己定義的一個Cursor物件,這裡要理解為什麼需要包裝遊標,需要了解檢視如何更新更改的內容。ContentProvider(比如Contacts)通常通過將一個URI 註冊為 query方法實現的一部分,告訴遊標它需要監視更改。這是通過 cursor.setNotificationUri完成的。遊標然後將向 ContentProvider 註冊此URI 及它的所有子URI。然後,當在 ContentProvider 上發生插入或刪除操作時,插入或刪除操作的程式碼需要發出一個事件,表示特定URI 所標識的行中的資料發生了更改。
這將出發遊標通過 requery進行更新,檢視也將相應更新。遺憾的是, MatrixCursor不適用此 requery。SQLiteCursor適用於它,但我們無法在這裡使用 SQLiteCursor,因為我們已將這些列對映到了一組新列。
為了解決這一限制,我們將MatrixCursor包裝到一個遊標包裝器中,並重寫 requery 方法以丟棄內部的 MatrixCursor,並使用更新的資料建立一個新遊標。更明確的講,每次資料更改時,我們都希望獲得新的 MatrixCursor。但我們僅向 Android活動資料夾框架返回所包裝的外部遊標。這將告訴活動資料夾框架只有一個遊標,但在後臺,當資料更改時我們會建立新遊標,我們下面來說說這兩個類。
[color=red]4. MyCursor.java[/color]
請注意,MyCursor最開始使用一個 MatrixCursor進行初始化。在requery上, MyCursor 將回調提供程式來返回一個 MatrixCursor。然後,新 MatrixCursor 將使用 set 方法替代舊 MatrixCursor。
[color=green]說明:可以通過重寫 MatrixCursor的requery來完成此任務,但該類無法清除資料並重新啟動。所以這是一種合理的解決辦法。(請注意,MyCursor擴充套件了 BetterCursorWrapper,接下來將討論。)[/color]
MyCursor 原始碼
現在看一下 BetterCursorWrapper類,瞭解如何包裝遊標。
[color=red]5. BetterCursorWrapper.java[/color]
BetterCursorWrapper類 非常類似於 Android資料庫框架中的 CursorWrapper類。但我們還需要 CursorWrapper 所缺少的另外兩項功能。首先,它沒有提供 set方法來替換 requery 方法中的內部遊標。其次, CursorWrapper 不是CrossProcessCursor。活動資料夾需要 CrossProcessCursor,而不是普通遊標,因為活動資料夾會跨程序邊界進行工作。
BetterCursorWrapper原始碼
大家看前幾個方法就行了,後邊方法都沒有用到 實現CrossProcessCursor這個介面就會實現這些方法。大家也可以用 Eclipse 自動生成一下,將游標放在 internalCursor上。用滑鼠右鍵單擊並選擇 Source---》GenerateDelegate Methods。Eclipse隨後將填充該類剩餘部分。現在看一下完成此示例需要的一個簡單activity.
[color=red]6 .SimpleActivity.java[/color]
SimpleActivity.java 不是活動資料夾必須的類,但在專案中包含它可以為所有專案提供一種通用模式。此外,它支援在通過 Eclipse除錯時,部署應用程式並在螢幕上檢視它。
SimpleActivity 原始碼
簡單的 XML 佈局檔案
現在,通過Eclipse構建、部署和執行示例活動資料夾專案所需的所有類都已就緒。我們可以執行試試了,剛開啟會開啟一個Activity 就是我們上邊的那個SimpleActivity 然後我們切換到主頁 按照我們 最開始 介紹的步驟就可以建立 活動檔案夾了,另外覺得覺得這個例子還有不足的地方,比如 我們可以自己定義一些聯絡人的頭像 然後從資料庫讀取出來,讓每個聯絡人都顯示自己的頭像。 歡迎大家 討論 批評 指點。
原始碼已上傳 :?:
[b]探索活動資料夾[/b]
活動資料夾是在SDK1.5中引入的,支援開發人員在裝置的預設開啟螢幕(我們將其稱為裝置的主頁)上公開 ContentProvider,如聯絡人資訊、筆記和媒體。將ContentProvider(比如Android的 contactsContentProvider)在主頁上公開為活動資料夾之後,在聯絡人資料庫中新增、刪除或修改聯絡人時,此活動資料夾能夠重新整理自身所包含的內容。
Android中的活動資料夾對ContentProvider的作用就相當於RSS閱讀器對釋出網站的作用。ContentProvider也是類似於根據URI提供資訊的網站。隨著網站的迅速增加,每個網站都會以獨特的方式釋出自己的資訊,這就需要集中多個網站的資訊,以便使用者可以通過單一閱讀器瞭解最新發展動態。為此,RSS應運而生。RSS強制在不同的資訊集之間提供一種通用的使用模式。有了通用模式,你只需設計一次閱讀器,就可以使用它閱讀任何內容,只要該內容具有統一的結構即可。
活動資料夾在概念上也沒有什麼不同。就像 RSS閱讀器為所釋出的網站內容提桶通用的介面一樣,活動資料夾也為Android中的 ContentProvider定義一種通用介面。只要 ContentProvider遵守此協議,Android就能夠在裝置的主頁上建立活動資料夾圖示來表示該 ContentProvider。當用戶單擊此活動資料夾圖示時,系統將聯絡 ContentProvider。ContentProvider 應該會返回一個遊標。根據活動資料夾契約,此遊標必須具有一組預定義的列。此遊標通過 ListView 或 GridView直觀地顯示出來。
android活動資料夾 E文名 android LiveFolder live活 活動的意思 為什麼說它是活的,因為它可以根據我們後臺資料庫的變化更新自身 更新UI 這樣無論什麼時候顯示的內容都是最新的。 比如 我們刪除了一條聯絡人資訊,我們的 Live Foler馬上也會 更新。是馬上 而且你也不用做任何操作 它自己會更新。至於 具體的效果 大家還是下載原始碼執行一下 就明白了。
Live Folder的工作原理如下。
(1) 首先在主頁上建立一個圖示,表示來自ContentProvier的一組行。通過為圖示指定一個URI來進行連線。
(2) 當用戶單擊該圖示時,系統接受URI 並用它呼叫ContentProvider。ContentProvider通過遊標(Cursor)返回一組行。
(3) 只要此遊標包含Live Foler想要的列(比如名稱、描述和在單擊該行時呼叫的程式),系統就會以ListView或GridView的形式呈現這些行。
(4) 因為在基礎資料儲存更改時,ListView和GridView能夠更新自己的資料,所以這些檢視為 “活的”,“活動資料夾” 也因此而得名。
在活動資料夾中有兩個重要的原則。第一個原則是,列名稱在各個遊標中是相同的。此原則使Android能夠同等地對待面向活動資料夾的所有遊標。第二個原則是,Android檢視知道如何查詢基礎遊標資料中的更新,並相應地更改自身。第二個原則不是活動資料夾所特有的,適用於Android中所有的檢視,尤其是依賴於遊標的檢視。上面是一個活動資料夾的一些概念了,下面介紹使用者如何使用活動資料夾。
[b] 使用者如何使用活動資料夾[/b]
活動資料夾通過裝置的主頁向用戶公開。使用者可以按照下面的順序來使用活動資料夾。
(1) 開啟android模擬器 來到主頁 (預設螢幕)
(2) 轉到主頁的上下文選單。通過在主頁的空白處進行長單擊(按住不撒手 大約2秒鐘),就可以看到上下文選單了。
(3) 找到一個名為 Folders(中文名就叫資料夾) 的上下文選單選項,單擊可以檢視可能可用的活動資料夾。
(4) 從列表中選擇並單擊希望在主頁上公開的活動資料夾名稱。這會在主頁上建立一個圖示來表示所選的活動資料夾。
(5) 單擊在第4步中設定的活動資料夾圖示,調出 ListView或GridView中的資訊(該活動資料夾表示的資料)行。
(6) 單擊一行以呼叫知道如何顯示該行資料的應用程式。
(7) 使用該應用程式顯示的更多選單選項檢視或操作目標選項。也可以使用應用程式的選單選項建立它支援的任何新項。
(8) 請注意,活動資料夾顯示區域會自動反應對一個或多個項所做的更改。
下面針對上邊的每一步我們來看一些圖片
[img]http://dl.iteye.com/upload/attachment/485147/ebf19c0b-0c46-3090-aedd-71090061b908.jpg[/img]
[color=green]開啟android模擬器 來到主頁 (預設螢幕)[/color]
[img]http://dl.iteye.com/upload/attachment/485162/7e773b2c-d3f2-33b9-af79-601df20aa210.jpg[/img]
[color=green]轉到主頁的上下文選單。通過在主頁的空白處進行長單擊(按住不撒手 大約2秒鐘),就可以看到上下文選單了。
[/color]
[img]http://dl.iteye.com/upload/attachment/485166/7d110dcb-d09d-361c-a921-d037c26090f3.jpg[/img]
[color=green]找到一個名為 Folders(中文名就叫 資料夾) 的上下文選單選項,單擊可以檢視可能可用的活動資料夾。[/color]
[img]http://dl.iteye.com/upload/attachment/485168/cca80fd3-ff20-3c20-8835-59c6a4860b04.jpg[/img]
[color=green]從列表中選擇並單擊希望在主頁上公開的活動資料夾名稱。這會在主頁上建立一個圖示來表示所選的活動資料夾。[/color]
[img]http://dl.iteye.com/upload/attachment/485173/063fd940-6244-30a8-a12b-8ad229e0a176.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/485175/bf31c5a3-4d13-3a90-88bb-cd15d0c30c84.jpg[/img]
[color=green]單擊在第4步中設定的活動資料夾圖示,調出 ListView或GridView中的資訊(該活動資料夾表示的資料)行。[/color]
[img]http://dl.iteye.com/upload/attachment/485177/adf209d4-0eb3-379c-b133-abc8f77f41e9.jpg[/img]
[color=green]單擊一行以呼叫知道如何顯示該行資料的應用程式。[/color]
[img]http://dl.iteye.com/upload/attachment/485179/624af5fd-1276-3b8c-b15a-c61d3bdda789.jpg[/img]
[color=green]使用該應用程式顯示的更多選單選項檢視或操作目標選項。也可以使用應用程式的選單選項建立它支援的任何新項。[/color]
[img]http://dl.iteye.com/upload/attachment/485181/05e4ad5d-b990-3ddb-b0c6-9c2851b16a8e.jpg[/img]
[color=green]編輯聯絡人,請注意,活動資料夾顯示區域會自動反應對一個或多個項所做的更改。[/color]
[b] 構建活動資料夾[/b]
上邊瞭解了一些活動資料夾的基本概念之後,接下來介紹如何構建活動資料夾。構建了活動資料夾之後,可以在主頁上為該活動資料夾建立一個圖示。我們還將介紹活動資料夾 “活動” 的原理。
要構建活動資料夾,需要兩樣東西:一個活動和一個專門的ContentProvider。Android使用此活動的 “ 標籤” 來填充可用活動資料夾列表。Android還呼叫此活動來獲得一個URI,這個URI將被呼叫來顯示一組行。
活動提供的URI 應該指向負責返回行的專門的ContentProvider。該ContentProvider通過一個定義良好的遊標返回這些行。我們要求遊標 “定義良好”, 因為遊標應該具有一組已知的預定義列名稱。
通常,將這兩項打包到一個應用程式中,將後將該程式部署到裝置上。還需要一些支援檔案才能使所有的這一切生效。 我們將使用一個例子來演示和解釋這些概念,這個例子包含以下檔案。
AndroidManifest.xml:好像所有的android程式都需要這個檔案。。。。。。。。。
AllContactsLiveFolderCreatorActivity.java:此Activity 的功能簡單說 就是來建立一個 Live Folder。
MyContactsProvider.java:此ContentProvider將對返回聯絡人遊標的活動資料夾URI 進行響應。這個ContentProvider內部使用了 Android隨帶的聯絡人的 ContentProvider。
MyCursor.java:這個專門的遊標知道在基礎資料更改時如何執行 requery。
BetterCursorWrapper.java: MyCursor需要此檔案來編排 requery。
SimpleActivity.java:這是一個簡單的activity類 它是一個可選檔案,可用於在開發專案時進行測試。在最終的部署中不需要此檔案。
下面我們來分別介紹每一個檔案。
[color=red]1. AndroidManifest.xml[/color]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xiaohang.zhimeng" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".SimpleActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- LIVE FOLDERS -->
<activity android:name=".AllContactsLiveFolderCreatorActivity"
android:label="My live folder" android:icon="@drawable/contacts">
<intent-filter>
<action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<provider android:authorities="com.ai.livefolders.contacts"
android:multiprocess="true" android:name=".MyContactsProvider" />
</application>
<uses-permission android:name="android.permission.READ_CONTACTS" />
</manifest>
根據活動資料夾協議,CREATE_LIVE_FOLDER Intent 允許主頁的上下文選單將
AllContactsLiveFolderCreatorActivity 顯示為一個標題為 “My live foler” 的選項。單擊此選單選項將在主頁上建立一個圖示。AllContactsLiveFolderCreatorActivity 負責定義此圖示,其中將包含一個影象和一個標籤。在本例子中,AllContactsLiveFolderCreatorActivity 中的程式碼將此標籤指定為 Contacts LF。下面讓我們看看建立活動資料夾的類AllContactsLiveFolderCreatorActivity。
package xiaohang.zhimeng;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.LiveFolders;
public class AllContactsLiveFolderCreatorActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
final String action = intent.getAction();
System.out.println("action is " + action);
if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
// 設定本Activity返回的結果
setResult(
RESULT_OK,
createLiveFolder(MyContactsProvider.CONTACTS_URI,
"Contacts LF", R.drawable.contacts));
} else {
setResult(RESULT_CANCELED);
}
finish();
}
private Intent createLiveFolder(Uri uri, String name, int icon) {
final Intent intent = new Intent();
intent.setData(uri);
// 設定LiveFolder 的Name(這個名字是顯示在手機的主頁上的)
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
// 設定LiveFolder 的圖示
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON,
Intent.ShortcutIconResource.fromContext(this, icon));
// 指定LiveFolder 顯示的模式 這裡用List以列表的形式顯示
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
LiveFolders.DISPLAY_MODE_LIST);
return intent;
}
}
[color=red] 2. AllContactsLiveFolderCreatorActivity [/color]類只有一個功能:擔當活動資料夾的生成程式或建立程式。可以將它視為活動資料夾的模板。每次單擊此活動(通過主頁上下文選單的Folers選項)時,它都會在主頁上生成一個活動資料夾。此活動告訴呼叫方(在這個例子中為主頁或LiveFolder框架) 活動資料夾名稱、為活動資料夾圖示使用的影象、提供可用資料的 URI,以及顯示模式 (列表或網格)。然後,活動資料夾框架負責在主頁上建立活動資料夾的圖示。
API裡邊已經明確的說明 為我們的LiveFoler(活動資料夾) 提供內容的 ContentProvider 經過查詢返回的Cursor物件必須 包含以下 列名稱,也可以把它理解成使用LiveFolder的一個契約 我來是來張API 的截圖比較合適。
[img]http://dl.iteye.com/upload/attachment/485186/84e1b405-a474-3ea4-826b-2ceb82d02dbb.jpg[/img]
[color=green]大家仔細 看看 有些是必須的 有些是不必須的[/color]
createLiveFoler 方法主要設定呼叫它的Intent的值。當此 Intent返回到呼叫方時,呼叫方將知道以下資訊。
[color=green] 活動資料夾的名稱
活動資料夾所使用的圖示
顯示模式:列表或網格
資料或為資料呼叫的內容URI[/color]
這些資訊足夠用來建立我們的活動檔案夾了。當用戶單擊活動資料夾的圖示時,系統將呼叫 URI 來檢索資料。此 URI 所標識的 ContentProvider提供了標準化的遊標。接下來就介紹該ContentProvider的程式碼:MyContactsProvider類。
[color=red]3. MyContactsProvider.java類[/color]
MyContactsProvider 具有以下功能。
(1) 按此方式標識傳入的 URI: content:// com.ai.livefolders.contacts。
(2) 對Android提供的由content://com.android.contacts/contacts 標識的聯絡人 ContentProvider進行內部呼叫。
(3) 讀取遊標的所有行並將其映射回一個遊標(比如 MatrixCursor),後者具有活動資料夾框架所需的合適的列名稱。
(4) 將 MattixCursor包裝到另一個遊標中,以便經過包裝的遊標上的 requery 在需要時呼叫聯絡人 ContentProvider。
MyContactsProvider的程式碼清單如下
package xiaohang.zhimeng;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.ContactsContract;
import android.provider.LiveFolders;
import android.util.Log;
public class MyContactsProvider extends ContentProvider {
public static final String AUTHORITY = "com.ai.livefolders.contacts";
// Uri that goes as input to the live-folder creation
public static final Uri CONTACTS_URI = Uri.parse("content://" + AUTHORITY
+ "/contacts");
// To distinguish this URI
private static final int TYPE_MY_URI = 0;
private static final UriMatcher URI_MATCHER;
static {
URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
URI_MATCHER.addURI(AUTHORITY, "contacts", TYPE_MY_URI);
}
@Override
public boolean onCreate() {
return true;
}
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
return 0;
}
// Set of columns needed by a live folder
// This is the live-folder contract
private static final String[] CURSOR_COLUMNS = new String[] {
BaseColumns._ID, LiveFolders.NAME, LiveFolders.DESCRIPTION,
LiveFolders.INTENT, LiveFolders.ICON_PACKAGE,
LiveFolders.ICON_RESOURCE };
// In case there are no rows
// use this stand-in as an error message
// Notice it has the same set of columns of a live folder
private static final String[] CURSOR_ERROR_COLUMNS = new String[] {
BaseColumns._ID, LiveFolders.NAME, LiveFolders.DESCRIPTION };
// The error message row
private static final Object[] ERROR_MESSAGE_ROW = new Object[] { -1, // id
"No contacts found", // name
"Check your contacts database" // description
};
// The error cursor to use
private static MatrixCursor sErrorCursor = new MatrixCursor(
CURSOR_ERROR_COLUMNS);
static {
// 為 CURSOR_ERROR_COLUMNS 新增給定的列值
sErrorCursor.addRow(ERROR_MESSAGE_ROW);
}
// Columns to be retrieved from the contacts database
private static final String[] CONTACTS_COLUMN_NAMES = new String[] {
ContactsContract.PhoneLookup._ID,
ContactsContract.PhoneLookup.DISPLAY_NAME,
ContactsContract.PhoneLookup.TIMES_CONTACTED,
ContactsContract.PhoneLookup.STARRED,
ContactsContract.PhoneLookup.PHOTO_ID };
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// Figure out the uri and return error if not matching
int type = URI_MATCHER.match(uri);
if (type == UriMatcher.NO_MATCH) {
// 如果URI 匹配出錯則返回錯誤資訊
return sErrorCursor;
}
Log.i("ss", "query called");
try {
// 呼叫 loadNewData方法進行查詢 返回 匹配好的MatrixCursor物件
MatrixCursor mc = loadNewData(this);
// setNotificationUri方法用來指定一個Uri以觀察它的變化
// 引數一:需要一個ContentResolver物件,從上下文物件獲得
// 引數二:要監視的URI
mc.setNotificationUri(getContext().getContentResolver(),
Uri.parse("content://com.android.contacts/contacts/"));
MyCursor wmc = new MyCursor(mc, this);
return wmc;
} catch (Exception e) {
System.out.println("Print yi chang ");
// 返回我們的錯誤物件
return sErrorCursor;
}
}
public static MatrixCursor loadNewData(ContentProvider cp) {
//
MatrixCursor mc = new MatrixCursor(CURSOR_COLUMNS);
Cursor allContacts = null;
try {
// 說一下query方法的引數
/*
* 引數一:要查詢的URI 我們查詢的完整URI 為"content://com.android.contacts/contacts"
* 也就是所有的聯絡人蔘數二:都要查詢那些列 上邊我們已經定義好引數三:本質上就是一個WHERE子句,它以SQL
* WHERE子句(不包含WHERE本身) 的格式宣告要返回的行 傳遞null將返回給定URI 的所有行
* 引數四:用來替換where子句中的?號引數五:指定排序的方式
*/
allContacts = cp
.getContext()
.getContentResolver()
.query(ContactsContract.Contacts.CONTENT_URI,
CONTACTS_COLUMN_NAMES, null, null,
ContactsContract.PhoneLookup.DISPLAY_NAME);
while (allContacts.moveToNext()) {
// 返回第二列的值 也就是 ContactsContract.PhoneLookup.TIMES_CONTACTED的值
// the zero-based index
String timesContacted = "Times contacted: "
+ allContacts.getInt(2);
Object[] rowObject = new Object[] {
allContacts.getLong(0), // id
allContacts.getString(1), // name
timesContacted, // description
Uri.parse("content://com.android.contacts/contacts/"
+ allContacts.getLong(0)), // intent uri
cp.getContext().getPackageName(), // package 返回應用程式的包名稱
R.drawable.contacts // 返回我們LiveFolder的圖示
};
// 給我們的 MatrixCursor 物件mc 新增給定的列值
mc.addRow(rowObject);
}
// 返回MatrixCursor物件
return mc;
} finally {
// 關閉Cursor物件, 並釋放所有資源並使其無效
allContacts.close();
}
}
// 如果有自定義型別,必須實現此方法 這裡我們 用的是 系統定義好的型別
@Override
public String getType(Uri uri) {
// indicates the MIME type for a given URI
// targeted for this wrapper provier
// This usually looks like
// "vnd.android.cursor.dir/vnd.google.note"
return ContactsContract.Contacts.CONTENT_TYPE;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException(
"no insert as this is just a wrapper");
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException(
"no delete as this is just a wrapper");
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
throw new UnsupportedOperationException(
"no update as this is just a wrapper");
}
}
大部分欄位大家也都應該明白,可能INTENT項有點迷惑。先說一下 timesContacted 它是用來記錄 我們的聯絡人呼叫我我們幾次 比如你電話存著 媽媽的電話 timesContacted就記錄著 媽媽給你打了幾個電話,或者說呼叫了你幾次,還有就連這個也可以時時更新,所以說還是比較強大滴。
INTENT 欄位實際上是一個字串,指向 ContentProvider 中該項的URL。在使用者單擊該項時,Android將使用此URL 來呼叫VIEW操作。此字串欄位稱為 INTENT 欄位是因為,在內部,Android會從字串 URI 派生 INTENT。
另外請大家注意,上面的MyContactsProvider 包含了下面幾行程式碼
MatrixCursor mc = loadNewData(this);
mc.setNotificationUri(getContext().getContentResolver(), Uri.parse("content://com.android.contacts/contacts/"));
函式 loadNewData() 從聯絡人提供程式獲取一組聯絡人並建立 MatrixCursor,MatrixCursor物件包含我們需要的列。這段程式碼然後告訴 MatrixCursor 向 ContentResolver 註冊自身,以便在URI (content://com.android.contacts/contacts/) 所指向的資料以任何形式發生變化時,ContentResolver能夠提醒遊標也就是我們的Cursor物件。
將 MatrixCursor 包裝到我們自己的遊標中也很重要。
MyCursor wmc = new MyCursor(mc,this);
MyCursor物件是我們自己定義的一個Cursor物件,這裡要理解為什麼需要包裝遊標,需要了解檢視如何更新更改的內容。ContentProvider(比如Contacts)通常通過將一個URI 註冊為 query方法實現的一部分,告訴遊標它需要監視更改。這是通過 cursor.setNotificationUri完成的。遊標然後將向 ContentProvider 註冊此URI 及它的所有子URI。然後,當在 ContentProvider 上發生插入或刪除操作時,插入或刪除操作的程式碼需要發出一個事件,表示特定URI 所標識的行中的資料發生了更改。
這將出發遊標通過 requery進行更新,檢視也將相應更新。遺憾的是, MatrixCursor不適用此 requery。SQLiteCursor適用於它,但我們無法在這裡使用 SQLiteCursor,因為我們已將這些列對映到了一組新列。
為了解決這一限制,我們將MatrixCursor包裝到一個遊標包裝器中,並重寫 requery 方法以丟棄內部的 MatrixCursor,並使用更新的資料建立一個新遊標。更明確的講,每次資料更改時,我們都希望獲得新的 MatrixCursor。但我們僅向 Android活動資料夾框架返回所包裝的外部遊標。這將告訴活動資料夾框架只有一個遊標,但在後臺,當資料更改時我們會建立新遊標,我們下面來說說這兩個類。
[color=red]4. MyCursor.java[/color]
請注意,MyCursor最開始使用一個 MatrixCursor進行初始化。在requery上, MyCursor 將回調提供程式來返回一個 MatrixCursor。然後,新 MatrixCursor 將使用 set 方法替代舊 MatrixCursor。
[color=green]說明:可以通過重寫 MatrixCursor的requery來完成此任務,但該類無法清除資料並重新啟動。所以這是一種合理的解決辦法。(請注意,MyCursor擴充套件了 BetterCursorWrapper,接下來將討論。)[/color]
MyCursor 原始碼
package xiaohang.zhimeng;
import android.content.ContentProvider;
import android.database.MatrixCursor;
public class MyCursor extends BetterCursorWrapper {
private ContentProvider mcp = null;
public MyCursor(MatrixCursor mc, ContentProvider inCp) {
super(mc);
mcp = inCp;
}
@Override
public boolean requery() {
MatrixCursor mc = MyContactsProvider.loadNewData(mcp);
this.setInternalCursor(mc);
return super.requery();
}
}
現在看一下 BetterCursorWrapper類,瞭解如何包裝遊標。
[color=red]5. BetterCursorWrapper.java[/color]
BetterCursorWrapper類 非常類似於 Android資料庫框架中的 CursorWrapper類。但我們還需要 CursorWrapper 所缺少的另外兩項功能。首先,它沒有提供 set方法來替換 requery 方法中的內部遊標。其次, CursorWrapper 不是CrossProcessCursor。活動資料夾需要 CrossProcessCursor,而不是普通遊標,因為活動資料夾會跨程序邊界進行工作。
BetterCursorWrapper原始碼
package xiaohang.zhimeng;
import android.content.ContentResolver;
import android.database.CharArrayBuffer;
import android.database.ContentObserver;
import android.database.CrossProcessCursor;
import android.database.CursorWindow;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Bundle;
public class BetterCursorWrapper implements CrossProcessCursor {
// Holds the internal cursor to delegate methods to
protected CrossProcessCursor internalCursor;
public CursorWindow getWindow() {
return internalCursor.getWindow();
}
public boolean onMove(int oldPosition, int newPosition) {
return internalCursor.onMove(oldPosition, newPosition);
}
public int getCount() {
return internalCursor.getCount();
}
public int getPosition() {
return internalCursor.getPosition();
}
public boolean move(int offset) {
return internalCursor.move(offset);
}
public boolean moveToPosition(int position) {
return internalCursor.moveToPosition(position);
}
public boolean moveToFirst() {
return internalCursor.moveToFirst();
}
public boolean moveToLast() {
return internalCursor.moveToLast();
}
public boolean moveToNext() {
return internalCursor.moveToNext();
}
public boolean moveToPrevious() {
return internalCursor.moveToPrevious();
}
public boolean isFirst() {
return internalCursor.isFirst();
}
public boolean isLast() {
return internalCursor.isLast();
}
public boolean isBeforeFirst() {
return internalCursor.isBeforeFirst();
}
public boolean isAfterLast() {
return internalCursor.isAfterLast();
}
public int getColumnIndex(String columnName) {
return internalCursor.getColumnIndex(columnName);
}
public int getColumnIndexOrThrow(String columnName)
throws IllegalArgumentException {
return internalCursor.getColumnIndexOrThrow(columnName);
}
public String getColumnName(int columnIndex) {
return internalCursor.getColumnName(columnIndex);
}
public String[] getColumnNames() {
return internalCursor.getColumnNames();
}
public int getColumnCount() {
return internalCursor.getColumnCount();
}
public byte[] getBlob(int columnIndex) {
return internalCursor.getBlob(columnIndex);
}
public String getString(int columnIndex) {
return internalCursor.getString(columnIndex);
}
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
internalCursor.copyStringToBuffer(columnIndex, buffer);
}
public short getShort(int columnIndex) {
return internalCursor.getShort(columnIndex);
}
public int getInt(int columnIndex) {
return internalCursor.getInt(columnIndex);
}
public long getLong(int columnIndex) {
return internalCursor.getLong(columnIndex);
}
public float getFloat(int columnIndex) {
return internalCursor.getFloat(columnIndex);
}
public double getDouble(int columnIndex) {
return internalCursor.getDouble(columnIndex);
}
public boolean isNull(int columnIndex) {
return internalCursor.isNull(columnIndex);
}
public void deactivate() {
internalCursor.deactivate();
}
public boolean requery() {
return internalCursor.requery();
}
public void close() {
internalCursor.close();
}
public boolean isClosed() {
return internalCursor.isClosed();
}
public void registerContentObserver(ContentObserver observer) {
internalCursor.registerContentObserver(observer);
}
public void unregisterContentObserver(ContentObserver observer) {
internalCursor.unregisterContentObserver(observer);
}
public void registerDataSetObserver(DataSetObserver observer) {
internalCursor.registerDataSetObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
internalCursor.unregisterDataSetObserver(observer);
}
public void setNotificationUri(ContentResolver cr, Uri uri) {
internalCursor.setNotificationUri(cr, uri);
}
public boolean getWantsAllOnMoveCalls() {
return internalCursor.getWantsAllOnMoveCalls();
}
public Bundle getExtras() {
return internalCursor.getExtras();
}
public Bundle respond(Bundle extras) {
return internalCursor.respond(extras);
}
// Constructor takes a crossprocesscursor as an input
public BetterCursorWrapper(CrossProcessCursor inCursor) {
this.setInternalCursor(inCursor);
}
// You can reset in one of the derived class's method
public void setInternalCursor(CrossProcessCursor inCursor) {
internalCursor = inCursor;
}
@Override
public void fillWindow(int pos, CursorWindow winow) {
internalCursor.fillWindow(pos, winow);
}
}
大家看前幾個方法就行了,後邊方法都沒有用到 實現CrossProcessCursor這個介面就會實現這些方法。大家也可以用 Eclipse 自動生成一下,將游標放在 internalCursor上。用滑鼠右鍵單擊並選擇 Source---》GenerateDelegate Methods。Eclipse隨後將填充該類剩餘部分。現在看一下完成此示例需要的一個簡單activity.
[color=red]6 .SimpleActivity.java[/color]
SimpleActivity.java 不是活動資料夾必須的類,但在專案中包含它可以為所有專案提供一種通用模式。此外,它支援在通過 Eclipse除錯時,部署應用程式並在螢幕上檢視它。
SimpleActivity 原始碼
package xiaohang.zhimeng;
import android.app.Activity;
import android.os.Bundle;
public class SimpleActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
簡單的 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="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="Live Folder Example" />
</LinearLayout>
現在,通過Eclipse構建、部署和執行示例活動資料夾專案所需的所有類都已就緒。我們可以執行試試了,剛開啟會開啟一個Activity 就是我們上邊的那個SimpleActivity 然後我們切換到主頁 按照我們 最開始 介紹的步驟就可以建立 活動檔案夾了,另外覺得覺得這個例子還有不足的地方,比如 我們可以自己定義一些聯絡人的頭像 然後從資料庫讀取出來,讓每個聯絡人都顯示自己的頭像。 歡迎大家 討論 批評 指點。
原始碼已上傳 :?: