1.简介
App Widget
应用程序窗口小部件,微型的应用程序视图,它可以被嵌入到其它应用程序中,比如桌面,并接收周期性的更新。
2.创建一个 App Widget 的主要步骤
- 在
AndroidManifest
中声明App Widget
- 在
xml
目录定义App Widget
的初始化xml
文件 - 实现
Widget
具体布局的Layout xml
。 - 继承
AppWidgetProvider
类,实现具体的Widget
业务逻辑。
2.1 在 AndroidManifest 中声明 App Widget
<receiver android:name=".sample.MyWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/appwidget_provider"/>
</receiver>
<receiver>
的 android:name
属性声明的就是Widget
所用的AppWidgetProvider
类,并且<intent-filter>
中必须要包含APPWIDGET_UPDATE
这个<action>
,所有 Widget
的 broadcast
都是通过这个 filter
来接收的。
<meta-data>
声明了 Widget
的AppWidgetProviderInfo
对应的资源xml
的位置,用的是 xml
目录下的appwidget_provider.xml
。这里需要简单介绍下AppWidgetProviderInfo
类,该类是用来描述Widget
的 meta
信息,包括Widget
的 xml
布局文件、刷新频率、最小宽高等等,而这些信息正是通过上述xml
的<appwidget-provider>
标签来描述的。
2.2 在 xml 目录定义 App Widget 的初始化 xml 文件
前面所说的用来描述 AppWidgetProviderInfo
的 xml
定义如下:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="200dp"
android:minHeight="50dp"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/app_icon"
android:initialLayout="@layout/widget"
android:configure="com.example.android.ExampleAppWidgetConfigure"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen|keyguard"/>
minWidth
&minHeight
:定义了Widget
的最小宽高,当minWidth
和minHeight
不是桌面cell
的整数倍时,Widget
的宽高会被阔至与其最接近的cells
大小。Google 官方给出了一个大致估算minWidth
&minHeight
的公式,根据Widget
所占的cell
数量来计算宽高:70 × n − 30
,n
是所占的cell
数量。updatePeriodMillis
:定义了Widget
的刷新频率,也就是App Widget Framework
多久请求一次AppWidgetProvider
的onUpdate()
回调函数。该时间间隔并不保证精确,出于节约用户电量的考虑,Android
系统默认最小更新周期是 30 分钟,也就是说:如果您的程序需要实时更新数据,设置这个更新周期是 2 秒,那么您的程序是不会每隔 2 秒就收到更新通知的,而是要等到 30 分钟以上才可以,要想实时的更新Widget
,一般可以采用Service
和AlarmManager
对Widget
进行更新。previewImage
:当用户选择添加Widget
时的预览图片。如果该属性没有定义,则展示application
的launcher icon
,该属性是在 3.0 以后引入的。initialLayout
:Widget
的布局Layout
文件。configure
:定义了用户在添加Widget
时弹出的配置页面的Activity
,用户可以在此进行Widget
的一些配置,该Activity
是可选的,如果不需要可以不进行声明。resizeMode
:Widget
在水平和垂直方向是否可以调整大小,值可以为:horizontal
(水平方向可以调整大小),vertical
(垂直方向可以调整大小),none
(不可以调整大小),也可以horizontal|vertical
组合表示水平和垂直方向均可以调整大小。widgetCategory
:表示Widget
可以显示的位置,包括home_screen
(桌面),keyboard
(锁屏),keyboard
属性需要 5.0 或以上 Android 版本才可以。
2.3 继承 AppWidgetProvider 类
AppWidgetProvider
继承自 BroadcastReceiver
,内部逻辑非常简单,就是在onReceive()
中处理Widget
相关的广播事件
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
分发到各个回调函数中onUpdate()
, onDeleted()
,onEnabled()
,onDisabled
, onAppWidgetOptionsChanged()
。
onUpdate()
:是最重要的回调函数,根据updatePeriodMillis
定义的定期刷新操作会调用该函数,此外当用户添加Widget
时
也会调用该函数,可以在这里进行必要的初始化操作。但如果在<appwidget-provider>
中声明了android:configure
的Activity
,在用户添加Widget
时,不会调用onUpdate()
,需要由configure Activity
去负责去调用AppWidgetManager.updateAppWidget()
完成Widget
更新,后续的定时更新还是会继续调用onUpdate()
的。onDeleted()
:当Widget
被删除时调用该方法。onEnabled()
:当Widget
第一次被添加时调用,例如用户添加了两个你的Widget
,那么只有在添加第一个Widget
时该方法会被调用。所以该方法比较适合执行你所有Widgets
只需进行一次的操作。onDisabled()
:与onEnabled
恰好相反,当你的最后一个Widget
被删除时调用该方法,所以这里用来清理之前在onEnabled()
中进行的操作。onAppWidgetOptionsChanged()
:当Widget
第一次被添加或者大小发生变化时调用该方法,可以在此控制Widget
元素的显示和隐藏。
示例代码:
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);
}
}
}
2.4 创建 App Widget Configuration Activity
如果你的 Widget
需要用户配置一些选项,你可以为你的 Widget
创建 Configuration Activity
,当用户添加Widget
时会自动弹出该Activity
。Configuration Activity
和普通Activity
一样需要在Manifest
中声明,但是需要额外声明一个 intent-filter: APPWIDGET_CONFIGURE
,例如:
<activity android:name=".ExampleAppWidgetConfigure">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
同时还需要在上述的 appwidget-provider
中声明:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
...
android:configure="com.example.android.ExampleAppWidgetConfigure"
... >
</appwidget-provider>
有两点需要注意的是:
Activity
必须返回带EXTRA_APPWIDGET_ID
的result
。- 声明
Configuration Activity
后onUpdate()
在Widget
添加时不会被调用,Activity
负责调用AppWidgetManager.updateAppWidget()
完成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); }
- 执行你的
Widget
自定义配置逻辑 -
更新
App Widget
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.example_appwidget); appWidgetManager.updateAppWidget(mAppWidgetId, views);
-
设置
result Intent
,带上EXTRA_APPWIDGET_ID
,退出Activity
Intent resultValue = new Intent(); resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); setResult(RESULT_OK, resultValue); finish();