App Widgets 是指能够嵌入其他应用程序中的小组件,并且能够周期性地进行更新。
App Widgets 并不是 Android 应用程序的核心组件,但却是应用程序开发不可或缺的部分。我们可以通过 App Widgets 使我们的 UI 界面更多样化,也可以通过 App Widget Provider 发布我们自己开发的 App Widgets 组件。
一个能够用于容纳 App Widgets 组件的应用程序组件被称为 App Widgets Host(App Widgets 宿主),例如图 1 所示的音乐播放程序。
Android 7.0 中涉及部分 App Widgets 类的使用方法会在后面教程进行详细介绍,本节主要对使用 App Widget Provider 发布自己的 App Widget 组件的方法进行简单介绍。
为了创建一个自己的 App Widget,需要完成以下工作。
1) AppWidgetProviderInfo 元数据
定义在 XML 文件中的用于描述 App Widget 的元数据对象,比如 App Widget 的布局、更新频率以及相关的 AppWidgetProvider 类。
2) 实现 AppWidgetProvider 类
在 AppWidgetProvider 类中定义了一系列方法,这些方法允许开发者以编程的方式和自己的 App Widget 进行交互,这种交互基于广播事件。当 App Widget 的状态发生改变,例如更新、启用、禁用和删除的时候,你都会接收到相应的广播通知。
3) 视图布局
在 XML 文件中为 App Widget 定义初始布局。
4) 实现 App Widget 配置 Activity
这是一个可选的 Activity,当用户添加 App Widget 时该 Activity 会被启动,并允许用户在创建 App Widget 时修改相关设置。
下面进行详细介绍。
首先,在 AndroidManifest.xml 文件中对 AppWidgetProvider 类进行声明。
AppWidgetProviderInfo 用于定义 App Widget 的一系列基本特性,例如最小布局的尺寸、初始的布局资源、刷新频率以及创建时要加载的配置 Activity 等。
使用
其中:
必须在 res/layout 目录下以 XML 文件的方式为 App Widget 定义一个布局文件。
App Widget 的布局是基于 RemoteViews 对象的,而 RemoteViews 对象可以支持以下布局:
和以下的小组件类:
但是并不支持它们的派生类。
此外,RemoteView 还支持 ViewStub,该组件不可见,自身无尺寸,可用于对布局资源进行支撑。
如果没有为自定义的 Widget 定义边界,它就会自动扩展到屏幕大小。因此,我们需要为自定义的 App Widget 定义边界。
自 Android 4.0 开始,App Widget 会自动在 Widget 的边界环绕盒之间添加空隙,以便为 Widget 和其他小组件以及屏幕上的图标提供更好的排列组合方式。为实现这个行为,我们需要将应用程序中的 "targetSdkVersion" 属性设置为大于 14。
实际上,我们可以自己定义一个带有自定义边界的布局,并且使该布局在应用于早期平台版本时正常显示边界,而在 Android 4.0 以后版本的平台上不显示额外边界。定义过程如下:
1) 设置 targetSdkVersion 为大于 14 的值。
2) 创建一个布局,并为其设置 dimension 资源,其边界信息由 dimension 资源设定,代码如下:
3) 创建两个 dimension 资源,一个在 res/values/ 目录下,用于提供低于 Android 4.0 版本的系统的边界信息,另一个在 res/values-v14 下,用于提供高于 Android 4.0 版本的操作系统的边界信息。
例如,res/values/dimens.xml 定义如下:
而 res/values-v14/dimens.xml 定义如下:
首先,AppWidgetProvider 类是 BroadcastReceiver 类的子类,可以方便地处理 App Widget 发出的广播,因此,其必须被声明在清单文件中的
AppWidgetProvider 只接受和相应的 App Widget 相关的广播消息,例如这个 App Widget 被更新、被删除、被启用或者被禁用的时候。当这些广播事件发生的时候,AppWidgetProvider 会接收到以下方法的调用请求。
1) onUpdate()
每间隔一定时间该方法就会被调用。
用于对 App Widget 进行更新。间隔时间由 AppWidgetProviderInfo 元数据中的 updatePeriodMillis 属性指定。当用户添加 App Widget 时,该方法也会被调用。因此,该方法中应该执行必要的操作,例如为视图定义事件处理器或者启动一个临时的服务等。
如果你为 App Widget 定义了配置 Activity,就应该由配置 Activity 负责进行第一次更新,而 onUpdate() 方法不会在用户执行添加操作的时候被调用,而只会在后期的更新时被调用。
2) onAppWidgetOptionsChanged()
该方法在 Widget 被首次放置到应用程序中或者 Widget 的尺寸被更改时被调用。
3) onDeleted(Context, int[])
该方法在 App Widget 被从 App Widget 宿主中删除的时候被调用。
4) onEnabled(Context)
该方法在 App Widget 的第一个实例被创建时被调用。
若用户添加了两个 App Widget 的实例,则该方法只会在第一次添加时被调用。如果你需要打开数据库或者其他只需要进行一次的设置,那么将代码放在这个方法中是个不错的主意。
5) onDisabled(Context)
该方法在最后一个 App Widget 实例从 App Widget 宿主中被删除的时候调用。
在该方法中,你应该对在 onEnabled() 方法中的操作进行善后,例如删除一个临时的数据库。
6) onReceive(Context, Intent)
每当接收到一个广播,该方法都会被调用。
并且,该方法会在上述各个方法之前被调用。通常我们不需要重写该方法,因为默认的 AppWidgetProvider 类已经很好地实现了对所有广播的过滤和处理方法的调用。
可见 onUpdate() 方法是最重要的回调方法,如果你创建的 App Widget 不需要进行创建临时文件等操作,那么你可能只需要定义 onUpdate() 方法就可以了。
例如,当你创建了一个带有 Button 的 App Widget,当点击按钮时会启动一个 Activity,那么你的 AppWidgetProvider 类应该像下面这样定义:
其中,appWidgetIds 是一个存放 ID 的数组,其中的每一个 ID 值都标识一个 AppWidgetProvider 创建的 App Widget。如果该数组中存放了多个 App Widget 的 ID,那么这些 App Widget 会被同步更新。
如果你想直接用自己的类接收并处理 App Widget 的广播,那么你需要实现自己的 BroadcastReceiver,重写 onReceiver() 方法,并处理以下 4 个 Intent:
如果想让用户在添加新的 App Widget 的时候对颜色、尺寸、更新周期等属性进行配置,那么就需要创建一个配置 Activity。配置 Activity 会在 App Widget 被创建时由其宿主启动。
该配置 Activity 需要在 Manifest 文件中进行声明,通过 ACTION_APPWIDGET_CONFIGURE 活动被宿主启动,代码如下:
此外,该 Activity 还需要在 AppWidgetProviderInfo XML 中通过 android:configure 属性被声明,例如:
当为 App Widget 定义了配置 Activity 后,Widget 在被创建时不会再调用 onUpdate 方法。
使用配置 Activity 对 App Widget 进行更新
当 Widget 使用了配置 Activity 后,配置 Activity 会在用户完成设置后对 Widget 进行更新。通过配置 Activity 对 Widget 进行更新并关闭配置 Activity 的过程如下:
1) 从启动 Activity 的 Intent 中获取 App Widget 的 ID 值。
2) 执行 App Widget 配置。
3) 完成配置后,获取 AppWidgetManager 类的实例。
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
4) 通过 RomoteViews 布局对 App Widget 进行更新。
RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId,views);
5) 创建返回 Intent,设置 Activity 返回值,并关闭 Activity。