这篇我们依然撇开播放器,讲一讲桌面Widget中最基本的功能,大家先把最基本的给弄懂了,下篇再来实现播放器的桌面Widget,可见实现一个复杂的桌面Widget是多么的困难。
先看下这篇的实现效果:
在Widget中实现一个text和两个button,当点击第一个button的时候,text中显示一个随机数。
在上一篇中《桌面widget详解(一)——基本demo构建》,我们简单介绍了怎么显示了Widget,但如何让Widget中的按钮得到交互等问题还没有涉及,这篇中,虽然是新开的布局,但有关显示的部分,我就不再细讲,大家可以参考上一篇。
一、Widget布局本来我不打算列出这部分内容,但考虑到这个布局是新的,所以还是给大家简单列一下吧。但有关xml下的<appWidget-provIDer和AndroIDManifest.xml下的注册就不再讲了。不过,为了区分,我把丑小孩的头像改成了小猫咪。其它没变化,看代码就知道了,下面列出Widget的布局:
<?xml version="1.0" enCoding="utf-8"?><linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" androID:background="#33000000" androID:orIEntation="vertical" > <TextVIEw androID:ID="@+ID/text" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:layout_marginBottom="5dip" androID:layout_margintop="30dip" androID:gravity="center_horizontal" androID:text="text" androID:textSize="14sp" /> <button androID:ID="@+ID/btn1" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:text="btn_1" /> <button androID:ID="@+ID/btn2" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:layout_margintop="5dip" androID:text="btn2" /> </linearLayout>
这个布局很简单,一个textVIEw和两个btn,没什么难度。
二、AppWidget中控件交互(Exampleappwidgetprovider.java) 1、发送广播与按钮事件绑定
因为appWidget运行的进程和我们创建的应用不在一个进程中,所以我们也就不能像平常引用控件那样来获得控件的实例。这时候,我们就要靠RemoteVIEws,直译成中文应该是远程视图; 也就是说通过这个东西我们能够获得不在同一进程中的对象,这也就为我们编写appWidget的处理事件提供了帮助。我们使用一下代码来创建一个RemoteVIEws。
RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget); // 绑定事件remoteVIEws.setonClickPendingIntent(R.ID.btn1,pendingIntent);
第一句创建了一个remoteVIEws实例,然后将其中的按钮与点击事件绑定,后面的参数中又出来了一个PendingIntent。下面看看PendingIntent又是个什么玩意。
intent英文意思是意图,pending表示即将发生或来临的事情。
PendingIntent这个类用于处理即将发生的事情。比如在通知Notification中用于跳转页面,但不是马上跳转。所以我们可以将它理解成一个封装成消息的intent的。即这个intent并不是立即start,而是像消息一样被发送出去,等接收方接到以后,再分析里面的内容。
Intent intent = new Intent();intent.setClass(context,Exampleappwidgetprovider.class);intent.setData(Uri.parse("harvic:" + R.ID.btn1)); // 设置pendingIntent的作用PendingIntent pendingIntent = PendingIntent.getbroadcast(context,intent,0);
可以看到,PendingIntent是Intent的封装,在构造PendingIntent前,也要先构造一个Intent,并可以利用Intent的属性传进去action,Extra等,同样,在接收时,对方依然是接收Intent的,而不是接收paddingIntent。这个问题,我们后面可以看到。
PendingIntent.getbroadcast(context,0);指从系统中获取一个用于可以发送broadcastReceiver广播的PendingIntent对象。
讲完这两个之后,我们看一下OnUpdate的内容:
其中:
String broadCastString = "harvic.provIDer";
@OverrIDepublic voID onUpdate(Context context,AppWidgetManager appWidgetManager,int[] appWidgetIDs) { Intent intent = new Intent(); intent.setClass(context,Exampleappwidgetprovider.class); intent.setData(Uri.parse("harvic:" + R.ID.btn1)); // 设置pendingIntent的作用 PendingIntent pendingIntent = PendingIntent.getbroadcast(context,0); RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget); remoteVIEws.setonClickPendingIntent(R.ID.btn1,pendingIntent); // 更新AppWidget appWidgetManager.updateAppWidget(appWidgetIDs,remoteVIEws);}
这里的总体步骤就是,构造一个RemoteVIEw,然后利用updateAppWidget()将构造的RemoteVIEw更新指定的Widget界面。
先看构造pendingIntent的过程:
Intent intent = new Intent();intent.setClass(context,0);
首先是构造一个广播时发送的intent,注意这个intent构造的是显示intent,直接将这个广播发送给Exampleappwidgetprovider,附带的数据通过Data传送(最后附录中会讲为什么不通过putExtra传送额外值),这里传送过去的btn1的ID值。
然后通过PendingIntent.getbroadcast();将intent封装到pendingIntent中,以待发送。
在构造了pendingIntent之后,就是将这个pendingIntent与btn1绑定,当用户点击btn1的时候,将广播发送出去。代码如下:
RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget);remoteVIEws.setonClickPendingIntent(R.ID.btn1,pendingIntent);
最后,是将这个remoteVIEw更新到所有Widget上。(因为用户对某一个apps可以创建多个Widget,要保持所有的Widget状态统一)
appWidgetManager.updateAppWidget(appWidgetIDs,remoteVIEws);
注意这里有个appWidgetIDs,这个参数是通过OnUpdate()传过来的,它是一个int数组,里面存储了用户所创建的所有Widget的ID值。更新的时候也是通过Widget的ID值,一个个更新的。
再絮叨一遍,我们这里做了两件事:
(1)、将按钮控件(R.ID.btn1)与pendingIntent绑定。当用户点击按钮时,就会把所构造的intent发送出去。
涉及代码为:
Intent intent = new Intent();intent.setClass(context,0);RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),pendingIntent);
(2)、更新所有的Widget
主要利用updateAppWidget(appWidgetIDs,remoteVIEws);将remoteVIEw根据Widget的ID值,一个个更新界面,涉及代码为:
RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget);// 更新AppWidgetappWidgetManager.updateAppWidget(appWidgetIDs,remoteVIEws);
可以看到,这里如果不算绑定按钮的话,其实什么都没有更新,因为RemoteVIEw除了绑定控件的点击事件,还可以设置textVIEw的文字,ImageVIEw的图片Resource等,我们这里都还没有涉及
2、接收广播由于我们在创建广播的Intent时,使用的显示Intent,所以我们的广播不需要注册就会发到这们这个类(Exampleappwidgetprovider.java)里面。
在接收到广播后,我们先判断Intent中是不是包含data,因为我们在发送广播时放data中塞了数据(btn1的ID),所以只要data中有数据就可以判定是用户点击btn1发来的广播。然后同样利用RemoteVIEw将textVIEw的文字改成btn click加一串随机数字,代码如下:
@OverrIDepublic voID onReceive(Context context,Intent intent) { if (intent == null) { return; } String action = intent.getAction(); if (broadCastString.equals(action)) { // 只能通过远程对象来设置appWidget中的控件状态 RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget); // 通过远程对象将按钮的文字设置为”一个随机数” Random random1 = new Random(); remoteVIEws.setTextVIEwText(R.ID.text,"btn click:" + random1.nextInt()); // 获得appWidget管理实例,用于管理appWidget以便进行更新 *** 作 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); // 相当于获得所有本程序创建的appWidget Componentname componentname = new Componentname(context,Exampleappwidgetprovider.class); // 更新appWidget appWidgetManager.updateAppWidget(componentname,remoteVIEws); } super.onReceive(context,intent);}
同样还是利用updateAppWidget()函数来更新Widget,对比OnUpdate()中的代码,这里有两点不同:
1、少了btn绑定,仅仅是更新界面,代码为:
RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget); // 通过远程对象将按钮的文字设置为”一个随机数”Random random1 = new Random();remoteVIEws.setTextVIEwText(R.ID.text,"btn click:" + random1.nextInt());
在这里,我们将R.ID.text的文字更新为btn click加一个随机数。因为每次点击按钮都会发送一个消息,所以每次点击产生的随机数是不同的,在界面上可以明显的表现出来。
2、改变了更新界面的方式
在OnUpdate中,我们更新界面是通过传过来的Widget的ID数组来更新所有Widget的。而这里是通过获取Componentname来更新的。其实这里还有另一种实现方式,即我们可以把OnUpdate中传过来的appWidgetIDs保存起来,在这里同样使用OnUpdate中的appWidgetManager.updateAppWidget(appWidgetIDs,remoteVIEws);来更新。但我更推荐这里利用Componentname的这种方式来更新,因为当我们的应用程序进程一被杀掉,当应用程序再起来的时候,使用Componentname这种更新方式的代码还是可以继续响应的,而利用保存appWidgetIDs的代码是不会响应的。
上面的例子中,我们简单实现了所谓的交互,但并没有办法识别出当前是哪个控件发出的,并根据不同的控件做出响应,先看看这部分效果:
下面对上面的代码进行补充,首先在发送时,就应该加以区别当前是哪个控件发出的intent,代码如下:
private PendingIntent getPendingIntent(Context context,int resID){ Intent intent = new Intent(); intent.setClass(context,Exampleappwidgetprovider.class); intent.setData(Uri.parse("harvic:" + resID)); PendingIntent pendingIntent = PendingIntent.getbroadcast(context,0); return pendingIntent;} @OverrIDepublic voID onUpdate(Context context,int[] appWidgetIDs) { RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget); remoteVIEws.setonClickPendingIntent(R.ID.btn1,getPendingIntent(context,R.ID.btn1)); remoteVIEws.setonClickPendingIntent(R.ID.btn2,R.ID.btn2)); // 更新AppWidget appWidgetManager.updateAppWidget(appWidgetIDs,remoteVIEws);}
先看OnUpdate中设置RemoteVIEw的代码:
RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget); remoteVIEws.setonClickPendingIntent(R.ID.btn1,R.ID.btn1)); remoteVIEws.setonClickPendingIntent(R.ID.btn2,R.ID.btn2));// 更新AppWidgetappWidgetManager.updateAppWidget(appWidgetIDs,remoteVIEws);
创建一个RemoteVIEw,然后将btn1,btn2分别进行绑定,这里我将PendingIntetn进行了封装:
private PendingIntent getPendingIntent(Context context,0); return pendingIntent;}
要设置data域的时候,把控件ID设置进去,因为我们在绑定的时候,是将同一个ID绑定在一起的,所以哪个控件点击,发送的intent中data中的ID就是哪个控件的ID,绑定ID的代码就是下面这行:
remoteVIEws.setonClickPendingIntent(R.ID.btn2,R.ID.btn2));
然后就是接收的部分:
接收时主要就是先根据传送过来的Intent,找到data中的控件ID:
Uri data = intent.getData();int resID = -1;if(data != null){ resID = Integer.parseInt(data.getSchemeSpecificPart());}
然后根据ID,定制RemoteVIEw的TextVIEw的字体内容
RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget);Random random1 = new Random(); switch (resID) {case R.ID.btn1: remoteVIEws.setTextVIEwText(R.ID.text,"btn1 click:" + random1.nextInt()); break;case R.ID.btn2: remoteVIEws.setTextVIEwText(R.ID.text,"btn2 click:" + random1.nextInt()); break;}
同样,最后是更新所有的wiget界面:
// 获得appWidget管理实例,用于管理appWidget以便进行更新 *** 作AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 相当于获得所有本程序创建的appWidgetComponentname componentname = new Componentname(context,Exampleappwidgetprovider.class);// 更新appWidgetappWidgetManager.updateAppWidget(componentname,remoteVIEws);
经过上面的讲解,总体的接收代码就是这样的:
@OverrIDepublic voID onReceive(Context context,Intent intent) { Uri data = intent.getData(); int resID = -1; if(data != null){ resID = Integer.parseInt(data.getSchemeSpecificPart()); } // 通过远程对象将按钮的文字设置为”一个随机数” RemoteVIEws remoteVIEws = new RemoteVIEws(context.getPackagename(),R.layout.example_appWidget); Random random1 = new Random(); switch (resID) { case R.ID.btn1: remoteVIEws.setTextVIEwText(R.ID.text,"btn1 click:" + random1.nextInt()); break; case R.ID.btn2: remoteVIEws.setTextVIEwText(R.ID.text,"btn2 click:" + random1.nextInt()); break; } // 获得appWidget管理实例,用于管理appWidget以便进行更新 *** 作 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); // 相当于获得所有本程序创建的appWidget Componentname componentname = new Componentname(context,Exampleappwidgetprovider.class); // 更新appWidget appWidgetManager.updateAppWidget(componentname,remoteVIEws); super.onReceive(context,intent);}
好了,到这所有的代码都讲完了。
四、附(通过匿名Intent发送广播------不推荐):在很多例子中使用的是匿名Intent来发送广播,即设定intent的Action来发送广播,这种方法我是极不推荐的,因为不能识别发送控件的ID,这主要是由于pendingIntent的原因。针对这个工程,我也写了一个匿名Intent广播的例子,在这我就不讲了,大家可以看源码。
总结以上是内存溢出为你收集整理的桌面widget详解(三)——桌面widget中的控件交互方法全部内容,希望文章能够帮你解决桌面widget详解(三)——桌面widget中的控件交互方法所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)