Android开发5:应用程序窗口小部件App Widgets的实现
前言
本次主要是实现一个Android应用,实现静态广播、动态广播两种改变 widget内容的方法,即在上篇博文中实验的基础上进行修改,所以此次实验的重点是AppWidget小部件的实现啦~
首先,我们简单说一下Widget是一个啥玩意~
应用程序窗口小部件(Widget)是微小的应用程序视图,可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。你可以通过一个App Widget provider来发布一个Widget。可以容纳其它App Widget的应用程序组件被称为App Widget宿主。
Widget是在桌面上的一块显示信息的东西,也通过单击Widget跳转到一个程序里面。而系统自带的程序,典型的Widget是music,这个Android内置的音乐播放小程序。这个是典型的Widget+app应用。就是一个程序既可以通过Widget启动,也可以通过App启动。Widget就是一个AppWidgetProvider+一个UI界面显示(预先绑定了好多Intent),界面上的信息可以通过程序控制而改变,单击Widget,上的控件只能激发发送一个Intent,或发出一个Service的启动通知。而AppWidgetProvider可以拦截这个Intent,而进行相应的处理(比如显示新的信息)。
基础知识
为了创建一个App Widget,你需要下面这些:
AppWidgetProviderInfo 对象
描述一个App Widget元数据,比如App Widget的布局,更新频率,以及AppWidgetProvider 类。这应该在XML里定义。
AppWidgetProvider 类的实现
定义基本方法以允许你编程来和App Widget连接,这基于广播事件。通过它,当这个App Widget被更新,启用,禁用和删除的时候,你都将接收到广播通知。
视图布局
为这个App Widget定义初始布局,在XML中。
另外,你可以实现一个App Widget配置活动。这是一个可选的活动Activity,当用户添加App Widget时加载并允许他在创建时来修改App Widget的设置。
widget 的添加:长按菜单键,点击 widgets 选项。找到对应的 widget 将其拖入桌面。对 于不同的 API 版本显示会稍有不同。
典型的 Android Widget 有三个主要组件,一个边框、一个框架和图形控件以及其他元素。 在 Android Studio 中创建 Widget 类后,会直接生成相关文件。
首先,在应用程序AndroidManifest.xml文件中声明AppWidgetProvider 类,比如:
<receiver Android:name="ExampleAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
</receiver>
<receiver>元素需要android:name属性,它指定了App Widget使用的AppWidgetProvider 。
<intent-filter> 元素必须包括一个含有android:name属性的<action>元素。该元素指定AppWidgetProvider接受ACTION_APPWIDGET_UPDATE 广播。这是唯一你必须显式声明的广播。当需要的时候,AppWidgetManager 会自动发送所有其他App Widget广播给AppWidgetProvider。
<meta-data> 元素指定了AppWidgetProviderInfo 资源并需要以下属性:
- android:name – 指定元数据名称。
- android:resource – 指定AppWidgetProviderInfo 资源路径。
1. Widget 布局文件 widget_demo.xml,布局中有一个 ImageView,一个 TextView。 要求:文字颜色为红色,大小为 20dp,整体背景为透明。最后效果如下:
2.增加AppWidgetProviderInfo元数据
AppWidgetProviderInfo定义一个App Widget的基本特性,比如最小布局尺寸,初始布局资源,刷新频率,以及(可选的)创建时加载的一个配置活动。使用单独的一个<appwidget-provider>元素在XML资源里定义AppWidgetProviderInfo 对象并保存到项目的res/xml/目录下。
Widget 内容提供者文件 widget_demo_info.xml,编辑该文件,设置其大小属性和布 局,如下图:
其中,minWidth 为最小宽度,minHeight 为最小高度,initialLayout 为初始布局。
3. 修改 WidgetDemo.java 代码,重写 onUpdate 方法,为 Widget 添加事件,使得能够返 回主页面。
这里需要使用到一种用户程序访问主屏幕和修改特定区域内容的方法:RemoteView 架 构 。RemoteView 架构允许用户程序更新主屏幕的 View,点击 Widget 激活点击事件,Android 会将其转发给用户程序,由 AppWidgetProviders 类处理,使得用户程序可更新主 屏幕 Widget。
pendingIntent是一种特殊的 Intent。主要的区别在于 Intent 的执行立刻的,而 pendingIntent 的执行不是立刻的。本次使用方法类的静态方法为 getActivity(Context, int, Intent, int),对应 Intent 的跳转到一个 activity 组件的操作。
使用AppWidgetProvider类
你必须通过在清单文件中使用<receiver>元素来声明你的AppWidgetProvider 类实现为一个广播接收器(参见上面的Declaring an App Widget in the Manifest)。
AppWidgetProvider 类扩展BroadcastReceiver 为一个简便类来处理App Widget广播。AppWidgetProvider只接收和这个App Widget相关的事件广播,比如这个App Widget被更新,删除,启用,以及禁用。当这些广播事件发生时,AppWidgetProvider 将接收到下面的方法调用:
onUpdate(Context, AppWidgetManager, int[])
这个方法调用来间隔性的更新App Widget,间隔时间用AppWidgetProviderInfo 里的updatePeriodMillis属性定义(参见添加AppWidgetProviderInfo元数据)。这个方法也会在用户添加App Widget时被调用,因此它应该执行基础的设置,比如为视图定义事件处理器并启动一个临时的服务Service,如果需要的话。但是,如果你已经声明了一个配置活动,这个方法在用户添加App Widget时将不会被调用,而只在后续更新时被调用。配置活动应该在配置完成时负责执行第一次更新。(参见下面的创建一个App Widget配置活动Creating an App Widget Configuration Activity。)
onDeleted(Context, int[])
当App Widget从宿主中删除时被调用。
onEnabled(Context)
当一个App Widget实例第一次创建时被调用。比如,如果用户添加两个你的App Widget实例,只在第一次被调用。如果你需要打开一个新的数据库或者执行其他对于所有的App Widget实例只需要发生一次的设置,那么这里是完成这个工作的好地方。
onDisabled(Context)
当你的App Widget的最后一个实例被从宿主中删除时被调用。你应该在onEnabled(Context)中做一些清理工作,比如删除一个临时的数据库。
onReceive(Context, Intent)
这个接收到每个广播时都会被调用,而且在上面的回调函数之前。你通常不需要实现这个方法,因为缺省的AppWidgetProvider 实现过滤所有App Widget 广播并恰当的调用上述方法。
注意: 在Android 1.5中, 有一个已知问题,onDeleted()方法在该调用时不被调用。为了规避这个问题,你可以像Group post中描述的那样实现onReceive() 来接收这个onDeleted()回调。
最重要的AppWidgetProvider 回调函数是onUpdated(), 因为它是在每个App Widget添加进宿主时被调用的(除非你使用一个配置活动)。如果你的App Widget 要接受任何用户交互事件,那么你需要在这个回调函数中注册事件处理器。如果你的App Widget不创建临时文件或数据库,或者执行其它需要清理的工作,那么onUpdated() 可能是你需要定义的唯一的回调函数。
4.重写 onReceive 方法
在 Widget 类中重写 onReceive 方法,这里需要使用到 RemoteView 以及 Bundle。当接 收到对应广播时进行数据处理。
实验内容
实现一个 Android 应用,实现静态广播、动态广播两种改变 widget 内容的方法。在上次实 验的基础上进行修改,所以一些关于静态动态广播的内容会简略。
具体要求:
(1)该界面为应用启动后看到的界面。
widget 初始情况如下
(2)点击静态注册按钮,跳转至如下界面。
点击表单项目。如 banana。widget 会发生对应变化。点击 Widget 上的图片可以跳转回主页面
(3)点击动态注册按钮,跳转至如下界面。 实现以下功能:
a)可以编辑广播的信息,点击 Send 按钮发送广播。
b)设置一个按钮进行广播接收器的注册与注销。
c)广播接收器若已被注册,发送出的广播信息能够及时更新桌面上 Widget 上文字内容及 更新为默认 dynamic 图片。
d)点击 Widget 上的图片可以跳转回主页面。
实验步骤
首先,在Android Studio中创建Widget类,直接生成相关文件,其中包括界面布局XML文件、widget的provider文件信息(xml)以及在项目的AndroidMenifest.xml文件中添加了一个receiver标签,需要我们添加过滤更新事件,并需要指向之前创建的Widget类。
AndroidMenifest.xml文件中,intent-filter中过滤了APPWIDGET_UPDATE事件,这个事件是由系统触发的更新事件,每个widget必须包含这个事件;meta-data标签描述的是widget的配置文件指向,该文件描述了widget的一些基本信息(其中由于需要在静态注册中实现,intent-filter中也过滤了staticreceiver):
<receiver
android:name=".MyAppWidget"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="com.example.yanglh6.myapplication4.staticreceiver" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/my_app_widget_info"/>
</receiver>
接下来根据要求编写widget的provider文件信息(xml),minWidth和minHeight是widget的最小宽度和高度,这个值是一个参考值,系统会根据实际情况进行改变,initialLayout属性指明widge的视图布局文件,updatePeriodMillis属性是widget每隔多久更新一次的时间,单位为毫秒:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/my_app_widget"
android:initialLayout="@layout/my_app_widget"
android:minHeight="55dp"
android:minWidth="200dp"
android:previewImage="@drawable/example_appwidget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen"></appwidget-provider>
接下来就是界面布局,在这个示例中需要一个ImageView控件和一个TextView控件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"> <ImageView
android:id="@+id/WidgetImage"
android:layout_width="60dp"
android:layout_height="60dp"
android:gravity="center"
android:src="@mipmap/apple"/> <TextView
android:id="@+id/WidgetName"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:textColor="@color/red"
android:textSize="20dp"
android:layout_toRightOf="@+id/WidgetImage"
android:text="Apple"
android:gravity="center"/> </RelativeLayout>
布局文件实现了一个如下图的布局:
然后在Widget中,重写onUpdate方法,为Widget添加事件,使得能够返回主页面。这里需要使用到一种用户程序访问主屏幕和修改特定区域内容的方法RemoteView架构。RemoteView架构允许用户程序更新主屏幕的View,点击 Widget激活点击事件,Android会将其转发给用户程序,由AppWidgetProviders类处理,使得用户程序可更新主屏幕Widget。
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
Intent clickInt = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, clickInt, 0);
RemoteViews view = new RemoteViews(context.getPackageName(),R.layout.my_app_widget);
view.setOnClickPendingIntent(R.id.WidgetImage, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, view);
}
接下来在Widge类中重写onReceive方法,这里需要使用到RemoteView以及Bundle。当接收到对应广播时进行数据处理(由于我们在AndroidMenifest.xml文件中注册时将APPWIDGET_UPDAT事件和staticreceiver都指向Widge类,所以在这里我们StaticReceiver类删掉,将里面对OnReceive函数重写的部分添加在Widget类中):
@Override
public void onReceive(Context context, Intent intent) {
Log.i("debug", intent.toString());
super.onReceive(context, intent);
RemoteViews view = new RemoteViews(context.getPackageName(),R.layout.my_app_widget);
Bundle bundle = intent.getExtras();
String widgetName = bundle.getString("name");
int widgetImage = bundle.getInt("ItemImage");
if (intent.getAction().equals("com.example.yanglh6.myapplication4.staticreceiver")) {
view.setTextViewText(R.id.WidgetName, widgetName);
view.setImageViewResource(R.id.WidgetImage, widgetImage);
AppWidgetManager appWidgetManager=AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(new ComponentName(context, MyAppWidget.class), view); Bitmap bitmap= BitmapFactory.decodeResource(context.getResources(),bundle.getInt("ItemImage"));
int imageId = (int) bundle.get("ItemImage");
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context);
builder.setContentTitle("静态广播")
.setContentText(bundle.getString("name"))
.setLargeIcon(bitmap)
.setSmallIcon(imageId)
.setTicker("您有一条新消息")
.setAutoCancel(true);
Intent Intent1 = new Intent(context, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, Intent1, 0);
builder.setContentIntent(pendingIntent);
Notification notify = builder.build();
notificationManager.notify(0, notify);
}
}
单独把Widget部分onReceive方法的重写列出:
public void onReceive(Context context, Intent intent) {
Log.i("debug", intent.toString());
super.onReceive(context, intent);
RemoteViews view = new RemoteViews(context.getPackageName(),R.layout.my_app_widget);
Bundle bundle = intent.getExtras();
String widgetName = bundle.getString("name");
int widgetImage = bundle.getInt("ItemImage");
if (intent.getAction().equals("com.example.yanglh6.myapplication4.staticreceiver")) {
view.setTextViewText(R.id.WidgetName, widgetName);
view.setImageViewResource(R.id.WidgetImage, widgetImage);
AppWidgetManager appWidgetManager=AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(new ComponentName(context, MyAppWidget.class), view);
}
}
对于动态注册来说,不需要在AndroidMenifest.xml添加receiver,但在DynamicActivity中进行注册:
dynamicReceiver = new DynamicReceiver();
IntentFilter dynamic_filter = new IntentFilter();
dynamic_filter.addAction("com.example.yanglh6.myapplication4.dynamicreceiver");
registerReceiver(dynamicReceiver, dynamic_filter);
所以动态注册时只能在DynamicReceiver中对Onreceive函数进行重写,完成Widget的更新(与静态注册类似):
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.example.yanglh6.myapplication4.dynamicreceiver")) {
Bundle bundle = intent.getExtras();
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), bundle.getInt("ItemImage"));
int imageId = bundle.getInt("ItemImage");
RemoteViews view = new RemoteViews(context.getPackageName(),R.layout.my_app_widget);
String widgetName = bundle.getString("name"); view.setTextViewText(R.id.WidgetName, widgetName);
view.setImageViewResource(R.id.WidgetImage, imageId);
AppWidgetManager appWidgetManager=AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(new ComponentName(context, MyAppWidget.class), view); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context);
builder.setContentTitle("动态广播")
.setContentText(widgetName)
.setLargeIcon(bitmap)
.setSmallIcon(imageId)
.setTicker("您有一条新消息")
.setAutoCancel(true);
Intent mIntent = new Intent(context, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mIntent, 0);
builder.setContentIntent(pendingIntent);
Notification notify = builder.build();
notificationManager.notify(0, notify);
}
}
完成实验~
运行截图
注意事项
自己要充分理解AndroidMenifest.xml各部分的含义以及Android的机制,在AndroidMenifest.xml的注册和指向必须清晰。
对于静态来说,在sendBroadcast(intent)实现后,在AndroidMenifest.xml找到intent注册时的receiver并指向对应的广播接收函数,在这个函数中实现各个事件;对于动态来说,由于在DynamicActivity中进行注册,在那时可以定义指向的动态广播接收类。
源码下载
源码下载点击这里~
注
1、本实验实验环境:
操作系统 Windows 10
实验软件 Android Studio 2.2.1
虚拟设备:Galaxy_Nexus
API:21
2、贴代码的时候由于插入代码框的大小问题,代码格式不太严整,望见谅~
谢谢大家~
Android开发5:应用程序窗口小部件App Widgets的实现的更多相关文章
- Android 之窗口小部件详解(三) 部分转载
原文地址:http://blog.csdn.net/iefreer/article/details/4626274. (一) 应用程序窗口小部件App Widgets 应用程序窗口小部件(Widget ...
- Android Widget(窗口小部件)
Android Widget简介 应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新.你可以通过一个App Widget Provide ...
- Android 之窗口小部件详解--App Widget
Android 之窗口小部件详解--App Widget 版本号 说明 作者 日期 1.0 添加App Widge介绍和示例 Sky Wang 2013/06/27 1 App ...
- 在android程序中加入widget(窗口小部件)并与之交互的关键代码
摘要: widget(窗口小部件)可以增强应用程序的交互性, 是很多应用中都会用到的功能,本文不求大而全,但是会给出程序与widget交互的关键代码 正文: 其实widget是嵌入(embedded) ...
- Android 之窗口小部件高级篇--App Widget 之 RemoteViews - 跨到对岸去
在之前的一篇博文( Android 之窗口小部件详解--App Widge t)中,已经介绍了App Widget的基本用法和简单实例.这篇主要讲解 App Widget 的高级内容,即通过 Remo ...
- Android 之窗口小部件高级篇--App Widget 之 RemoteViews
Android 之窗口小部件高级篇--App Widget 之 RemoteViews 在之前的一篇博文(Android 之窗口小部件详解--App Widget)中,已经介绍了App Widget的 ...
- ArcGIS API For JavaScript 开发(三)使用小部件设计页面框架
其实上一个的鹰眼.比例尺.图例等都是小部件:这篇文章主要是页面布局设计,dojo提供了非常多的小部件,从功能的角度可以分为3大类:表单小部件.布局小部件和应用小部件. 表单小部件于HTML中的表单部件 ...
- YII2 小部件(widgets)
小部件基本上在views中使用,在视图中可调用 yii\base\Widget::widget() 方法使用小部件. 该方法使用 配置 数组初始化小部件并返回小部件渲染后的结果. 例如如下代码插入一个 ...
- Android 手机卫士14--Widget窗口小部件AppWidgetProvider
1.AndroidManifest.xml根据窗体小部件广播接受者关键字android.appwidget.action.APPWIDGET_UPDATE 搜索android:resource=&qu ...
随机推荐
- TSql 巧用Alt 键
1,查看表的信息 在TSql 编辑器中,选中一个表,如图 点击Alt+F1,就可以查看表的属性定义 2,使用alt批量插入逗号 在Tsql中使用 in 子句,在(value_List)列表中,经常有很 ...
- C#设计模式系列:备忘录模式(Memento)
1.备忘录模式简介 1.1>.定义 备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可以将该对象恢复到原先保存的状态. 1.2>.使用频率 ...
- [WPF]带下拉列表的文本框
控件我已经弄好了,代码比较多,所以没办法全面介绍. 一开始我是直接继承Selector类来实现,做是做出来了,不过发现性能不太好.于是,我就想着自己来实现.毕竟我是做给自己用的,也不考虑过多的东西,也 ...
- 解密jQuery内核 DOM操作
jQuery针对DOM操作的插入的方法有大概10种 append.prepend.before.after.replaceWith appendTo.prependTo.insertBefore.in ...
- 小白学习MVC5+EF6遇到的问题一
这两天有空的时候会看看Miro大神的MVC5+EF6系列文章,推荐大家看看. 以前没有接触过,纯小白一个,今天在学习的过程中遇到了一个问题,习惯了WebForm,在运行页面之前都会右键设置为起始页,我 ...
- JSON入门指南--服务端处理JSON
平时公司使用的ASP.NET MVC3来开发Web项目,其实在ASP.NET中已经原生的支持JSON.所以基本不需要引进Newtonsoft.Json.dll.下面看在MVC4中,后台生成JSON数据 ...
- 用scikit-learn和pandas学习线性回归
对于想深入了解线性回归的童鞋,这里给出一个完整的例子,详细学完这个例子,对用scikit-learn来运行线性回归,评估模型不会有什么问题了. 1. 获取数据,定义问题 没有数据,当然没法研究机器学习 ...
- 关于z-index鲜为人知的事情
关于z-index很少有人去深入的了解它,因为它看起来一点儿也不复杂,不就是谁的数字大,谁就显示在前面吗?然而今天所摘录的这篇博文,让我震惊了.我承认我从来没有花时间去看具体的z-index相关文档, ...
- 附录B 安装MySql数据库
B.1 卸载旧的MaySql程序 第一步 查找以前是否安装有mysql 使用命令查看是否已经安装过mysql: #rpm -qa | grep -i mysql 如果没有结果,则可以进行mysq ...
- 记录一则ORA-00054,ORA-00031解决过程
生产环境:AIX 5.3 + Oracle 10.2.0.5 任务要求:普通表改造分区表,历史数据不要 这个需求很简单: pl/sql导出建表语句,依次修改成分区的建表语句,注意将索引修改成本地索 ...