Android之Widget
其實Android API開發指南中的App Widgets章節 已經說得很清楚了,下面只是對自己的理解進行一次梳理。
--
AppWidget 就是HomeScreen上顯示的小部件,提供直觀的交互操作。通過在HomeScreen中長按,在彈出的對話框中選擇Widget部件來進行創建,長按部件后並拖動到垃圾箱里進行刪除。同一個Widget部件可以同時創建多個。
AppWidget的實現主要涉及到以下類: AppWidgetProvider RemoteViews
AppWidgetManager
1.
首先需要提供一個定義了Widget界面布局的XML文件(位於res/layout/..),需要注意的是使用的組件必須是RemoteViews所支持的,目前原生API中支持的組件如下:
FrameLayout LinearLayout RelativeLayout
AnalogClock
Button Chronmeter ImageButton ImageView ProgressBar
TextView
*如果使用了除此之外的組件,則在Widget創建時會導致android.view.InflateExceptionn異常。
PS:這就導致有一些功能或樣式無法實現,如很基本的list或文本編輯框都是無法直接實現的。如果想自定義Widget中的View的話只能通過修改framework來提供相應組件的支持。
2. 然后需要提供一個xml文件來定義Widget的基本屬性,放置到res/xml/..目錄下。
如果使用的是Eclipse可按如下操作: 1) 在res/目錄下創建xml/目錄
2)創建xml文件(名字可任意),選擇類型為AppWidgetProvider 3)在彈出的便捷界面進行參數設置
主要設置的參數如下: minWidth: 定義Wdiget組件的寬度 minHeight: 定義Wdiget組件的高度
updatePeriodMillis: 更新的時間周期 initialLayout: Widget的布局文件
configure: 如果需要在啟動前先啟動一個Activity進行設置,在這里給出Activity的完整類名(后面會說到,與一般Activity的實現有些許差別)
*Widget大小的計算單元格數*74)-2,API上說是為了防止像素計算時的整數舍入導致錯所以-2...不是很明白
一個完整的樣例: Xml代碼 收藏代碼
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp" android:minHeight="32dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/widget_provider"
android:configure="com.demo.widget.MyWidgetConfiguration" >
</appwidget-provider>
3.
xml都定義好后,接下來就是創建一個繼承自AppWidgetProvider的子類,AppWidgetProvider實際上就是一個BroadcastReceiver,里面提供了以下函數:
onReceive(Context, Intent)
onUpdate(Context , AppWidgetManager, int[] appWidgetIds)
onEnabled(Context) onDeleted(Context, int[] appWidgetIds)
onDisabled(Context) 可通過重寫以上函數來監聽Widget狀態的變化並進行相應的處理。
以上函數具體調用情況歸納如下: [啟動 - 無confiure Activity] onReceive
onEnabled —— 第一個widget被顯示 onReceive onUpdate —— 刷新界面
[啟動 - 帶confiuration Activity] onReceive onUpdate
[拖動]
<無狀態變化>
[周期更新] onReceive onUpdate
[刪除]
onReceive onDeleted —— widget被刪除 onReceive
onDisabled —— 最后一個widget被移除
[啟動時位置不夠] onReceive onEnabled
onReceive onUpdate onReceive onDeleted onReceive
onDisabled
*每次狀態的變化會觸發onReceive,一般該函數是不需要重寫的。
簡單了解AppWidgetProvider之后,我們來看具體實現。
這里創建一個MyAppWidgetProvider繼承AppWidgetProvider: Java代碼 收藏代碼
public class MyWidgetProvider extends AppWidgetProvider {
static final String TAG = "widget";
/** * 更新 */
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
Log.i(TAG, "onUpdate"); }
/** * 第一個Widget組件啟動時觸發 */
public void onEnabled(Context context){
Log.i(TAG, "onEnabled"); }
/** * 最后一個Widget組件關閉時觸發 */
public void onDisabled(Context context){
Log.i(TAG, "onDisabled"); }
/** * 任一Widget組件被刪除時觸發
*/
public void onDeleted(Context context, int[] appWidgetIds){
Log.i(TAG, "onDeleted"); }
/** * 以上函數觸發前會先觸發該函數,一般不需要重寫 */
public void onReceive(Context context, Intent intent){
Log.i(TAG, "onReceive");
super.onReceive(context, intent); }
}
其中onUpdate顧名思義是對Widget進行更新的,前面定義的更新周期就是作用於該函數的。
Widget的更新與Activity不同,必須借助於RemoteViews和AppWidgetMananger。具體實現如下:
Java代碼 收藏代碼
public void onUpdate(Context context, AppWidgetMananger appWidgetManager, int[] appWidgetIds){
int N = appWidgetIds.length; // 可能啟動了多個Widget,appWidgetIds記錄了這些Widget的ID
for(int i=0; i<N; i++){
RemoteViews views = new RemoteViews(getPackageName(), R.layout.widget_views);
appWidgetManager.updateAppWidget(appWidgetIds[i], views);
} }
其中需要注意的是,雖然RemoteViews參數都是一樣的,但是對於每個Widget最好都新創建一個再進行傳遞,否則會導致一些錯誤。具體可參考AppWidget RemoteViews 內存溢出 。
其他函數的可以根據需要實現。
由於無法獲取到RemoteViews創建的界面中的元素,對於Widget中組件的操作只能通過RemoteViews所提供的有限的函數進行,常用的有:
setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)
setProgressBar(int viewId, int max, int progress, boolean indeterminate)
setTextViewText(int viewId, CharSequence text)
setViewVisibility(int viewId, int visibility)
詳細函數列表可參考API中的RemoteViews類 。
4. 最后,更新AndroidManifest.xml。
AppWidgetProvider對應一個receiver屬性: Xml代碼 收藏代碼
<receiver android:name="MyWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
</intent-filter> <meta-data android:resource="@xml/widget_property" android:name="android.appwidget.provider"></meta-data>
</receiver>
5.
提供Configuration Activity
Configuration Activity是一個在Widget啟動前先啟動的Activity,方便用戶對Widget的屬性進行設置。
在res/xml/...下對應的"屬性文件"中添加configure字段指定啟動的Activity,並在 AndroidManifest.xml中該Activity下提供一個action為 android.appwidget.action.APPWIDGET_CONFIGURE 的IntenFilter。
需要注意的是,
如果設置了Configure屬性,則必須在指定的Activity中進行如下處理:
1.在onCreate中setContentView()函數前添加setResult(RESULT_CANCLE) ,這樣如果在Activity初始化完成前按下了BACK按鍵,則Widget不會啟動;
2.在setContentView()函數之后(不一定要在onCreate中,在Activity退出前即可),添加如下設置以指定需要啟動的Widget:
Java代碼 收藏代碼
int mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
否則會導致退出Activity后Widget不啟動。
>> Widget創建步驟匯總: 1.定義Widget布局XML -> res/layout/...
2.定義Widget屬性文件(xml) -> res/xml/... 3. 創建AppWidgetProider子類,實現onUpdate()等函數,在manifest中注冊receiver,添加一個action為 android.appwidget.action.APPWIDGET_UPDATE 的IntentFilter,並添加如下<meta- data>標識: Xml代碼 收藏代碼
<meta-data android:resource="@xml/<屬性文件xml>" android:name="android.appwidget.provider"></meta-data>
4. 創建Coniguration Activity(注意處理好setResult),添加到屬性文件中的Configure屬性,在manifest中注冊activity,添加一個 action為android.appwidget.action.APPWIDGET_CONFIGURE 的IntentFilter
最后附上一個完整的例子, 實現思路如下: 1. 提供一個Configuration Activity,這里只簡單顯示一行文字;
2.退出后Widget啟動;
--
AppWidget 就是HomeScreen上顯示的小部件,提供直觀的交互操作。通過在HomeScreen中長按,在彈出的對話框中選擇Widget部件來進行創建,長按部件后並拖動到垃圾箱里進行刪除。同一個Widget部件可以同時創建多個。
AppWidget的實現主要涉及到以下類: AppWidgetProvider RemoteViews
AppWidgetManager
1.
首先需要提供一個定義了Widget界面布局的XML文件(位於res/layout/..),需要注意的是使用的組件必須是RemoteViews所支持的,目前原生API中支持的組件如下:
FrameLayout LinearLayout RelativeLayout
AnalogClock
Button Chronmeter ImageButton ImageView ProgressBar
TextView
*如果使用了除此之外的組件,則在Widget創建時會導致android.view.InflateExceptionn異常。
PS:這就導致有一些功能或樣式無法實現,如很基本的list或文本編輯框都是無法直接實現的。如果想自定義Widget中的View的話只能通過修改framework來提供相應組件的支持。
2. 然后需要提供一個xml文件來定義Widget的基本屬性,放置到res/xml/..目錄下。
如果使用的是Eclipse可按如下操作: 1) 在res/目錄下創建xml/目錄
2)創建xml文件(名字可任意),選擇類型為AppWidgetProvider 3)在彈出的便捷界面進行參數設置
主要設置的參數如下: minWidth: 定義Wdiget組件的寬度 minHeight: 定義Wdiget組件的高度
updatePeriodMillis: 更新的時間周期 initialLayout: Widget的布局文件
configure: 如果需要在啟動前先啟動一個Activity進行設置,在這里給出Activity的完整類名(后面會說到,與一般Activity的實現有些許差別)
*Widget大小的計算單元格數*74)-2,API上說是為了防止像素計算時的整數舍入導致錯所以-2...不是很明白
一個完整的樣例: Xml代碼 收藏代碼
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp" android:minHeight="32dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/widget_provider"
android:configure="com.demo.widget.MyWidgetConfiguration" >
</appwidget-provider>
3.
xml都定義好后,接下來就是創建一個繼承自AppWidgetProvider的子類,AppWidgetProvider實際上就是一個BroadcastReceiver,里面提供了以下函數:
onReceive(Context, Intent)
onUpdate(Context , AppWidgetManager, int[] appWidgetIds)
onEnabled(Context) onDeleted(Context, int[] appWidgetIds)
onDisabled(Context) 可通過重寫以上函數來監聽Widget狀態的變化並進行相應的處理。
以上函數具體調用情況歸納如下: [啟動 - 無confiure Activity] onReceive
onEnabled —— 第一個widget被顯示 onReceive onUpdate —— 刷新界面
[啟動 - 帶confiuration Activity] onReceive onUpdate
[拖動]
<無狀態變化>
[周期更新] onReceive onUpdate
[刪除]
onReceive onDeleted —— widget被刪除 onReceive
onDisabled —— 最后一個widget被移除
[啟動時位置不夠] onReceive onEnabled
onReceive onUpdate onReceive onDeleted onReceive
onDisabled
*每次狀態的變化會觸發onReceive,一般該函數是不需要重寫的。
簡單了解AppWidgetProvider之后,我們來看具體實現。
這里創建一個MyAppWidgetProvider繼承AppWidgetProvider: Java代碼 收藏代碼
public class MyWidgetProvider extends AppWidgetProvider {
static final String TAG = "widget";
/** * 更新 */
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
Log.i(TAG, "onUpdate"); }
/** * 第一個Widget組件啟動時觸發 */
public void onEnabled(Context context){
Log.i(TAG, "onEnabled"); }
/** * 最后一個Widget組件關閉時觸發 */
public void onDisabled(Context context){
Log.i(TAG, "onDisabled"); }
/** * 任一Widget組件被刪除時觸發
*/
public void onDeleted(Context context, int[] appWidgetIds){
Log.i(TAG, "onDeleted"); }
/** * 以上函數觸發前會先觸發該函數,一般不需要重寫 */
public void onReceive(Context context, Intent intent){
Log.i(TAG, "onReceive");
super.onReceive(context, intent); }
}
其中onUpdate顧名思義是對Widget進行更新的,前面定義的更新周期就是作用於該函數的。
Widget的更新與Activity不同,必須借助於RemoteViews和AppWidgetMananger。具體實現如下:
Java代碼 收藏代碼
public void onUpdate(Context context, AppWidgetMananger appWidgetManager, int[] appWidgetIds){
int N = appWidgetIds.length; // 可能啟動了多個Widget,appWidgetIds記錄了這些Widget的ID
for(int i=0; i<N; i++){
RemoteViews views = new RemoteViews(getPackageName(), R.layout.widget_views);
appWidgetManager.updateAppWidget(appWidgetIds[i], views);
} }
其中需要注意的是,雖然RemoteViews參數都是一樣的,但是對於每個Widget最好都新創建一個再進行傳遞,否則會導致一些錯誤。具體可參考AppWidget RemoteViews 內存溢出 。
其他函數的可以根據需要實現。
由於無法獲取到RemoteViews創建的界面中的元素,對於Widget中組件的操作只能通過RemoteViews所提供的有限的函數進行,常用的有:
setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)
setProgressBar(int viewId, int max, int progress, boolean indeterminate)
setTextViewText(int viewId, CharSequence text)
setViewVisibility(int viewId, int visibility)
詳細函數列表可參考API中的RemoteViews類 。
4. 最后,更新AndroidManifest.xml。
AppWidgetProvider對應一個receiver屬性: Xml代碼 收藏代碼
<receiver android:name="MyWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
</intent-filter> <meta-data android:resource="@xml/widget_property" android:name="android.appwidget.provider"></meta-data>
</receiver>
5.
提供Configuration Activity
Configuration Activity是一個在Widget啟動前先啟動的Activity,方便用戶對Widget的屬性進行設置。
在res/xml/...下對應的"屬性文件"中添加configure字段指定啟動的Activity,並在 AndroidManifest.xml中該Activity下提供一個action為 android.appwidget.action.APPWIDGET_CONFIGURE 的IntenFilter。
需要注意的是,
如果設置了Configure屬性,則必須在指定的Activity中進行如下處理:
1.在onCreate中setContentView()函數前添加setResult(RESULT_CANCLE) ,這樣如果在Activity初始化完成前按下了BACK按鍵,則Widget不會啟動;
2.在setContentView()函數之后(不一定要在onCreate中,在Activity退出前即可),添加如下設置以指定需要啟動的Widget:
Java代碼 收藏代碼
int mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
否則會導致退出Activity后Widget不啟動。
>> Widget創建步驟匯總: 1.定義Widget布局XML -> res/layout/...
2.定義Widget屬性文件(xml) -> res/xml/... 3. 創建AppWidgetProider子類,實現onUpdate()等函數,在manifest中注冊receiver,添加一個action為 android.appwidget.action.APPWIDGET_UPDATE 的IntentFilter,並添加如下<meta- data>標識: Xml代碼 收藏代碼
<meta-data android:resource="@xml/<屬性文件xml>" android:name="android.appwidget.provider"></meta-data>
4. 創建Coniguration Activity(注意處理好setResult),添加到屬性文件中的Configure屬性,在manifest中注冊activity,添加一個 action為android.appwidget.action.APPWIDGET_CONFIGURE 的IntentFilter
最后附上一個完整的例子, 實現思路如下: 1. 提供一個Configuration Activity,這里只簡單顯示一行文字;
2.退出后Widget啟動;