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了。