App Widget也是一種UI組件,它可以讓一小塊程式(program piece)嵌入到案頭上。與普通的Widget(TextView、WebView等)相比,有以下幾個不同:
App Widget是有生命的UI組件,它會自動更新自身內容;
Widget不能自動更新自身內容,只能被動的等待使用者的調用;
應用上,由於App Widget自動更新的特性,所以它比較適合用來設計一些天氣,新聞,日曆等功能。
App Widget的設計流程:
規劃App Widget的大小及更新時間,在/res/xml目錄新增一份XML檔案;
規劃App Widget的UI,修改res/layout/main.xml;
編寫App Widget的主程式;
編輯AndroidManifest.xml,設定App Widget可接受App Widget的更新事件:android.appwidget.action.APPWIDGET_UPDATE
及設計一個App Widget至少需要以下4個檔案:
res/xml/appwidget_provider.xml
res/layout/main.xml
src//HelloAppWidgetProvider.java
AndroidManifest.xml
下面是一個具體實現App Widget的例子:
建立一個Android工程HelloAppWidget,在/res目錄建立一個檔案夾xml,在/res/xml目錄下建立一個appwidget_provider.xml檔案:
[html] <?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="85dp"
android:minHeight="30dp"
android:updatePeriodMillis="1000"
android:initialLayout="@layout/main"
>
</appwidget-provider>
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="85dp"
android:minHeight="30dp"
android:updatePeriodMillis="1000"
android:initialLayout="@layout/main"
>
</appwidget-provider>說明如下:
<appwidget-provider>標籤定義App Widget的屬性
android:minWidth 寬度
android:minHeight 長度
android:updatePeriodMillis定義App Widget的更新頻率,Android架構每隔一段時間,會callback AppWidgetProvider類的onUpdate()事件;以前android的版本設定為1毫秒為單位,現在版本為了省電,更新時間為30~60分鐘,所以現在設定30分鐘以內的更新意義不大,系統預設為30~60分鐘更新
android:initialLayout屬性指定此App Widget的UI layout定義,”@”符號在Android的XML定義檔案,代表「目錄」之意,因此”@layout/main”表示「layout目錄下的main.xml檔案」
為了介面美觀,google提供了App Widget的外觀設計原則,以下是官方提供的設計參考:
編輯main.xml檔案:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/appwidget_text"
android:textColor="#ff0000"
/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/appwidget_text"
android:textColor="#ff0000"
/>
</LinearLayout>我們的App Widget使用LinearLayout來安排布局,而UI為一個TextView物件。在這裡,將此TextView的id定義為”appwidget_text”。
在Android工程中添加一個新類HelloAppWidgetProvider extends AppWidgetProvider:
[java] package com.android;
import java.util.Date;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.widget.RemoteViews;
public class HelloAppWidgetProvider 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];
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
public void onDeleted(Context context, int[] appWidgetIds) {
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
CharSequence text;
java.text.DateFormat df = new java.text.SimpleDateFormat("hh:mm:ss");
text = "http://blog.csdn.net/imyang2007" + " Time:" + df.format(new Date());
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
views.setTextViewText(R.id.appwidget_text, text);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
HelloAppWidgetProvider.java 代碼說明
是目前的HelloAppWidget範例設計,說明如下:
- onUpdate(): 收到ACTION_APPWIDGET_UPDATE廣播時,架構會callback此method
onDelete(): 收到ACTION_APPWIDGET_DELETE廣播時,架構會callback此method
AppWidgetManager: 管理App Widget的類
在AndroidManifest.xml裡我們要讓HelloAppWidgetProider類可以接收ACTION_APPWIDGET_UPDATE廣播事件;ACTION_APPWIDGET_UPDATE是最主要的App Widget事件,當AppWidgetProvider被要求為App Widget提供”RemoteView”時,就會收到這個事件。
RemoteViews 就是表示UI的類。res/layout/main.xml描述了應用程式的UI,UI裡當然包含許多組件(Widget),Android應用程式的UI就是一個View tree,view tree就是View Hierarchy。總結來說,RemotViews是一個用來表示View Hierarchy的類。通過RemoteViews可以找到UI裡的每一個組件。
onUpdate()負責更新已經安裝在案頭上的App Widget內容,因此我們實現一個updateAppWidget()來進行真正更新的工作。onUpdate()的第二個參數為AppWidgetManager,這是一個管理AppWidgetProvider的類,我們必須通過架構callback本方法時回傳給我們的AppWidgetProvider,來更新案頭上的App Widget。onUpdate()第三個參數appWidgetIds陣列,存放需要更新的App Widget ID;架構會將需要更新的App Widget的ID回傳給onUpdate(),程式必須負責更新每一個需要更新的App Widget。
updateAppWidget()說明:
通過UI layout取得自已的「View Hierarchy」(UI),以RemoteViews表示;
調用RemoteView的setTextViewText()方法,修改UI裡的「R.id.appwidget_text」組件,更改文字內容;
調用AppWidgetProvider的updateAppWidget()方法,更新指定的App Widget,將其UI更新為RemoteView的UI;
updateAppWidget()的第二個參數為RemoteView,即說明1.取得的UI,說明2.修改了此UI裡的”R.id.appwidget_text”組件,最後通過App Widget Manager更新App Widget的UI。
App Widget的最後一步,編輯AndroidManifest.xml檔案:
[html] <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<receiver android:name=".HelloAppWidgetProvider" >
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_provider" />
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
<activity
android:name=".HelloAppWidgetActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<receiver android:name=".HelloAppWidgetProvider" >
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_provider" />
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
<activity
android:name=".HelloAppWidgetActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
說明如下:
在<application>裡加入<receiver>標籤,指定android:name屬性為主要的provider類,即”HelloAppWidgetProvider”,注意「.」表示後面的字串為一個「類別名」,不要忽略了這個重要的小數點
在<receiver>裡加入<meta-data>標籤,指定android:resource屬性為App Widget的資源名稱,就是「@xml/appwidget_provider」,即「xml目錄的appwidget_provider.xml文檔」
在<receiver>裡加入<intent-filter>標籤,讓App Widget可以接收APPWIDGET_UPDATE事件(event)
至此,一個簡單的App Widget設計完成,運行效果為:
最後,發現一個問題,程式並沒有實現我們的預想的每秒更新一次App Widget,而是在30~60分鐘更新一次,這是1.6之後的android版本為了使裝置不那麼耗電,把App Widget的更新時間增加到了30分鐘以上。
摘自 Young的專欄