(转)Android appWidget实现

2012-12-17  白云 

AppWidget是在android客户端开发中十分常用,就是用户在桌面上见到的小窗口,利用这个小窗口可以给用户提供一些快捷操作,如mp3的控制小窗口,时钟的桌面窗口等,通过在桌面中长按,在弹出的对话框中选择Widget部件来进行创建,长按部件后将部件拖动到垃圾箱进行删除。本文主要介绍如何创建一个appWidget,以及appWidget如何与客户端程序交互,对于android基础略有提及,不做过多介绍,不明白可留言,文中sample部分参考《anroid开发视屏教程》
    先来看下项目结构,以便读者有个整体的把握

下面详细介绍各个文件的作用:
    首先在res文件夹下新建一个名为xml的文件夹,在此文件夹中新建一个xml文件,这里命名为example_appwidget_info.xml(可随意命名),该文件内容如下:

此文件描述appWidget的参数信息,从上到下分别为高度、宽度、刷新时间间隔、布局文件,此处布局文件引用了layout文件夹下的example_appwidget.xml,此文件用来描述appWidget中内容的布局,下面来看看这个文件的内容

 此文件的布局同activity的布局,不做过多解释,这里只简单定义了一个TextView,内容为“firstWidgetText”
 
     定义完了appWidget,接下来新建一个类继承AppWidgetProvider,用来处理appWidget中控件产生的事件,这里将这个类命名为ExampleAppWidgetProvider

  这里复写了4个父类方法,分别为onUpdate(到达指定的更新时间或者当用户向桌面添加AppWidget时被调用)、onEnabled(AppWidget的实例第一次被创建时调用)、onDisabled(最后一个appWidget被删除时调用)、onDeleted(删除一个AppWidget时调用)。4个方法都只简单输出各自方法名用来判断调用情况
 
   其实这个类是个receiver,何以见得?从AndroidManifest.xml可以找到答案,下面就来看看这个文件

 看红框部分,注册了name为ExampleAppWidgetProvider的receiver,而ExampleAppWidgetProvider就是上文中提到类的类名, intent-filter中的action是系统自带的用于更新所有appwidget的广播动作, meta-data标签是一个描述我们创建appwidget的元数据,android:name="android.appwidget.provider"是固定的,android:resource="@xml/example_appwidget_info"用来指定appWidget配置文件的位置,即上文中介绍的example_appwidget_info.xml,用来初始化appWidget
   到此,程序已可以在桌面上添加小窗口,读者可尝试运行添加AppWidget,看看receiver的方法的调用的情况,截图如下

 前面的内容介绍了如何创建一个appWidget,但目前appWidget没有任何的交互功能,下面介绍appWidget如何与程序交互。首先要考虑一个问题,appWidget的进程和应用的进程是两个不同的进程,能通过引用控件来获得实例吗,当然是不行的。这时候需要一个对象叫做RemoteViews,能帮助我们获得不在同一进程中的对象,下面的代码演示了RemoteView的使用

实例化RemoteView时的参数还是容易理解的,一个是layout resource的包名,另一个是layout resource,难理解的是监听语句中的pendingIntent,笔者也找了一些资料,其中一种说法是, “PendingIntent可以看成是一个特殊的Intent,如果我们把Intent看成一封信,那么PendingIntent就是一封被信封包裹起来的信。这封信在remoteViews.setOnClickPendingIntent()中被“邮寄”到了appwidget, 当appwidget中的按钮单击时他知道将这封信打开,并执行里面的内容。这样就避免了直接从appwidget中执行本地代码”。 笔者的理解正好相反,不是将 PendingIntent  “邮寄”到了appwidget ,而是在应用的进程里取到appWidget中所有控件的实例remoteViews, AppWidget只是把一个进程的控件嵌入到别外一个进程的窗口里的一种方法。View在另 外一个进程里显示,但事件的处理方法还是在原来的进程里。读者可以自行理解这部分的内容,有什么不同的意见可以留言说明理由,但可以肯定的是 pendingIntent是用来当点击appWidget控件时出发的Intent,下面来看看PendingIntent的定义:

介绍了以上的知识点以后,来看看ExampleAppWidgetProvider的onUpdate方法的整体实现,应该不难理解

   创建一个Intent对象,从contex跳转到TargetActivity,,TargetActivity为最简单的activity(创建时生成),用getActivity创建一个PendingIntent,然后初始化一个RemoteView对象,调用setOnclickClientPending方法,当点击R.id.widgetButtonId时执行pendingIntent,然后更新appWidget.这里引用的 R.id.widgetButtonId 定义在example.xml中

  下面来看一下执行的效果
 
     添加appWidget后:


   点击button后跳转到TargetActivity
 

 上文中实现了appWidget的交互,但只是实现了Activity的跳转,下面实现用appWidget接收一个广播
 
     首先修改上文中提到过的onUpdate方法,如图

 创建一个Intent对象,设置action为UPDATE_ACTION,UPDATE_ACTION为ExampleAppWidgetProvider类中定义的静态常量(private static final String UPDATE_ACTION="mars.appwidget03.UPDATE_APP_WIDGET";),调用getBroadcast初始化pendingIntent(),这是和上文实现有区别的。其余都和上文中的一样。这样,当点击appWidget中的button时,将发送一个广播,而要接收这个广播,就需要在AndroidManifest.xml中添加intent-filter,action和intent的action同

通过过滤器后,会在AppWidgetProvider类中的onReceive方法接收到广播,因为ExampleAppWidgetProvider类继承AppWidgetProvider,所以在此类中复写onReceive方法


先从参数intent中获取action,然后判断action是否是UPDATE_ACTION,为什么要做判断?因为onReceive接收到的并不 仅仅只有这个action,还有其他应用发送的broadcast,针对不同的action会有不同的处理。因为appWidget运行的进程和应用的进 程不在同一个进程中,所以这里仍然需要创建RemoteViews对象,然后将image从系统自带的图片换成自己想要的(这里对于imageView的 布局在example_appwidget.xml中,如下图),将文本换成"test",更新appwidget.

 最后来看下执行的效果
     添加appWidget后:

  点击button后:

  这里因为笔者引用的图片很大,所以把下面的button挤掉了,读者可忽略此问题。

---------------------------------------------------------------------------------

转自:http://www.taobaotest.com/blogs/2296


419°/4199 人阅读/0 条评论 发表评论

登录 后发表评论