android 转 比较好的widget讲解

1.创建AppWidget布局,包含两个TextView用来显示内容: 
Xml代码    收藏代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent" android:layout_height="fill_parent"  
  4.     android:orientation="vertical">  
  5.     <TextView android: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.     <TextView android: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 转 比较好的widget讲解_第1张图片

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

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

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

此外还要为此receiver添加meta-data信息,以告知系统相关的AppWidgetProvider信息: 
Xml代码    收藏代码
  1. <meta-data android: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. RemoteViews appWidgetView = new RemoteViews(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(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代码    收藏代码
  1.  <receiver android:name="AppWidgetDemo">  
  2. <intent-filter>  
  3.     <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>  
  4.     <action android:name="com.demo.appwidget.refresh"></action>  
  5. </intent-filter>  
  6. <meta-data android: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(new OnClickListener() {            
  2.             public void onClick(View v) {  
  3.                 Intent intent = new Intent();  
  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.             String value = intent.getStringExtra("value");            
  3.             RemoteViews appWidgetView = new RemoteViews(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(new ComponentName(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(new OnClickListener() {             
  2.     public void onClick(View v) {  
  3.         String value = teInput.getText().toString();  
  4.         RemoteViews appWidgetView = new RemoteViews(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(new ComponentName(SendMsgActivity.this, AppWidgetDemo.class), appWidgetView);  
  9.     }  
  10. });  

此段代码可直接刷新AppWidget的内容,不会触发其onUpdate()。 
7.响应点击事件  
因为onUpdate是每个AppWidget被放置到桌面上时都会被呼叫的函数,所以在此函数中完成事件的关联: 
Java代码    收藏代码
  1. Intent intent = new Intent(context, SendMsgActivity.class);  
  2. PendingIntent pendingIntent = 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了。 

你可能感兴趣的:(android 转 比较好的widget讲解)