1.建立AppWidget布局,包含兩個TextView用來顯示內容: 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_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/tv1" android:textColor="#FF0000" android:textSize="24sp" android:textStyle="bold" android:text="-1"></TextView> <TextView android:layout_height="wrap_content" android:id="@+id/tv2" android:textSize="24sp" android:textStyle="bold" android:textColor="#00FF00" android:layout_width="fill_parent" android:text="-2"></TextView> </LinearLayout> 2.在res下建立xml目錄,再在xml目錄裡面建立AppWidget資訊xml檔案: 2.1建立xml檔案時,type選擇AppWidget Provider。 2.2填充屬性: 寬高的計算公式為:佔用螢幕格數*74-2 Update period millis:設定為0,手動重新整理。根據實驗,設定不為0時,至少在2.2上系統根本不按照設定的值重新整理,還是自己控制重新整理時機好了。 Initial layout:就添控制項要使用的布局。 Configure暫時不用,留空。 3.建立AppWidgetDemo類: 重載AppWidgetProvider中的所有函數,每個函數裡面增加輸出語句,以查看調用順序。 Java代碼 public class AppWidgetDemo extends AppWidgetProvider { @Override public void onDeleted(Context context, int[] appWidgetIds) { // TODO Auto-generated method stub super.onDeleted(context, appWidgetIds); Log.e("AppWidgetDemo", "onDeleted"); } @Override public void onDisabled(Context context) { // TODO Auto-generated method stub super.onDisabled(context); Log.e("AppWidgetDemo", "onDisabled"); } @Override public void onEnabled(Context context) { // TODO Auto-generated method stub super.onEnabled(context); Log.e("AppWidgetDemo", "onEnabled"); } @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub super.onReceive(context, intent); Log.e("AppWidgetDemo", "onReceive,Action:" + intent.getAction()); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // TODO Auto-generated method stub super.onUpdate(context, appWidgetManager, appWidgetIds); Log.e("AppWidgetDemo", "onUpdate,Count:" + appWidgetIds.length); } } 4.在AndroidManifest.xml檔案中聲明此Widget: 添加一個Receiver,其name為AppWidgetDemo類的類名。 Xml代碼 <receiver android:name="AppWidgetDemo"></receiver> 為此Receiver添加Intent filter,接收系統發出android.appwidget.action.APPWIDGET_UPDATE的Intent。 Xml代碼 <receiver android:name="AppWidgetDemo"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action> </intent-filter> </receiver> 此外還要為此receiver添加meta-data資訊,以告知系統相關的AppWidgetProvider資訊: Xml代碼 <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info_demo"></meta-data> meta-data的name是約定好的android.appwidget.provider,resource則是第2步建立的AppWidget資訊xml檔案。 5.至此AppWidget已經可用了,安裝到模擬器上看下運行流程。 5.1添加一個Widget到案頭上: onEnabled被呼叫:按照說明,當案頭上出現第一個此Widget的執行個體時,此函數被呼叫。 onReceive被呼叫:onReceive,Action:android.appwidget.action.APPWIDGET_ENABLED onUpdate被呼叫:onUpdate,Count:1,並且待更新的AppWidget數量為1 onReceive被呼叫:onReceive,Action:android.appwidget.action.APPWIDGET_UPDATE 5.2再添加一個Widget到案頭上: onUpdate被呼叫:onUpdate,Count:1,並且待更新的AppWidget數量仍然為1,而不是2。 onReceive被呼叫:onReceive,Action:android.appwidget.action.APPWIDGET_UPDATE 5.3從案頭上移除一個Widget: onDeleted:每個執行個體被移除時都會被呼叫 onReceive,Action:android.appwidget.action.APPWIDGET_DELETED 5.4再從案頭上移除一個Widget: onDeleted:仍然執行 onReceive,Action:android.appwidget.action.APPWIDGET_DELETED onDisabled:因為是最後一個活動的執行個體被移除了,所以被呼叫。 onReceive,Action:android.appwidget.action.APPWIDGET_DISABLED 6.重新整理AppWidget 6.1在onUpdate()中重新整理: onUpdate在AppWidget放到案頭時會被調用,在Update period millis達到時可能會被調用。 Java代碼 RemoteViews appWidgetView = new RemoteViews(context.getPackageName(), R.layout.widget_layout_demo); appWidgetView.setTextViewText(R.id.tv1, String.valueOf(mCount)); appWidgetView.setTextViewText(R.id.tv2, String.valueOf(mCount)); appWidgetManager.updateAppWidget(appWidgetIds, appWidgetView); 先擷取一個RemoteViews,也就是AppWidget的布局所對應的View; 使用指定的Id更新要更新的控制項; 更新整個RemoteViews,此時就可以更新AppWidget內容了。 但是這樣的代碼還有一個問題,當向案頭依次添加多個控制項時會出現下面這樣的效果: 即更新時沒有同時更新所有的AppWidget,這是因為onUpdate中傳進來的數組中只包含了1個id,如果想同時更新多個,那麼可以把更新語句更換為: Java代碼 appWidgetManager.updateAppWidget(new ComponentName(context, AppWidgetDemo.class), appWidgetView); 通過組件名可以把所有的名字元合的AppWidget都更新。 6.2在onReceive()中更新 6.2.1自訂Action通知重新整理: 在AndroidManifest.xml中定義的receiver的intent-filter增加一個自訂的Action: com.demo.appwidget.refresh Xml代碼 <receiver android:name="AppWidgetDemo"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action> <action android:name="com.demo.appwidget.refresh"></action> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info_demo"></meta-data> lt;/receiver> 包含此Action的Intent可以在後台服務或者Activity中發出,然後會被此Receiver接收,進而觸發onReceive。 本例中採用按鈕按下時廣播intent: Java代碼 btnSend.setOnClickListener(new OnClickListener() { public void onClick(View v) { Intent intent = new Intent(); intent.setAction("com.demo.appwidget.refresh"); intent.putExtra("value", teInput.getText().toString()); SendMsgActivity.this.sendBroadcast(intent); } }); 在接收端: Java代碼 if(intent.getAction().equals("com.demo.appwidget.refresh")) { String value = intent.getStringExtra("value"); RemoteViews appWidgetView = new RemoteViews(context.getPackageName(), R.layout.widget_layout_demo); appWidgetView.setTextViewText(R.id.tv1, value); appWidgetView.setTextViewText(R.id.tv2, value); AppWidgetManager.getInstance(context).updateAppWidget(new ComponentName(context, AppWidgetDemo.class), appWidgetView); } 判斷是否是感興趣的Action,是的話就取值,然後更新。 6.2.2接收系統訊息重新整理: 比如intent-filter中再增加一個action:"android.provider.Telephony.SMS_RECEIVED",在AndroidMenifest.xml中任意位置添加<uses-permission android:name="android.permission.RECEIVE_SMS" />設定好許可權,當系統有短訊息收到時就能觸發onReceive了。 但是有些Action比較特殊,比如android.intent.action.TIME_TICK,根據android.content.intent文檔中的描述:You can not receive this through components declared in manifests, only by exlicitly registering for it with Context.registerReceiver(). 這個Action在AndroidManifest.xml中聲明了也沒用,必須要自己開個服務註冊receiver才能收到,然後再轉寄一次給自己。 6.3直接在外部Activity或者Service中重新整理: Java代碼 btnRefresh.setOnClickListener(new OnClickListener() { public void onClick(View v) { String value = teInput.getText().toString(); RemoteViews appWidgetView = new RemoteViews(SendMsgActivity.this.getPackageName(), R.layout.widget_layout_demo); appWidgetView.setTextViewText(R.id.tv1, value); appWidgetView.setTextViewText(R.id.tv2, value); AppWidgetManager.getInstance(SendMsgActivity.this) .updateAppWidget(new ComponentName(SendMsgActivity.this, AppWidgetDemo.class), appWidgetView); } }); 此段代碼可直接重新整理AppWidget的內容,不會觸發其onUpdate()。 7.響應點擊事件 因為onUpdate是每個AppWidget被放置到案頭上時都會被呼叫的函數,所以在此函數中完成事件的關聯: Java代碼 Intent intent = new Intent(context, SendMsgActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); appWidgetView.setOnClickPendingIntent(R.id.tv1, pendingIntent); 另外要注意此段代碼必須要在appWidgetManager.updateAppWidget()之前,否則是不會生效的。 運行後可以點擊AppWidget的第一個控制項,就能呼叫指定的Activity了。 8.Config Activity 這個可直接參考SDK文檔中的Dev Guide-->App Widgets了。