下文是我翻译于 App Widgets的文章,如果有不当之处请大家指出
app widget是一种嵌入在其他应用(例如主屏幕)和并且能偶接受间接性更新的小应用,你可以自己提供app widget provider 来在用于界面上定义app widget,包含了app widget的应用程序组件叫做app widget host.
1.基础的工作
AppWidgetProviderInfo 对象
描述了app widget 所需的元数据,例如app widget 的布局,更新的频率 和实现AppWidgetProvider的类,这些都应该定义的XML文件夹中。
继承实现AppWidgetProvider对象
定义了一些基本的方法,这些方法能够让你与app widget进行交互,这些方法都是基于android的广播事件,当你的app widget调用updated(),enabled(),disabled()和delete()的方法的时候都会接收到广播。
2.在AndroidManifest.xml文件中进行相应的配置
首先需要在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这个属性,这个标签指定了你自己继承实现AppWigetProvider的类,同时你还要在<intent-fliter>这个标签中指定一个包含android:name的标签,这个标签表示将会去过滤会包含了ACTION_APPWIDGT_UPDATE的广播,这是唯一一个是你必须要明确指定接受的广播,系统的AppWidgetManager将会自动的将app widget广播发送到需要接受的AppWidgetProvider的实现类中。
<meta-data>元素指定了AppWidgetProviderInfo的信息并且必须包含下面两个信息。
android:name:这个属性指定了元数据的名称。使用android.appwidget.provider来指定数据作为AppWidgetProviderInfo的描述信息。
anndroid:resources 来指定AppWidgetProviderInfo描述信息的位置。
3.添加AppWidgetProviderInfo的元数据
AppWidgetProviderInfo定义了App Widget的基本信息,例如布局尺寸的最小值,和初始化布局文件的资源,更新app widget的频率,同时你还可以定义在创建widget的时候启动一个activity,这一项是可选的。你需要在res下新建一个xml目录,在这个目录中创建一个xml文件,这个xml文件的根元素必须要是<appwidget-provider>,例如下面的例子。
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp"
android:minHeight="72dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure" >
</appwidget-provider>
下面是对xml文件中元素的说明
minWidth和minHeight属性指定了app widget布局文件所需要最小尺寸。
在主屏幕上放置的app widget取决于定义了宽和高的网格,如果我们自定义的最小宽或者高不和屏幕网格中的格子不匹配,那么app widget的尺寸会自动的变成与当前格子最匹配的尺寸,因为屏幕的方向是可以改变的,因此网格格子尺寸大小也是可以变化的,所以建议你假设格子的宽和高的值为74像素,同时,你应该从最终的尺寸计算中减去2,因为可能会出现的像素计算问题。为了计算出最佳的宽和高,建议你使用下面的公式。
(number of cells * 74) - 2
因此,可以使用72来作为一个app widget的高,使用294来作为一个app widget的宽,宽度包含了四个格子。
updatePeriodMillis属性定义了app widget要求调用AppWidgetProvider 实现类的onUpdate方法的频率。但是并不能保证能够在准确的时间去调用这个更新方法,我们建议这个更新的周期越大越好,从省电的角度来看,一个小时不要超过一次。你也应该允许用户去配置这个调用的周期,例如有人需要每一刻钟去更新证券的信息,有的人可能一天只更新四次。注意:如果到了更新的时候,设备正处于休眠期,那么设备将会被唤醒去完成这次更新。如果你一个小时以内更新的频率低于一次,那么这将不会对你的电池造成什么明显的影响,如果你更新的十分频繁,或者不需要在设备休眠的时候更新,那么建议你使用闹钟来完成这个功能,在闹钟中你可以设定是否需要唤醒设备。你可以设定闹钟的类型为ELAPSED_REALTIME或者RTC,这将只在设备处于唤醒状态进行更新。那么在这种情况下,你可以设定updatePeriodMillis为0
initiaLayou属性指定了app widget的布局文件。
configure属性指定了当用户创建一个app widget的时候需要启动的activity,这个属性允许用户去设定app widget的属性.。这一项是可选的。
4.创建App Widget的布局文件
必须定义一个在res/layout中定义一个布局文件,来作为app widget的初始化布局文件。你可以使用下面列表中出现的View对象来作为布局文件。
如果你属性创建布局文件,那么创建一个app widget的布局文件同样是一个很简单的事情,但是需要注意的事情是app widget的布局是基于RemoteViews的,这些并不是支持所有的布局文件,只有下面三种是被支持的:FrameLayout,LinearLayout和RelativeLayout,同时下面这七种Widget对象也是被支持的:AnalogClock,Button,Chronometer,ImageButton,ImageView,ProgressBar和TextView。
5.使用AppWidgetProvider class
AppWidgetProvider 类继承了BroadcastReceiver,这样方便于去处理App Widget广播,AppWidgetProvider类只处理与App Widget相关的广播,例如当app widget 发生以下的事件updated,deleted,endabled和disabled。当这些广播事件发生以后,AppWidgetProvider会调用这些方法
onUpdate(Context context,AppWidgetManager,int [] widgetids) 这个方法会在updatePeriodMillis属性定义的时间周期类调用,当用户添加新的App Widget的时候,这个方法也会被调用。因此在这个方法里面需要执行一些必要的设置,例如设定一个视图的事件处理器或者启动一个临时的服务。但是,如果你为app widget添加了启动的activity,那么在用户添加app widget的时候,这个方法不会被执行,但是在随后的更新中,这个方法会被执行。在完成配置任务的activity完成配置的时候要执行一次更新。
onDeleted(Context context,int [] widgetsid)每当App Widget被删除的时候调用
onEnabled(Context context)这个方法只会在第一次创建app widget的时候被调用,例如,当用户创建了两个app widget,这个方法只会在创建第一个app widget的时候被调用,如果你需要使用数据库或者其他只需要只执行一次的设定,那么你可以将它们放在这个方法里面调用。当最后一个app widget实例被删除的时候,这个方法会被调用。在这里你可以完成一些必要的清理工作。
onReceive(Context context,Intent intent)这个方法在收到关于app widget的广播之后并且在上面所有的方法调用之前被调用,实际上你并不需要自己去实现这个方法,因为在AppWidgetProvider提供的默认实现中,会根据系统的广播来在上面的方法选择一个调用。
在AppWidgetProvider所有的方法中最重要的是Onupdate方法,因为每次添加一个app widget的都会被调用,除非你为你的app widget配置了一个activity.如果你的app widget需要处理与用户的交互事件,那么你也许需要在这个方法中定义一个事件处理的handler,如果你的app widget不需要创建临时性的文件或者其他需要在最后处理的任务,那么你或许仅仅需要在onupdate中来定义自己业务处理逻辑,其他的方法空实现即可。例如,当你的app widget中包含一个button,当点击button的时候,需要启动一个activity,那么下面的示例可以供你参考。
public class ExampleAppWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current App Widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
这个AppWidgetProvider实现类仅仅定义了onupdate方法,在这个方法中我们定义了一个可以启动activity的PendingIntent,我们点击了注册了setOnClickPendingIntent()方法的button时,会启动我们定义的activity.需要注意的是,在这个方法里面包含了对appwidgetids数组的遍历,这个数组是当我们创建app widget的时候AppWidgetProvider为我们创建的。
此外,当用户创建了不止一个app widget的时候,这些app widget会全部同时的更新。但是针对更新的频率我们只能定义一个updatePeriodMillis属性。例如当我们设定更新周期为两个小时一次,我们创建两个app widget之间相隔了一个小时,那么第二个被添加的app widget也会在第一个app widget 被添加两个小时之后更新。第二个的更新周期会被忽略。
注意:因为AppWidgetProvider是继承与BroadcastReceiver,你的进程在这些方法返回之后并不能保证会继续执行,如果你的进程需要进行耗时的操作,那么请考虑一下在onupdate方法中启动一个service,在这个service中,你可以考虑继续进行你需要完成的工作而不需要担心ANR
6.接收到的App Widget 广播意图
AppWidgetProvider是一个方便用来处理接收到的AppWidget 广播的类。如果你需要直接接受到App Widget的广播,那么你可以实现你自己的BroadcastReceiver或者重写onReceiver方法。下面这四种意图是你需要关心的。
ACTION_APPWIDGET_UPDATE ACTION_APPWIDGET_DELETED ACTION_APPWIDGET_ENABLED ACTION_APPWIDGET_DISABLED
7.为app widget 添加启动的activity
如果你想在用户创建一个app widget的时候可以配置app widget的基本信息,你可以为app widget配置一个activity,用户可以通过这个activity来配置widget 的更新频率,大小,颜色或者其他功能性的设置。
这个配置性的Activity同样需要在AndroidManifest.xml文件中进行声明,但是这个activity需要对android.appwidget.action.APPWIDGET_CONFIGURE 进行过滤。如下面的示例
<activity android:name=".ExampleAppWidgetConfigure">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
同样需要在AppWidgetProviderInfo xml文件中定义这个activity的信息。示例如下:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
...
android:configure="com.example.android.ExampleAppWidgetConfigure"
... >
</appwidget-provider>
类的名称一定是全限定名称,因为你可能引用了其他包的类。这些就是启动activity所需要的。现在你需要的就是一个继承了Activity的类,但是你还需要注意下面两点。
①这个activity需要返回一个结果,这个结果中包含了intent传递过来的App Widget ID
②当app widget被创建的时候 onupdate方法不会被执行,因为在启动activity的时候,系统不会发出 ACTION_APPWIDGET_UPDATE广播,那么当加载activity之后,activity需要主动去要求一次更新app widget。否则出了第一次会被跳过意外,后面的更新会被执行。
下面给出了配置activity实现。
8.通过activity更新app widget
如果创建app widget的时候使用了activity,那么在activity配置结束的时候应该主动要求更新一次app widget。可以通过AppWidgetManager来完成一次更新的请求。
下面是要求更新app widget的步骤。
①首先通过启动Activity的intent 获取App Widget id
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
}
②完成用户配置的设置。
③当配置完成以后,通过调用AppWidgetManager的getIntance(Context context)来得到AppWidgetManager的实例。
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
④通过RemoteViews并且调用AppWidgetManager实例的updateAppWidget方法来完成更新。
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
⑤最后创建一个返回的Intent
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
上面的翻译来自于个人的理解,部分语言实在是不知道怎么翻译,希望大家多多指点。