轉載請注明出處:http://blog.csdn.net/qinjuning
今天給大家展現的如何創建一個窗口小部件(AppWidget)以及如何在自己的應用程序中添加窗口小部件(AppWidget)。
本文組織如下:
第一部分:創建一個窗口小部件;
第二部分:在自己的應用程序中添加窗口小部件(AppWidget)。
整個功能其實實現起來也是很簡單的,只望大家能耐着性子點看 。
第一部分 、創建一個窗口小部件
網上資料已經許多了 ,在此先給大家推薦幾個不錯的Blog ,大家可以先去看看:
從中我們知道:
1、每個AppWidget都有一個AppWidgetProviderInfo對象,該對象描述了每個AppWidget的基本數據(meta-data)信息 ,
其定義在<appwidget-provider>節點信息。
2、每個AppWidget都對應一個RemoteViews視圖對象,該RemoteViews提供了特定AppWidget的展示(View視圖)和操作
(例如,點擊該RemoteViews會跨進程處理一些事情)。
關於RemoteViews類請參考:Android中文API(138) —— RemoteViews
3、AppWidgetManager類維護了應用程序中所有的AppWidget,並且為給每個AppWidget特定的Id去標識他們(一般我們
用 appWidgetId去標識)。通過給定的appWidgetId,AppWidgetManager可以管理對應的AppWidget,例如:更新該
AppWidgetId的RemoteViews視圖,刪除該AppWidget對象等 。
4、AppWidgetProvider廣播類從來說是一個監聽器,系統把對AppWidget的操作(例如,創建和更新等)分發給
AppWidgetProvider類去處理。
對每個AppWidget,我們可以創建多個其多個實例,當然這些實例對應於不同的appWidgetId。 假設存在這么個
MyAppWidgetProvider廣播類,以及對應的MyAppWidgetProviderInfo對象。 那么,則存在如下關系:
MyAppWidgetProvider.class : 代表了由該MyAppWidgetProvider創建的窗口小部件(AppWidget)的類型,一般用
CompontentName對象形式表示 。 那么存在如下關系:

