Android AppWidget实例验证

1.创建AppWidget布局,包含两个TextView用来显示内容:
Xml代码
收藏代码
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"android:layout_height="fill_parent"
  4. android:orientation="vertical">
  5. <TextViewandroid:layout_height="wrap_content"
  6. android:layout_width="fill_parent"android:id="@+id/tv1"
  7. android:textColor="#FF0000"android:textSize="24sp"android:textStyle="bold"
  8. android:text="-1"></TextView>
  9. <TextViewandroid:layout_height="wrap_content"android:id="@+id/tv2"
  10. android:textSize="24sp"android:textStyle="bold"android:textColor="#00FF00"
  11. android:layout_width="fill_parent"android:text="-2"></TextView>
  12. </LinearLayout>

2.在res下创建xml目录,再在xml目录里面创建AppWidget信息xml文件:
2.1新建xml文件时,type选择AppWidget Provider。

Android AppWidget实例验证_第1张图片

2.2填充属性:
Android AppWidget实例验证_第2张图片
宽高的计算公式为:占用屏幕格数*74-2
Update period millis:设置为0,手动刷新。根据实验,设置不为0时,至少在2.2上系统根本不按照设置的值刷新,还是自己控制刷新时机好了。
Initial layout:就添控件要使用的布局。
Configure暂时不用,留空。
3.创建AppWidgetDemo类:
Android AppWidget实例验证_第3张图片
重载AppWidgetProvider中的所有函数,每个函数里面增加输出语句,以查看调用顺序。
Java代码
收藏代码
  1. publicclassAppWidgetDemoextendsAppWidgetProvider{
  2. @Override
  3. publicvoidonDeleted(Contextcontext,int[]appWidgetIds){
  4. //TODOAuto-generatedmethodstub
  5. super.onDeleted(context,appWidgetIds);
  6. Log.e("AppWidgetDemo","onDeleted");
  7. }
  8. @Override
  9. publicvoidonDisabled(Contextcontext){
  10. //TODOAuto-generatedmethodstub
  11. super.onDisabled(context);
  12. Log.e("AppWidgetDemo","onDisabled");
  13. }
  14. @Override
  15. publicvoidonEnabled(Contextcontext){
  16. //TODOAuto-generatedmethodstub
  17. super.onEnabled(context);
  18. Log.e("AppWidgetDemo","onEnabled");
  19. }
  20. @Override
  21. publicvoidonReceive(Contextcontext,Intentintent){
  22. //TODOAuto-generatedmethodstub
  23. super.onReceive(context,intent);
  24. Log.e("AppWidgetDemo","onReceive,Action:"+intent.getAction());
  25. }
  26. @Override
  27. publicvoidonUpdate(Contextcontext,AppWidgetManagerappWidgetManager,int[]appWidgetIds){
  28. //TODOAuto-generatedmethodstub
  29. super.onUpdate(context,appWidgetManager,appWidgetIds);
  30. Log.e("AppWidgetDemo","onUpdate,Count:"+appWidgetIds.length);
  31. }
  32. }

4.在AndroidManifest.xml文件中声明此Widget:
添加一个Receiver,其name为AppWidgetDemo类的类名。
Xml代码
收藏代码
  1. <receiverandroid:name="AppWidgetDemo"></receiver>

为此Receiver添加Intent filter,接收系统发出android.appwidget.action.APPWIDGET_UPDATE的Intent。
Xml代码
收藏代码
  1. <receiverandroid:name="AppWidgetDemo">
  2. <intent-filter>
  3. <actionandroid:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
  4. </intent-filter>
  5. </receiver>

此外还要为此receiver添加meta-data信息,以告知系统相关的AppWidgetProvider信息:
Xml代码
收藏代码
  1. <meta-dataandroid:name="android.appwidget.provider"
  2. 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代码
收藏代码
  1. RemoteViewsappWidgetView=newRemoteViews(context.getPackageName(),R.layout.widget_layout_demo);
  2. appWidgetView.setTextViewText(R.id.tv1,String.valueOf(mCount));
  3. appWidgetView.setTextViewText(R.id.tv2,String.valueOf(mCount));
  4. appWidgetManager.updateAppWidget(appWidgetIds,appWidgetView);

