一、概述
App Widget是應用程序窗口小部件(Widget)是微型的應用程序視圖,它可以被嵌入到其它應用程序中(比如桌面)並接收週期性的更新。你可以通過一個App Widget Provider來發佈一個Widget。官方文檔地址:《App Widgets》
這裏涉及到兩個方面的內容:AppWidgetProvider類和appwidget-provider標籤;
1、appwidget-provider標籤:
這個玩意是用來定義桌面widget的大小,初始狀態等等信息的,它的位置應該放在res/xml文件夾下,具體的xml參數如下:
- android:minWidth : 最小寬度
- android:minHeight : 最小高度
- android:updatePeriodMillis : 更新widget的時間間隔(ms),"86400000"爲1個小時
- android:previewImage : 預覽圖片
- android:initialLayout : 加載到桌面時對應的佈局文件
- android:resizeMode : widget可以被拉伸的方向。horizontal表示可以水平拉伸,vertical表示可以豎直拉伸
- android:widgetCategory : widget可以被顯示的位置。home_screen表示可以將widget添加到桌面,keyguard表示widget可以被添加到鎖屏界面。
- android:initialKeyguardLayout : 加載到鎖屏界面時對應的佈局文件
至於具體怎麼用,等下實戰的時候會講。
2、AppWidgetProvider類:
上面我們通過appwidget-provider標籤就可以得到初始化的佈局,視圖等,但我們的widget要實時更新怎麼辦,要響應用戶操作怎麼辦,這就需要額外的類來輔助處理了,這個類就是AppWidgetProvider。
由於AppWidgetProvider要接收到當前widget的狀態(是否被添加,是否被刪除等),所以要接收通知,必然是派生自BroadcastReceiver。
AppWidgetProvider中的廣播處理函數如下:(根據不同的使用情況,重寫不同的函數)
onUpdate():
在3種情況下會調用OnUpdate()。onUpdate()是在main線程中進行,因此如果處理需要花費時間多於10秒,處理應在service中完成。(第二篇會講爲什麼還要有service)
(1)在時間間隔到時調用,時間間隔在widget定義的android:updatePeriodMillis中設置;
(2)用戶拖拽到主頁,widget實例生成。無論有沒有設置Configure activity,我們在Android4.4的測試中,當用戶拖拽圖片至主頁時,widget實例生成,會觸發onUpdate(),然後再顯示activity(如果有)。這點和資料說的不一樣,資料認爲如果設置了Configure acitivity,就不會在一開始調用onUpdate(),而實驗顯示當實例生成(包括創建和重啓時恢復),都會先調用onUpate()。在本例,由於此時在preference尚未有相關數據,創建實例時不能有效進行數據設置。
(3)機器重啓,實例在主頁上顯示,會再次調用onUpdate()
onDeleted(Context, int[]):
當 widget 被刪除時被觸發。
onEnabled(Context):
當第1個 widget 的實例被創建時觸發。也就是說,如果用戶對同一個 widget 增加了兩次(兩個實例),那麼onEnabled()只會在第一次增加widget時觸發。
onDisabled(Context):
當最後1個 widget 的實例被刪除時觸發。
onReceive(Context, Intent):
在接收到廣播時,調用。
二、實戰
先看一下顯示效果:
就這麼一個桌面widget,一個音樂播放器,這一篇只是顯示出來,後面我們會慢慢完善它的功能。
1、appwidget-provider及佈局文件
前面我們講過,appwidget-provider提供了桌面widget的初始化顯示狀態、默認圖標、大小等功能,但它必須放在res/xml文件夾下,看看這裏的代碼:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/example_appwidget"
android:minHeight="60dp"
android:minWidth="180dp"
android:previewImage="@drawable/preview"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen|keyguard" >
</appwidget-provider>
這裏有幾個參數:
prviewImage:就是添加桌面控件時,我們的控件在列表中的顯示狀態,如下圖所示,下面這個醜娃娃就是我們的顯示圖標……
android:initialLayout="@layout/example_appwidget":這個就是指定初始化顯示時,應該顯示的佈局;
下面看看這個佈局文件:example_appwidget.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#33000000"
android:gravity="center"
android:orientation="horizontal" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dip"
android:background="@drawable/car_musiccard_up"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dip"
android:background="@drawable/car_musiccard_play"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/car_musiccard_down"/>
</LinearLayout>
這個佈局就是最上頭顯示出來的那個播放器的佈局。
2、ExampleAppWidgetProvider extends AppWidgetProvider
在佈局成功之後,下面就是根據通知實現邏輯了,這篇我們只接收通知,邏輯下篇再說。首先新建一個類ExampleAppWidgetProvider,將其派生自AppWidgetProvider;然後就會有下面的代碼:
public class ExampleAppWidgetProvider extends AppWidgetProvider {
/*
* 在3種情況下會調用OnUpdate()。onUpdate()是在main線程中進行,因此如果處理需要花費時間多於10秒,處理應在service中完成。
*(1)在時間間隔到時調用,時間間隔在widget定義的android:updatePeriodMillis中設置;
*(2)用戶拖拽到主頁,widget實例生成。無論有沒有設置Configure activity,我們在Android4.4的測試中,當用戶拖拽圖片至主頁時,widget實例生成,會觸發onUpdate(),然後再顯示activity(如果有)。這點和資料說的不一樣,資料認爲如果設置了Configure acitivity,就不會在一開始調用onUpdate(),而實驗顯示當實例生成(包括創建和重啓時恢復),都會先調用onUpate()。在本例,由於此時在preference尚未有相關數據,創建實例時不能有效進行數據設置。
*(3)機器重啓,實例在主頁上顯示,會再次調用onUpdate()
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
// widget被刪除時調用
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
Log.d(TAG, "onDeleted(): appWidgetIds.length=" + appWidgetIds.length);
super.onDeleted(context, appWidgetIds);
}
// 最後一個widget被刪除時調用
@Override
public void onDisabled(Context context) {
Log.d(TAG, "onDisabled");
super.onDisabled(context);
}
// 第一個widget被創建時調用
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
}
// 接收廣播的回調函數
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
}
這裏不是繼承接口,這幾個函數並不是需要全部重寫的,根據需要,要用到哪個可以重寫哪個。
3、註冊ExampleAppWidgetProvider
前面我們講到AppWidgetProvider派生自BroadcastReciver,所以要提前註冊,有關BroadcastReciver的註冊有兩種方法,靜態註冊和動態註冊,因爲這裏要接收來自的消息,而且在程序啓動時就開始自動監聽,所以,這裏需要靜態註冊。
<!-- 聲明widget對應的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>
(1)接收的action定義爲:"android.appwidget.action.APPWIDGET_UPDATE"這表明接收系統發來的有關這個app的所有widget的消息(主要是增加、刪除)。
(2)<meta-data> 指定了 AppWidgetProviderInfo 對應的資源文件
android:name -- 指定metadata名,指定爲android.appwidget.provider表示這個data中的數據是AppWidgetProviderInfo 類型的
android:resource -- 指定 AppWidgetProviderInfo 對應的資源路徑。即,xml/example_appwidget_info.xml。
三、可能出現的錯誤:
1、有關佈局錯誤
在構造Widget佈局時,App Widget支持的佈局和控件非常有限,有如下幾個:
App Widget支持的佈局:
- FrameLayout
- LinearLayout
- RelativeLayout
- GridLayout
App Widget支持的控件:
- AnalogClock
- Button
- Chronometer
- ImageButton
- ImageView
- ProgressBar
- TextView
- ViewFlipper
- ListView
- GridView
- StackView
- AdapterViewFlipper
除此之外的所有控件(包括自定義控件)都無法顯示,無法顯示時,添加出來的widget會顯示“加載佈局出錯”
2、appwidget-provider出現錯誤
如果appwidget-provider頁面出現錯誤提示:error: No resource identifier found for attribute 'widgetCategory' in package
'android'
這是由於build target應該在17以上,有兩種方法解決:
方法1:找到工程中的project.properties文件將target=android-14改爲target=android-17
方法2:工程上右鍵-》properties 選擇Android,將Project Build Target改爲17或以上,如圖:(改完之後rebuild一下工程)