從上圖可是,每個appWidget都對應於一個MyAppWidgetProvider類,於是當任何一個appWidgetId發生變化時,我們需要
同步其他實例,保持同步性。
AppWidgetProviderInfo類補充說明:
publicComponentNameconfigure: 一般為一個Activity,表明該Activity復雜需要管理AppWidget的創建操作。
public int updatePeriodMillis:用來更新AppWidget,但該屬性在SDK1.5已廢除
AppWidgetProvider類介紹:
常用方法:
onDeleted() : 當該類型的AppWidget每次被刪除時,調用此方法
onDisabled() : 當該類型的窗口小部件(AppWidget)全被刪除時,調用此方法
onEnabled() : 當第一次創建該類型的AppWidget時,調用此方法
onReceive() : 廣播接受者方法 , 用來接受廣播消息
onUpdate() : 每次創建該類型的AppWidget都會調用此方法 , 通常來說我們需要在該方法里為該AppWidget指定
RemoteViews對象。
AppWidgetManager類介紹:
常用常量:
ACTION_APPWIDGET_PICK= "android.appwidget.action.APPWIDGET_PICK“
說明:列出所有能夠創建AppWidget的對象,該對象一般為自定義的AppWidgetProvider廣播接受者。
注意:發送該Intent必須添加附加值:EXTRA_APPWIDGET_ID 。
該EXTRA_APPWIDGET_ID含義:該appWidgetId與我們發送Action為ACTION_APPWIDGET_PICK 后
所選擇的AppWidget綁定。因此,我們可以通過這個appWidgetId獲取該AppWidget的信息了。
ACTION_APPWIDGET_CONFIGURE= "android.appwidget.action.APPWIDGET_CONFIGURE”
說明: 如果選擇的AppWidget配置了android:configure 屬性,需要再次啟動性對應的Activity,繼而進一步去選擇
AppWidget。同時發送該Intent必須添加附加值:EXTRA_APPWIDGET_ID,含義同上。
常用方法:
public int[] getAppWidgetIds(ComponentName provider)
功能:獲取對應ComponentName類型的所有appWidgetId
參數說明: provider 通常為 XXXAppProvider.class類型
publicAppWidgetProviderInfo getAppWidgetInfo(int appWidgetId)
功能: 獲取特定appWidgetId對應的AppWidgetProviderInfo對象
public staticAppWidgetManager getInstance(Contextcontext)
功能: 獲取 AppWidgetManager對象
public void updateAppWidget(int appWidgetId, RemoteViews views)
功能: 以特定的views視圖更新appWidgetId的窗口小部件(AppWidget) 。同時會發送ACTION_APPWIDGET_UPDATE廣播
public void updateAppWidget(int[] appWidgetIds,RemoteViews views)
功能:以特定的views視圖更新所有appWidgetIds的窗口小部件(AppWidget),同時發送ACTION_APPWIDGET_UPDATE
廣播
public void updateAppWidget(ComponentName provider, RemoteViews views)
功能: 已特定的views更新組件類型為provider的所有窗口小部件(AppWidget),同時發送ACTION_APPWIDGET_UPDATE
廣播。
示例Demo :
說明:創建一個簡單的AppWidget實例,點擊按鈕后可以更該圖片資源顯示 ,具體代碼在
截圖為:
關於如何創建一個AppWidget的教材,我也不再多說了,大家可以參考上面我提到的兩篇重量級博客去學習:
PS: 具體代碼可在后面下載 。
再次強調一點,每個AppWidget都對應與AppWidgetProvider , 我們需要同步更新這些AppWidget對象。
第二部分:在自己的應用程序中添加窗口小部件(AppWidget)
本部分的主要功能是像Launchcer那樣添加AppWidget 。 知識點介紹如下:
1、AppWidgetHost 類
功能:對每個應用程序App,該類提供了和AppWidgetService(該AppWidgetService用來管理所有AppWidget,類似於
NotificationManagerService系統服務管理所有Notifciation,不懂?其實我也不懂,知其大意即可)交互,用來更新、管理
AppWidget。打個比喻:AppWidgetHost是宿主對象,每個AppWidget都是寄生蟲,可以附加在(顯示)AppWidgetHost上。
每個能添加、顯示AppWidget的Activity都是一個AppWidgetHost對象,比如Launcher.java(Activity對象),以及我們后面自定
義的MainActivity.java(Activity對象)。
常用方法為:
public AppWidgetHost(Context context, int hostId)
功能:構造一個AppWidgetHost對象
參數: hostId 大意是該AppWidgetHost(宿主對象)對應的Id號,一般賦予一整數即可。
public int allocateAppWidgetId()
功能:申請一個新的appWidgetId ,該id會與新創建的AppWidget綁定。
public void startListening()
功能:監聽所有AppWidget的變化 ,該方法必須在Activity的onCreate()/onStart()調用,否則 AppWidget是不會得到更新的
public void stopListening()
功能: 對應於startListening(),即停止對AppWidget的更新監聽。可以在Activity的onStop()方法里調用 ,
一般無需調用此方法去停止監聽。
public final AppWidgetHostView createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget)
功能: 根據指定的appWidgetId以及AppWidgetProviderInfo對象去構建一個AppWidgetHostView對象(具體該對象,
參見下 文)。
AppWidgetHostView 與 RemoteViews的區別
對每個AppWidget內部而言,都有一個RemoteViews對象,用於視圖顯示;而對於外部而已,則以AppWidgetHostView
形式代言 這個RemoteViews視圖。換句話來說就是,AppWidgetHost對象而言,它並不知道RemoteViews存在,而只是知
RemoteViews 的代言人AppWidgetHostView。
好了 ,該說明的都說明了,下面最后給大家補充一下如何利用在自己的應用程序里添加窗口小部件(AppWidget) 。
也是兩步走:
第一步: 發送Action為ACTION_APPWIDGET_PICK的 Intent ,則所有能創建窗口小部件的AppWidgetProvider的廣播
接收者都會顯示 ,同時為該新創建的AppWidget分配一個appWidgetId ,該appWidgetId即可唯一標記我們選擇的
AppWidget。
第二步:如果選擇的AppWidget對應地AppWidgetProviderInfo對象配置了android:configure屬性,則需要在此啟動該配置
屬性(一般為一個Activity類) ,然后在完成添加AppWidget的操作 ; 否則,沒有配置android:configure屬性,就可以添加
AppWidget的操作。
示例Demo截圖:

主工程流程如下:
- package com.qin.addappwidget;
- import android.app.Activity;
- import android.appwidget.AppWidgetHost;
- import android.appwidget.AppWidgetHostView;
- import android.appwidget.AppWidgetManager;
- import android.appwidget.AppWidgetProviderInfo;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- import android.widget.ImageView;
- import android.widget.LinearLayout;
- import android.widget.TextView;
- import android.widget.Toast;
- public class MainActivity extends Activity
- {
- private static String TAG = "AddAppWidget" ;
- private Button btAddShortCut;
- private LinearLayout linearLayout ; // 裝載Appwidget的父視圖
- private static final int MY_REQUEST_APPWIDGET = 1;
- private static final int MY_CREATE_APPWIDGET = 2;
- private static final int HOST_ID = 1024 ;
- private AppWidgetHost mAppWidgetHost = null ;
- AppWidgetManager appWidgetManager = null;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- btAddShortCut = (Button) findViewById(R.id.bt_addShortcut);
- linearLayout = (LinearLayout)findViewById(R.id.linearLayout) ;
- //其參數hostid大意是指定該AppWidgetHost 即本Activity的標記Id, 直接設置為一個整數值吧 。
- mAppWidgetHost = new AppWidgetHost(MainActivity.this, HOST_ID) ;
- //為了保證AppWidget的及時更新 , 必須在Activity的onCreate/onStar方法調用該方法
- // 當然可以在onStop方法中,調用mAppWidgetHost.stopListenering() 停止AppWidget更新
- mAppWidgetHost.startListening() ;
- //獲得AppWidgetManager對象
- appWidgetManager = AppWidgetManager.getInstance(MainActivity.this) ;
- btAddShortCut.setOnClickListener(new View.OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- //顯示所有能創建AppWidget的列表 發送此 ACTION_APPWIDGET_PICK 的Action
- Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK) ;
- //向系統申請一個新的appWidgetId ,該appWidgetId與我們發送Action為ACTION_APPWIDGET_PICK
- // 后所選擇的AppWidget綁定 。 因此,我們可以通過這個appWidgetId獲取該AppWidget的信息了
- //為當前所在進程申請一個新的appWidgetId
- int newAppWidgetId = mAppWidgetHost.allocateAppWidgetId() ;
- Log.i(TAG, "The new allocate appWidgetId is ----> " + newAppWidgetId) ;
- //作為Intent附加值 , 該appWidgetId將會與選定的AppWidget綁定
- pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, newAppWidgetId) ;
- //選擇某項AppWidget后,立即返回,即回調onActivityResult()方法
- startActivityForResult(pickIntent , MY_REQUEST_APPWIDGET) ;
- }
- });
- }
- // 如果
- protected void onActivityResult(int requestCode, int resultCode, Intent data)
- {
- //直接返回,沒有選擇任何一項 ,例如按Back鍵
- if(resultCode == RESULT_CANCELED)
- return ;
- switch(requestCode){
- case MY_REQUEST_APPWIDGET :
- Log.i(TAG, "MY_REQUEST_APPWIDGET intent info is -----> "+data ) ;
- int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ;
- Log.i(TAG, "MY_REQUEST_APPWIDGET : appWidgetId is ----> " + appWidgetId) ;
- //得到的為有效的id
- if(appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID){
- //查詢指定appWidgetId的 AppWidgetProviderInfo對象 , 即在xml文件配置的<appwidget-provider />節點信息
- AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;
- //如果配置了configure屬性 , 即android:configure = "" ,需要再次啟動該configure指定的類文件,通常為一個Activity
- if(appWidgetProviderInfo.configure != null){
- Log.i(TAG, "The AppWidgetProviderInfo configure info -----> " + appWidgetProviderInfo.configure ) ;
- //配置此Action
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ;
- intent.setComponent(appWidgetProviderInfo.configure) ;
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- startActivityForResult(intent , MY_CREATE_APPWIDGET) ;
- }
- else //直接創建一個AppWidget
- onActivityResult(MY_CREATE_APPWIDGET , RESULT_OK , data) ; //參數不同,簡單回調而已
- }
- break ;
- case MY_CREATE_APPWIDGET:
- completeAddAppWidget(data) ;
- break ;
- }
- }
- //向當前視圖添加一個用戶選擇的
- private void completeAddAppWidget(Intent data){
- Bundle extra = data.getExtras() ;
- int appWidgetId = extra.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID , -1) ;
- //等同於上面的獲取方式
- //int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ;
- Log.i(TAG, "completeAddAppWidget : appWidgetId is ----> " + appWidgetId) ;
- if(appWidgetId == -1){
- Toast.makeText(MainActivity.this, "添加窗口小部件有誤", Toast.LENGTH_SHORT) ;
- return ;
- }
- AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;
- AppWidgetHostView hostView = mAppWidgetHost.createView(MainActivity.this, appWidgetId, appWidgetProviderInfo);
- //linearLayout.addView(hostView) ;
- int widget_minWidht = appWidgetProviderInfo.minWidth ;
- int widget_minHeight = appWidgetProviderInfo.minHeight ;
- //設置長寬 appWidgetProviderInfo 對象的 minWidth 和 minHeight 屬性
- LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(widget_minWidht, widget_minHeight);
- //添加至LinearLayout父視圖中
- linearLayout.addView(hostView,linearLayoutParams) ;
- }
- }
最后 ,關於AppWidget點擊后觸發的相應事件,是通過設置該AppWidget的RemoteViews的某個控件點擊事件而觸發的。
示例DEMO下載地址:http://download.csdn.net/detail/qinjuning/4018449