先获取一个RemoteViews,也就是AppWidget的布局所对应的View;
使用指定的Id更新要更新的控件;
更新整个RemoteViews,此时就可以更新AppWidget内容了。

但是这样的代码还有一个问题,当向桌面依次添加多个控件时会出现下面这样的效果:

即更新时没有同时更新所有的AppWidget,这是因为onUpdate中传进来的数组中只包含了1个id,如果想同时更新多个,那么可以把更新语句更换为:
Java代码
收藏代码
  1. appWidgetManager.updateAppWidget(newComponentName(context,AppWidgetDemo.class),appWidgetView);

通过组件名可以把所有的名字符合的AppWidget都更新。

6.2在onReceive()中更新
6.2.1自定义Action通知刷新:
在AndroidManifest.xml中定义的receiver的intent-filter增加一个自定义的Action:
com.demo.appwidget.refresh
Xml代码
收藏代码
  1. <receiverandroid:name="AppWidgetDemo">
  2. <intent-filter>
  3. <actionandroid:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
  4. <actionandroid:name="com.demo.appwidget.refresh"></action>
  5. </intent-filter>
  6. <meta-dataandroid:name="android.appwidget.provider"
  7. android:resource="@xml/widget_info_demo"></meta-data>
  8. lt;/receiver>

包含此Action的Intent可以在后台服务或者Activity中发出,然后会被此Receiver接收,进而触发onReceive。
本例中采用按钮按下时广播intent:
Java代码
收藏代码
  1. btnSend.setOnClickListener(newOnClickListener(){
  2. publicvoidonClick(Viewv){
  3. Intentintent=newIntent();
  4. intent.setAction("com.demo.appwidget.refresh");
  5. intent.putExtra("value",teInput.getText().toString());
  6. SendMsgActivity.this.sendBroadcast(intent);
  7. }
  8. });

在接收端:
Java代码
收藏代码
  1. if(intent.getAction().equals("com.demo.appwidget.refresh")){
  2. Stringvalue=intent.getStringExtra("value");
  3. RemoteViewsappWidgetView=newRemoteViews(context.getPackageName(),R.layout.widget_layout_demo);
  4. appWidgetView.setTextViewText(R.id.tv1,value);
  5. appWidgetView.setTextViewText(R.id.tv2,value);
  6. AppWidgetManager.getInstance(context).updateAppWidget(newComponentName(context,AppWidgetDemo.class),appWidgetView);
  7. }

判断是否是感兴趣的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代码
收藏代码
  1. btnRefresh.setOnClickListener(newOnClickListener(){
  2. publicvoidonClick(Viewv){
  3. Stringvalue=teInput.getText().toString();
  4. RemoteViewsappWidgetView=newRemoteViews(SendMsgActivity.this.getPackageName(),R.layout.widget_layout_demo);
  5. appWidgetView.setTextViewText(R.id.tv1,value);
  6. appWidgetView.setTextViewText(R.id.tv2,value);
  7. AppWidgetManager.getInstance(SendMsgActivity.this)
  8. .updateAppWidget(newComponentName(SendMsgActivity.this,AppWidgetDemo.class),appWidgetView);
  9. }
  10. });

此段代码可直接刷新AppWidget的内容,不会触发其onUpdate()。
7.响应点击事件
因为onUpdate是每个AppWidget被放置到桌面上时都会被呼叫的函数,所以在此函数中完成事件的关联:
Java代码
收藏代码
  1. Intentintent=newIntent(context,SendMsgActivity.class);
  2. PendingIntentpendingIntent=PendingIntent.getActivity(context,0,intent,0);
  3. appWidgetView.setOnClickPendingIntent(R.id.tv1,pendingIntent);

另外要注意此段代码必须要在appWidgetManager.updateAppWidget()之前,否则是不会生效的。
运行后可以点击AppWidget的第一个控件,就能呼叫指定的Activity了。
8.Config Activity
这个可直接参考SDK文档中的Dev Guide-->App Widgets了。

你可能感兴趣的:(appwidget)