第7章 App Widgets
App Widgets是一個應用程式的微型視圖,可以嵌入到其他應用程式(如主畫面)並且能夠定期更新。你發行就緒一個應用程式的App Widget,而這些視圖稱為視窗的使用者介面。一個應用程式組件,可以支援其他應用程式的App Widgets稱為App Widget的主機(host)。下面的是顯示音樂的App Widget。
該文檔將介紹如何在應用程式裡發布和使用App Widget。
7.1 基礎知識
要建立一個App Widget,您需要瞭解以下幾點:
◆AppWidgetProviderInfo對象:
描述了一個App Widget的中繼資料,如在App
Widget的布局,更新頻率,和AppWidgetProvider類。都應在XML中定義。
◆AppWidgetProvider類的實現:
定義一個基於廣播事件與App Widget的介面方法。通過它,您將收到廣播對App Widget進行更新,啟用,禁用和刪除。
◆視圖布局:
在XML中初步定義App Widget布局。
此外,還可以實現App Widget可配置的Activity。當使用者添加您的App Widget,並允許他或她在建立時修改設定時啟動這個可配置的Activity。
該文檔將介紹如何在應用程式裡發布和使用App Widget。
7.2 在Manifest.xml中聲明App Widgets
首先,在您的應用程式的AndroidManifest.xml檔案中應聲明AppWidgetProvider類。例如代碼清單7-1所示:
<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>
代碼清單 7-1
<receiver>節點需要的android:name屬性,在App Widget中指定使用AppWidgetProvider。 <intent-filter>節點必須包含一個<action>節點的name屬性。此屬性指定AppWidgetProvider接受ACTION_APPWIDGET_UPDATE廣播。這是唯一的廣播,你必須明確聲明。在AppWidgetManager自動發送其他App widget廣播到AppWidgetProvider是必要的。<meta-data>節點指定的AppWidgetProviderInfo的資源,需要以下屬性:
android:name - 指定的中繼資料的名稱。使用android.appwidget.provider識別作為
◆AppWidgetProviderInfo描述符的資料。
◆android:resource - 指定的AppWidgetProviderInfo的資源位置。
7.3 添加AppWidgetProviderInfo中繼資料
AppWidgetProviderInfo是定義App Widget的本質,例如其最小的布局尺寸,初始布局資源,如何更新App Widget,和(可選)配置Activity,在建立時發起。在XML資源檔中定義AppWidgetProviderInfo對象,使用<appwidget-provider>節點和在項目的res / xml/檔案夾中儲存。如代碼清單7-2所示:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="294dp" android:minHeight="72dp" android:updatePeriodMillis="86400000" android:previewImage="@drawable/preview" android:initialLayout="@layout/example_appwidget" android:configure="com.example.android.ExampleAppWidgetConfigure" android:resizeMode="horizontal|vertical"></appwidget-provider>
代碼清單 7-2
以下是<appwidget-provider>屬性的摘要,在這之前最好你已經閱讀了第一部分的Widget設計章節:
◆App Widget預設的情況下minWidth和MinHeight屬性值指定最小佔據的空間,AppWidgets預設是在Home螢幕位置,在其視窗基礎上的儲存格中有一個明確高度和寬度的網格。如果一個App Widget的最小寬度或高度值不匹配儲存格的尺寸,則App Widget尺寸向上舍入到最接近的儲存格大小。
註:為了使App Widget更容易移植在不同裝置,App Widget的最小尺寸不應大於4×4儲存格
◆minResizeWidth和minResizeHeight屬性指定App Widget的絕對最小尺寸。這些值應該指定尺寸下,否則應用程式組件將無法辨認或以其他方式使用。在android3.1,允許使用者使用這些屬性調整控制項大小,可能是小於預設尺寸界定的minwidth和minheight屬性。
◆updateperiodmillis屬性定義,往往在App Widget架構,應從appwidgetprovider請求通過調用onupdate()回調方法更新。實際上不能保證更新的準確性,我們建議儘可能的少更新或者不超過每小時一次,以此來節省電池。你也可以在configuration-some中允許使用者調整頻率,比如證劵報價機,一些使用者可能想要15分鐘更新一次,一些則想要每天只更新四次
註:如果該裝置是睡著的,而它是一個更新的時間(定義updatePeriodMillis)時,則該裝置將被喚醒以執行更新。如果你不超過每小時更新一次,這可能不會對電池壽命造成重大的問題。但是,如果您需要頻繁更新或你並不需要更新,而裝置是睡著的,你就可以根據警示代替執行,則不會喚醒裝置執行更新。要做到這一點,設定一個Intent,當您的AppWidgetProvider收到的警示時,使用AlarmManager。設定警示類型有ELAPSED_REALTIME或RTC,這在收到警示時,該裝置被喚醒。然後設定updatePeriodMillis為零(“0”)。
◆initialLayout的屬性指向布局資源,它定義了App Widget的布局。
◆在Activity啟動時對屬性進行配置定義,使用者添加App Widget,以便讓使用者配置App Widget屬性。這是可選的。
◆在配置previewImage屬性後將指定一個App Widget表徵圖是什麼樣子,當選擇這個App Widget時使用者可以進行預覽。如果沒有提供表徵圖,使用者卻認為laucher是您的應用程式圖示。這個欄位對應android:previewImage進行在 <receiver>元素的AndroidManifest.xml檔案中。在android3.0引入。
◆在Android 3.0引入,該autoAdvanceViewId屬性指定的App Widget子視圖的視圖ID。
◆在Android 3.1引入,該resizeMode屬性指定其中一個可以調整規則的Widget。您可以使用此屬性使主畫面Widget的調整方式,如水平,垂直,或兩軸。使用者長按一個Widget,會顯示其調整的介面,然後拖動水平和/或垂直的控鍵,改變布局網格的大小。resizemode屬性值包括"horizontal", "vertical", 和"none"。兩者都有如“horizontal | vertical”。
7.4 建立App Widget布局
你必須為你的App widget定義初始布局,你可以在XML定義並儲存在項目的res/layout/目錄中。你可以使用下列的View對象來設計你的App widget,但在你開始設計你的App widget之前,請閱讀和理解App widget的設計準則。 如果你熟悉XML的布局,建立App widget的布局很簡單。然而,你們必須知道App widget的布局都是基於RemoteViews類,它不支援各種布局或view widget。
一個RemoteViews對象支援以下布局類:
FrameLayout
LinearLayout
RelativeLayout
以下View支援widget:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
但是這些類的子類卻都不支援。
1. 添加邊距到App Widgets
widget通常不應該擴充到螢幕邊緣,不應與其他widget視圖共同重新整理,所以你應該在你的widget框中增加邊距。自Android 4.0起,App widget提供了widget之間的自動填滿架構和App widget的包圍盒,以便使用者在home螢幕更好的調整其他widget和表徵圖。要獲得這種功能,你需要吧應用程式的targetSdkVersion設定為14或更高。
早期版本,編寫一個布局很容易,並可以自訂邊距,在Android4.0或以上版本並沒有額外的邊距,步驟如下:
◆設定應用程式的targetsdkversion值為14或更高。
◆建立一個如下布局,引用dimension資源,如代碼清單7-3所示:
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/widget_margin"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:background="@drawable/my_widget_background"> … </LinearLayout> </FrameLayout>
代碼清單 7-3
◆建立兩個dimensions 的資源,一個在res /values/提供Android 4.0之前的自訂邊距,一個在res/values-v14/沒有為Android4.0widgets提供額外的padding:
res/values/dimens.xml:
<dimen name="widget_margin">8dp</dimen>
res/values-v14/dimens.xml:
<dimen name="widget_margin">0dp</dimen>
7.5 使用AppWidgetProvider類
首先,你必須在AndroidManifest<receiver>節點裡聲明 的AppWidgetProvider類的實現(參見本章的“7.2在Manifest.xml中聲明App Widgets” )。
AppWidgetProvider繼承broadcastreceiver用來處理App widget廣播非常方便。 AppWidgetProvider只接收和App widget相關的事件廣播,如App widget進行更新時,相關的App widget進行刪除,啟用和禁用。這些廣播事件發生時,AppWidgetProvider將調用以下的方法:
◆onUpdate()
這種在AppWidgetProviderInfo由updatePeriodMillis屬性定義的時間間隔來使AppWidget更新。當使用者添加App widget時,這種方法也被調用,所以它應該進行基本的設定,如定義View事件的處理,如果有必要,還應啟動臨時service。不過,如果你已經聲明配置的Activity,當使用者添加App widge這種方法則不會調用,而是後續更新調用。配置的Activity完成後,它的作用就是執行首次更新。
◆onAppWidgetOptionsChanged()
當wodget第一次被調整時被調用。你能使用這個回調來顯示或隱藏內容。你可以通過getAppWidgetOptions()來獲得大小範圍,它返回一個Bundle,你可以使用下面String鍵,來獲得值:
AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH—當前寬度的最小值,單位是DP
AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT—當前高度的最小值,單位是DP
AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH—當前寬度的最大值,單位是DP
AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT—當前高度的最大值,單位是DP
Android4.1從在引入這個方法。請注意
◆onDeleted(Context, int[])
每次一個App Widget從App Widget主機中刪除時被調用。
◆onEnabled(Context)
首次建立App widget執行個體時調用。例如,如果使用者為同一個App widget添加了兩個執行個體,這也只調用一次。如果你需要開啟一個新的資料庫或進行其他的設定,那麼在這個地方執行個體是非常好的。
◆onDisabled(Context)
當最後一個App widget的執行個體時從App
widget主機中刪除時被調用,使用onDisabled(Context)方法進行清理,比如刪除臨時資料庫。
◆onReceive(Context, Intent)
可以理解為一個通用廣播接收介面,上面的每個方法的回調。你通常不需要實現這個方法,因為預設的AppWidgetProvider實現過濾器所有App widget廣播,並適當調用以上的方法。
當每個App
widget添加到一個主機時,最重要的是AppWidgetProvider onUpdate()方法回調(除非你使用一個配置的Activity)。如果你的App widget接受任何使用者互動事件,那麼在回調時,你需要註冊事件處理器。如果你的App
widget不能建立臨時檔案或資料庫,或者執行其他的工作,那就需要清楚,onUpdate()方法,可能是你唯一需要定義的回調方法。例如,如果你想要一個App widget上有一個按鈕,當點擊時啟動一個Activity,你可以這樣實現AppWidgetProvider,如代碼清單7-4所示:
public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { final int N = appWidgetIds.length; for (int i=0; i<N; i++) { int appWidgetId = appWidgetIds[i]; Intent intent = new Intent(context, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout); views.setOnClickPendingIntent(R.id.button, pendingIntent); appWidgetManager.updateAppWidget(appWidgetId, views); } }}
代碼清單 7-4
上面代碼清單7-4中的AppWidgetProvider只定義onupdate()方法,其目的是定義一個PendingIntent啟動一個Activity並使用使用setonclickpendingintent(int,pendingintent)附加到App widget按鈕。注意,在appWidgetIds中它包括一個迴圈遍曆每個條目,這是一個數組的id標識,確定每個App widget。這樣,如果使用者建立多個App widget的執行個體,然後他們都同時更新。然而,只有一個updateperiodmillis時間表將管理所有的App widget。例如,如果更新計劃被定義為每兩個小時,在第一個後等待一小時在添加第二個執行個體,那麼它們兩個都將使用第一個的周期而第二個更新周期會被忽略。讀者可以參考ApiDemos\src\com\example\android\apis\appwidget的例子。
AppWidgetProvider就是一個方便的類而已。如果你想直接接收App widget廣播,你可以實現自己的BroadcastReceiver或覆蓋的onReceive(Context, Intent) 方法。你需要注意以下幾個intent:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
7.6 建立一個App Widget配置的Activity
如果你想要一個使用者,當他增加了一個新的App widget時來配置設定,那麼你可以建立一個App widget配置Activity。當前的Acitivity將自動啟動的App widget的主機,並允許使用者在建立時配置App widget的顏色,大小,更新周期或其他功能的設定。 這個配置Activity應該在Android manifest檔案中聲明是一個標準的Activity。然而,它將通過App widget主機使用ACTION_APPWIDGET_CONFIGURE Action來啟動,所以這個Activity需要接收這種Intent。如代碼清單7-5所示:
<activity android:name=".ExampleAppWidgetConfigure"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> </intent-filter></activity>
代碼清單 7-5
此外,Activity必須在AppWidgetProviderInfo XML檔案中聲明android:configure屬性(參見前面小節“添加AppWidgetProviderInfo中繼資料”)。例如,需要配置的Activity聲明如代碼清單7-6所示:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ... android:configure="com.example.android.ExampleAppWidgetConfigure" ... ></appwidget-provider>
代碼清單 7-6
注意,這個Activity是聲明完整命名空間,因為它是從外部包引用的。
你需要的是啟動一個配置Activity。現在你所需要的是實際的Activity。然而,當你實現Activity有兩個重要的事情要記住:
◆這個App widget主機調用配置Activity,而配置Activity應該總是返回一個結果碼。這個結果碼應該包括App widget ID。
◆當建立App widget時OnUpdate()方法將不會被調用(配置Activity啟動時,系統將不發送ACTION_APPWIDGET_UPDATE廣播)。
這是配置Activity的職責,它請求從App widget首次建立AppWidgetManager時更新。然而,onUpdate()方法將調用後續更新,它僅在第一次跳過。
請參閱以下的程式碼片段,看它怎樣返回配置和更新的App widget後的結果。
當一個App widget使用配置Activity時,這個Activity配置完成後負責更新App widget。你可以從AppWidgetManager通過請求直接更新。
1. 從通過Intent啟動的Activity中獲得App widget的ID:
Intent intent = getIntent();Bundle extras = intent.getExtras();if (extras != null) { mAppWidgetId = extras.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);}
2. 執行你的App widget配置.
3. 當配置完成後,通過調用getInstance(context)獲得一個AppWidgetManager執行個體:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
4. 通過調用updateAppWidget(int,RemoteViews)來使用RemoteViews布局更新App widget:
RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.example_appwidget);appWidgetManager.updateAppWidget(mAppWidgetId, views);
5. 最後,建立返回的intent,其設定Activity的結果,並終止該Activity:
Intent resultValue = new Intent();resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);setResult(RESULT_OK, resultValue);finish();
7.7 設定一個預覽的圖片
Android3.0之後引入了previewImage屬性,它指定一個Appwidget縮圖。下面讓我們看下代碼清單7-7,看看是如何設定的
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ... android:previewImage="@drawable/preview"></appwidget-provider>
代碼清單 7-7
為了協助您的Appwidget(指定在previewImage領域)建立預覽映像,在Android模擬器中包含一個應用程式被稱為“Widget Preview”。要建立預覽映像,則要啟動該應用程式,選擇你的應用程式的Appwidget,並設定你希望的預覽映像,然後將它儲存,並將其放置在你的應用程式的drawable資源下。