應用程序窗口小部件 App Widgets
應用程序窗口小部件( Widget )是微小的應用程序視圖,可以被嵌入到其它應用程序中(比如桌面)并接收周期性的更新。你可以通過一個 App Widget provider 來發布一個 Widget 。可以容納其它 App Widget 的應用程序組件被稱為 App Widget 宿主。下面的截屏顯示了一個音樂 App Widget 。
這篇文章描述了如何使用 App Widget Provider 發布一個 App Widget 。
基礎知識 The Basics
為了創建一個 App Widget ,你需要下面這些:
描述一個 App Widget 元數據,比如 App Widget 的布局,更新頻率,以及 AppWidgetProvider 類。這應該在 XML 里定義。
AppWidgetProvider 類的實現
定義基本方法以允許你編程來和 App Widget 連接,這基于廣播事件。通過它,當這個 App Widget 被更新,啟用,禁用和刪除的時候,你都將接收到廣播通知。
視圖布局
為這個 App Widget 定義初始布局,在 XML 中。
另外,你可以實現一個 App Widget 配置活動。這是一個可選的活動 Activity ,當用戶添加 App Widget 時加載并允許他在創建時來修改 App Widget 的設置。
下面的章節描述了如何建立這些組件:
在清單中聲明一個應用小部件
首先,在應用程序 AndroidManifest.xml 文件中聲明 AppWidgetProvider 類,比如:
<receiver android:name="ExampleAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info " />
</receiver>
<receiver> 元素需要 android:name 屬性,它指定了 App Widget 使用的 AppWidgetProvider 。
<intent-filter> 元素必須包括一個含有 android:name 屬性的 <action> 元素。該元素指定 AppWidgetProvider 接受 ACTION_APPWIDGET_UPDATE 廣播。這是唯一你必須顯式聲明的廣播。當需要的時候, AppWidgetManager 會自動發送所有其他 App Widget 廣播給 AppWidgetProvider 。
<meta-data> 元素指定了 AppWidgetProviderInfo 資源并需要以下屬性:
· android:name – 指定元數據名稱。
· android:resource – 指定 AppWidgetProviderInfo 資源路徑。
增加 AppWidgetProviderInfo 元數據
AppWidgetProviderInfo 定義一個 App Widget 的基本特性,比如最小布局尺寸,初始布局資源,刷新頻率,以及(可選的)創建時加載的一個配置活動。使用單獨的一個 <appwidget-provider> 元素在 XML 資源里定義 AppWidgetProviderInfo 對象并保存到項目的 res/xml/ 目錄下。
比如:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp" <!-- density-independent pixels -->
android:minHeight="72dp"
android:updatePeriodMillis="86400000" <!-- once per day -->
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure" >
</appwidget-provider>
下面是 <appwidget-provider> 屬性的總結:
· minWidth 和 minHeight 屬性的值指定了這個 App Widget 布局需要的最小區域。
缺省的 App Widgets 所在窗口的桌面位置基于有確切高度和寬度的單元網格。如果 App Widget 的最小長寬和這些網格單元的尺寸不匹配,那么這個 App Widget 將收縮到最接近的單元尺寸。(參見 App Widget Design Guidelines 以獲取更多關于桌面單元尺寸的信息)
因為桌面布局方向(由此,單元的尺寸)可以變化,按照拇指規則,你應該假設最壞情況單元尺寸是
74
像素高和寬。不過,你必須從最后的尺寸中減去
2
以把像素計算過程中產生的任何的整數舍入誤差考慮在內。要找到像素密度無關的最小寬度和高度,使用這個公式:
(number of cells * 74) - 2
遵循這個公式,你應該使用
72dp
為每一個單元高度,
294dp
為四個單元寬度。
· updatePerdiodMillis 屬性定義了 App Widget 框架調用 onUpdate() 方法來從 AppWidgetProvider 請求一次更新的頻度。實際更新時間并不那么精確,而且我們建議更新頻率越低越好 - 也許每小時不超過一次以節省電源。你也許還會允許用戶在配置中調整這個頻率 - 一些人可能想每 15 分鐘一次股票報價,或者一天只要四次。
· initialLayout 屬性指向定義 App Widget 布局的資源。
· configure 屬性定義了 Activity ,當用戶添加 App Widget 時啟動,以為他或她配置 App Widget 特性。這是可選的(閱讀下面的 Creating an App Widget Configuration Activity )。
參見 AppWidgetProviderInfo 類以獲取更多可以被 <appwidget-provider> 元素接受的屬性信息。
創建 App Widget 布局
你必須在 XML 中為你的 App Widget 定義一個初始布局并保存到項目的 res/layout/ 目錄下。你可以使用如下所列的視圖對象來設計你的 App Widget ,但是在此之前,請先閱讀并理解 App Widget Design Guidelines .
如果你熟悉在 XML 中聲明布局,那么創建這個 App Widget 布局是很簡單的。但是,你必須意識到那個 App Widget 布局是基于 RemoteViews , 這并不支持所有類型的布局或視圖小部件。
一個 RemoteViews 對象(以及,相應的,一個 App Widget )可以支持下面這個布局類:
以及下面的小部件類:
· Button
· TextView
不支持這些類的派生。
使用 AppWidgetProvider 類
你必須通過在清單文件中使用 <receiver> 元素來聲明你的 AppWidgetProvider 類實現為一個廣播接收器(參見上面的 Declaring an App Widget in the Manifest )。
AppWidgetProvider 類擴展 BroadcastReceiver 為一個簡便類來處理 App Widget 廣播。 AppWidgetProvider 只接收和這個 App Widget 相關的事件廣播,比如這個 App Widget 被更新,刪除,啟用,以及禁用。當這些廣播事件發生時, AppWidgetProvider 將接收到下面的方法調用:
onUpdate(Context, AppWidgetManager, int[])
這個方法調用來間隔性的更新 App Widget ,間隔時間用 AppWidgetProviderInfo 里的 updatePeriodMillis 屬性定義(參見添加 AppWidgetProviderInfo 元數據)。這個方法也會在用戶添加 App Widget 時被調用,因此它應該執行基礎的設置,比如為視圖定義事件處理器并啟動一個臨時的服務 Service , 如果需要的話。但是,如果你已經聲明了一個配置活動,這個方法在用戶添加 App Widget 時將不會被調用,而只在后續更新時被調用。配置活動應該在配置完成時負責執行第一次更新。(參見下面的創建一個 App Widget 配置活動 Creating an App Widget Configuration Activity 。 )
當 App Widget 從宿主中刪除時被調用。
當一個 App Widget 實例第一次創建時被調用。比如,如果用戶添加兩個你的 App Widget 實例,只在第一次被調用。如果你需要打開一個新的數據庫或者執行其他對于所有的 App Widget 實例只需要發生一次的設置,那么這里是完成這個工作的好地方。
當你的 App Widget 的最后一個實例被從宿主中刪除時被調用。你應該在 onEnabled(Context) 中做一些清理工作,比如刪除一個臨時的數據庫。
這個接收到每個廣播時都會被調用,而且在上面的回調函數之前。你通常不需要實現這個方法,因為缺省的 AppWidgetProvider 實現過濾所有 App Widget 廣播并恰當的調用上述方法。
注意 : 在 Android 1.5 中, 有一個已知問題, onDeleted() 方法在該調用時不被調用。為了規避這個問題,你可以像 Group post 中描述的那樣實現 onReceive() 來接收這個 onDeleted() 回調。
最重要的 AppWidgetProvider 回調函數是 onUpdated() , 因為它是在每個 App Widget 添加進宿主時被調用的(除非你使用一個配置活動)。如果你的 App Widget 要接受任何用戶交互事件,那么你需要在這個回調函數中注冊事件處理器。如果你的 App Widget 不創建臨時文件或數據庫,或者執行其它需要清理的工作,那么 onUpdated() 可能是你需要定義的唯一的回調函數。比如,如果你想要一個帶一個按鈕的 App Widget ,當點擊時啟動一個活動,你可以使用下面的 AppWidgetProvider 實現:
public class ExampleAppWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current App Widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
這個 AppWidgetProvider 僅定義了 onUpdated() 方法,為了定義一個 PendingIntent , 來啟動一個活動并使用 setOnClickPendingIntent(int, PendingIntent) 方法把它附著到這個 App Widget 的按鈕上。注意它包含了一個遍歷 appWidgetIds 中所有項的循環,這是一個 IDs 數組,每個 ID 用來標識由這個 Provider 創建的一個 App Widget 。這樣,如果用戶創建多于一個這個 App Widget 的實例,那么它們將被同步更新。不過,對于所有的 App Widget 實例,只有一個 updatePeriodMillis 時間表被管理。比如,如果這個更新時間表被定義為每隔兩個小時,而且 App Widget 的第二個實例是在第一個后面一小時添加的,那么它們將按照第一個所定義的周期來更新而第二個被忽略(它們將都是每 2 個小時進行更新,而不是每小時)。
注意 : 因為這個 AppWidgetProvider 是一個廣播接收器 BroadcastReceiver ,不能保證你的進程在回調函數返回后仍然繼續運行(參見應用程序基礎 > 廣播接收器的生命周期 Application Fundamentals > Broadcast Receiver Lifecycle 以獲取更多信息)。如果你的 App Widget 設置過程能持續幾秒鐘(也許當執行網頁請求時)而且你要求你的進程繼續,考慮在 onUpdated() 方法里啟動一個服務 Service 。 從這個服務里,你可以執行自己的 App Widget 更新,而不必擔心 AppWidgetProvider 由于一個應用程序無響應錯誤 Application Not Responding (ANR) 而關閉。參見 Wiktionary sample's AppWidgetProvider ,這是個 App Widget 運行一個 Service 的例子。
同樣參見 ExampleAppWidgetProvider.java 例子類。
接收 App Widget 廣播意圖
AppWidgetProvider 只是一個簡便類。如果你想直接接收 App Widget 廣播,你可以實現自己的 BroadcastReceiver 或者重寫 onReceive(Context, Intent) 回調函數。你需要注意的 4 個意圖如下:
創建一個 App Widget 配置活動
如果你想讓用戶在添加一個新的 App Widget 時調整設置,你可以創建一個 App Widget 配置活動。這個活動將被 App Widget 宿主自動啟動并允許用戶在創建時配置可用的設置,比如 App Widget 顏色,尺寸,更新周期或者其它功能設置。
這個配置活動應該在 Android 清單文件中聲明為一個通用活動。不過,它將被通過 ACTION_APPWIDGET_CONFIGURE 活動而被 App Widget 宿主啟動,因此這個活動需要接受這個意圖。比如:
<activity android:name=".ExampleAppWidgetConfigure">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
同樣的,活動必須在 AppWidgetProviderInfo XML 文件中聲明,通過 android:configure 屬性(參見上面的添加 AppWidgetProviderInfo 元數據 Adding the AppWidgetProviderInfo Metadata )。比如,配置活動可以聲明如下:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
...
android:configure="com.example.android.ExampleAppWidgetConfigure"
... >
</appwidget-provider>
注意這個活動是用全名聲明的,因為它將從你的程序包外被引用。
這就是所有關于配置活動你一開始需要了解的?,F在你需要一個真實的活動。這兒就有,不過,當你實現這個活動時記住兩件重要的事情:
<!--StartFragment-->
? App Widget 宿主調用配置活動而且配置活動應該總是返回一個結果 .這個結果應該包含這個通過啟動該活動的意圖傳遞的App Widget ID(以 EXTRA_APPWIDGET_ID 保存在意圖的附加段 Intent extras 中 )
? 當這個 App Widget 被創建時將不會調用 onUpdate() 方法 (當一個配置活動啟動時,系統將不會發送 ACTION_APPWIDGET_UPDATE 廣播 ). 配置活動應該在 App Widget 第一次被創建時負責從 AppWidgetManager 請求一個更新 .不過, onUpdate() 將在后續更新中被調用 -只忽略第一次.
參見下面章節的代碼片斷 ,該示例說明了如何從配置中返回一個結果并更新這個 App Widget.
<!--StartFragment-->
從配置活動中更新一個 App Widget
<!--EndFragment-->
當一個 App Widget 使用一個配置活動 ,那么當配置結束時,就應該由這個活動來更新這個 App Widget. 你可以直接 AppWidgetManager 里請求一個更新來這么做 .
下面是恰當的更新 App Widget 以及關閉配置活動這個過程的一個概要描述 :
<!--EndFragment--> <!--EndFragment-->-
首先,從啟動這個活動的意圖中獲取App Widget ID:
Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) { mAppWidgetId = extras.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); }
- 實施你的App Widget 配置。
-
當配置完成后,通過調用
getInstance(Context)
獲取一個AppWidgetManager實例:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
-
以一個
RemoteViews
布局調用
updateAppWidget(int, RemoteViews)
更新App Widget:
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget); appWidgetManager.updateAppWidget(mAppWidgetId, views);
- 最后,創建返回意圖,設置活動結果,并結束這個活動:
Intent resultValue = new Intent(); resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); setResult(RESULT_OK, resultValue); finish();
提示: 當你的配置活動第一次打開時,設置活動結果為RESULT_CANCELED。這樣,如果用戶在結束之前從活動外返回,這個App Widget 宿主會接收到配置取消通知而不會添加這個App Widget。
參見ApiDemos里面的 ExampleAppWidgetConfigure.java 例子。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
