一.什么是AppWidget
Android帮助文档中将所有的控件都叫做Widget,实际上我们平常说的Widget都是泛指AppWidget,即在手机桌面上放置的控件,比如时钟,日历等。具体定义如下:
应用程序窗口小部件(Widget)是微小的应用程序视图,可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。
二.操作
通过在HomeScreen中长按,在弹出的对话框中选择Widget部件来进行创建,长按部件后并拖动到垃圾箱里进行删除。同一个Widget部件可以同时创建多个。
三.基本概念
1.AppWidgetProviderInfo对象
这个对象为AppWidget提供元数据,包括布局、更新频率等信息,这个对象定义在xml文件中,不需要自己生成,时系统自己生成的。
2.AppWidgetProvider
这个类定义了AppWidget的基本生命周期函数,具体如下:
onReceive(Context, Intent) 接收广播事件
onUpdate(Context , AppWidgetManager, int[] appWidgetIds) 到达指定的更新时间或用户向桌面添加widget时候调用
onEnabled(Context) 当AppWidget实例第一次被创建时调用
onDeleted(Context, int[] appWidgetIds) 当AppWidget被删除时调用
onDisabled(Context) 当最后一个AppWidget被删除时调用
四.创建AppWidget的基本步骤
1.定义AppWidgetProviderInfo:在res中新建文件夹xml,在其中定义AppWidget_ProviderInfo.xml文件,主要设置的参数如下:
minWidth: 定义Wdiget组件的宽度
minHeight: 定义Wdiget组件的高度
updatePeriodMillis: 更新的时间周期
initialLayout: Widget的布局文件
代码示例:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp"
android:minHeight="32dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/widget_provider" >
</appwidget-provider>
2.为AppWidget指定布局和样式,即此AppWidget在手机桌面上显示的样式,其实就是一个布局xml文件。需要注意的是使用的组件必须是RemoteViews所支持的,目前原生API中支持的组件如下:
FrameLayout
LinearLayout
RelativeLayout
AnalogClock
Button
Chronmeter
ImageButton
ImageView
ProgressBar
TextView
如果使用了除此之外的组件,则在Widget创建时会导致android.view.InflateExceptionn异常。这就导致有一些功能或样式无法实现,如很基本的list或文本编辑框都是无法直接实现的。
3.实现AppWidgetProvider。
覆写其中的回调函数,后边会详细介绍。
4.在AndroidManifest.xml中声明(用receiver标签声明)
<receiver android:name="MyWidgetProvider"> //名字就是自己写的AppWidgetProvider
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action> //接收什么样的广播,默认的action有4
//种,对应除onReceive外的4种生命周期函数,也可以自己写。
</intent-filter>
<meta-data android:resource="@xml/AppWidget_ProviderInfo" //在xml文件夹下建立的AppWidgetProviderInfo文件
android:name="android.appwidget.provider"></meta-data> //name固定这么写
</receiver>
五.在AppWidget中使用控件
1.一个重要的概念
AppWidget和其原本的App并不在同一个进程中,而是运行在HomeScreen进程当中,因此,在控件监听器的绑定,更新等操作都会与以前基本的方法有所不同。
2.PendingIntent
(1)概念
PendingIntent是一个特殊的Intent,实际上它像一个邮包,其中包裹着真正的Intent,当邮包未打开时,Intent是被“挂起”的,所以并不执行,只有当邮包拆开时才会执行。
(2)执行过程
进程A创建PendingIntent,发送给进程B,进程B此事并不执行,直到用户出发某一事件时,包裹被拆开,里面的Intent真正执行。所以Intent什么时候被执行是不知道的,由用户出发事件来决定,整个过程类似回调函数的原理。
(3)创建
有三种方法创建PendingIntent:
getActivity(Context context,int requestCode,Intent intent,int flags) 这时的PendingIntent作用是启动一个新的Activity
getBroadcast(Context context,int requestCode,Intent intent,int flags) 这时的PendingIntent作用是发送一个广播
getService(Context context,int requestCode,Intent intent,int flags) 这时的PendingIntent作用是启动一个Service
3.RemoteViews
(1)RemoteViews表示了一系列view对象,即AppWidget所有的控件。
(2)RemoteViews所标示的对象都运行在另外的进程中。
(3)应用
RemoteViews.setOnClickPendingIntent(R.id.widgetButtonId,pendingIntent); //第一个参数就是需要绑定的控件,
//第二个参数是点击后触发执行的PendingIntent。
4.为控件绑定监听器的实现(点击按钮,开启新的Activity)
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) { //appWidgetManager用来管理appWidget,而appWidgetIds[]指的是,每当新建一个appWidget,
//系统就会给新建的appWidget一个ID,而这个数组中储存的就是这些ID。
// TODO Auto-generated method stub
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) {
//创建Intent 对象
Intent intent=new Intent(context,TargetActivity,.class);
//创建PendingIntent 对象,即把intent装进pendingIntent中
PendingIntent pendingIntent=new PendingIntent.getActivity(context,0,intent,0);
//通过布局文件得到RemoteViews 对象
RemoteViews remoteViews=new RemoteViews (context.getPackageName,R.layout.my_AppWidget);
//绑定监听事件,即当widgetButtonId控件有onClick事件时,会执行pendingIntent(就是之前所说的拆邮包)
remoteViews.setOnClickPendingIntent(R.id.widgetButtonId,pendingIntent);
//使用appWidgetManager更新AppWidge。第一个参数指明更新的是哪一个appWidgetId,第二个参数指明了更新的远程Views。
appWidgetManager.updateAppWidget(appWidgetIds[i],remoteViews);
}
super.onUpdate(context,appWidgetManager,appWidgetIds);
}
六.接收AppWidget中的广播(AppWidget与AppWidgetProvider的互动)
1.机制
其实,AppWidgetProvider就是一个广播接收器。在AppWidgetProvider中,pendingIntent传来后由onReceive方法接收,之后检查intent对象中的action,由action来决定分发给哪一个函数。Android默认设置了四种action,对应了除onReceive外剩下的四种方法。因此,我们可以理解为AppWidgetProvider中的方法其实又依赖于onReceive。
2.实现步骤
(1)在AndroidManifest.xml中为AppWidgetProvider注册intent-filter,如前介绍。
(2)使用getBroadcast方法创建一个PendingIntent对象。
(2)为AppWidget中的控件注册处理器(即绑定监听器)。
(4)在onReceive方法中接收广播消息。
3.代码示例
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) {
Intent intent=new Intent();
intent.setAction(UPDATE_ACTION);
//创建PendingIntent 对象,即把intent装进pendingIntent中,当pendingIntent执行的时候会发送一个广播
PendingIntent pendingIntent=new PendingIntent.getBroadcast(context,0,intent,0);
RemoteViews remoteViews=new RemoteViews (context.getPackageName,R.layout.my_AppWidget);
remoteViews.setOnClickPendingIntent(R.id.widgetButtonId,pendingIntent); 。
appWidgetManager.updateAppWidget(appWidgetIds[i],remoteViews);
}
super.onUpdate(context,appWidgetManager,appWidgetIds);
}
之后覆写onReceive方法,实现接收广播后需要执行的操作即可(如“七”将要介绍)。当点击button时,会触发pendingIntent的执行,这时候会发送一个广播,而onReceive会接收广播并执行相应操作。
七.更新AppWidget中控件的状态
1.AppWidget中的更新操作是使用RemoteViews的一系列方法进行的。(比如更换图片:RemoteViews.setImageViewResource等)
2.更新后应记得使用AppWidgetManager通知AppWidget进行更新。
3.代码示例
说明:这里实现的即是点击某按钮,发送一个广播,在onReceive方法里实现更新操作,即需要覆写onReceive方法。
@Override
public void onReceive(Context context,Intent intent) {
String action=intent.getAction();
if(UPDATE_ACTION.equals(action)){
RemoteViews remoteViews=new RemoteViews (context.getPackageName,R.layout.my_AppWidget);
//将id为imageId的控件中的图片更新为image1
remoteViews.setImageViewResource(R.id.imageId,R.drawable.image1);
AppWidgetManager appWidgetManager=AppWidgetManager.getInstance(context);
ComponentName componentName=new ComponentName (context,MyAppWidgetProvider.class);
//通知AppWidgetProvider更新
appWidgetManager.updateAppWidget(componentName,remoteViews);
} else{
super.onReceive(context,intent);
}
}