Home Tags About

Android 学习笔记之 App Widgets

04 Jun 2012
android widget

[TOC]

在 Manifest 中声明

例如:

<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>

解释如下:

  • ExampleAppWidgetProvider 指定了该 widget 所使用的 AppWidgetProvider 的类名
  • “android.appwidget.action.APPWIDGET_UPDATE” 必须显示声明,其他一些广播会自动接收
  • “@xml/example_appwidget_info” 指明使用的 AppWidgetProviderInfo 资源

增加 AppWidgetProviderInfo 资源

该资源描述 widget 所必需的一些特性,例如最小布局尺寸,多久进行更新,在第一次创建 widget 时启动一个Activity对其进行配置(可选)。 该xml文件保存在目录 res/xml/ 下面。

示例:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dp"
    android:minHeight="72dp"
    android:updatePeriodMillis="86400000"
    android:previewImage="@drawable/preview"
    android:initialLayout="@layout/example_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure" 
    android:resizeMode="horizontal|vertical">
</appwidget-provider>

解释:

  • minWidthminHeight 指定 widget 的默认最小空间。Android 会把 widget 放置在桌面的网格当中,每个网格有固定的宽和高。如果 widget 的 minWidth 和 minHeight 与网格的 大小不匹配,那么系统将会进行四舍五入,取最接近的网格大小。

    注意: 为了 widget 在不同设备上的可移植性,最好不要把 widget 的最小尺寸设为大于 4 x 4 网格的尺寸。

  • minResizeWidthminResizeWidth 属性指定了 widget 的绝对最小尺寸,也就是说,如果 widget 小于该尺寸,便会因为变得模糊、看不清而不可用。 使用这两个属性,可以允许用户重新调整 widget 的大小,使 widget 的大小可以小于 minWidth 和 minHeight。该特性是在 Android 3.1 中引入的。

  • updatePeriodMillis 属性定义了 widget 的更新频率。实际的更新时机不一定是精确的按照这个时间发生的。建议更新尽量不要太频繁,最好是低于1小时一次。 或者可以在配置 Activity 里面供用户对更新频率进行配置。

    注意: 当更新时机到达时,如果设备正在休眠,那么设备将会被唤醒以执行更新。如果更新频率不超过1小时一次,那么对电池寿命应该不会造成多大的影响。 如果你需要比较频繁的更新,或者你不希望在设备休眠的时候执行更新,那么可以使用基于 alarm 的更新来替代 widget 自身的刷新机制。将 alarm 类型设置为 ELAPSED_REALTIME 或 RTC,将不会唤醒休眠的设备,同时请将 updatePeriodMillis 设为 0。

  • initialLayout 属性指向 widget 的布局资源文件

  • configured 为可选属性,定义了 widget 的配置 Activity。如果定义了该项,那么当 widget 创建时,会自动启动该 Activity。

  • previewImage 指定预览图,该预览图在用户选择 widget 时出现,如果没有提供,则会显示应用的图标。 该字段对应在 AndroidManifest.xml 中 receiver 的 android:previewImage 字段。由 Android 3.0 引入。

  • autoAdvanceViewId 属性指定一个子view ID,表明该子 view 会自动更新。在 Android 3.0 中引入。

  • resizeMode 指定了 widget 的调整尺寸的规则。可取的值有: “horizontal”, “vertical”, “none”。Android 3.1 引入。

创建 Widget Layout

你必须在 res/layout/ 目录中为你的 widget 定义一个初始 Layout。

由于 Widget layout 是基于 RemoteViews 的,所以并非所有的 layout 和 view 都可以使用。

一个 RemoteViews 对象支持如下 layout:

  • FrameLayout
  • LinearLayout
  • RelativeLayout

并支持如下 view widget:

  • AnalogClock
  • Button
  • Chronometer
  • ImageButton
  • ImageView
  • ProgressBar
  • TextView
  • ViewFlipper
  • ListView
  • GridView
  • StackView
  • AdapterViewFlipper

这些类的子类是不支持的。

使用页边距 (margins)

通常情况下,Widgets 不应该扩展到屏幕的边缘,也不应该与其他 widget 贴在一块。因此,你应该在你的 widget 四周增加 margins。

从 Android 4.0 开始,为了更好的与其他 widgets 和 icons 对其,系统会在 widget frame 和 widget bounding box 之间会自动增加 padding。 你需要把 targetSdkVersion 设为 14 或者更高,才能利用该特性。

It’s easy to write a single layout that has custom margins applied for earlier versions of the platform, and has no extra margins for Android 4.0 and greater:

我们希望在旧的平台上使用自定义的 margins,而在 Android 4.0 及以后的版本上不出现多余的 margins,那么需要按照如下的指示进行操作:

Set your application’s targetSdkVersion to 14 or greater.

  1. 设置 targetSdkVersion 为 14 或者更高.
  2. 在布局中引用 dimension resource 作为 margins,如下所示:

     <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:padding="@dimen/widget_margin">
        
       <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="horizontal"
         android:background="@drawable/my_widget_background">
         …
       </LinearLayout>
        
     </FrameLayout>
    
  3. 创建两个布局资源,一个位于 /res/values/,一个位于 res/values-v14/。前者供 Android 4.0 之前的版本使用,后者供 Android 4.0 及后续版本使用:

res/values/dimens.xml:

<dimen name="widget_margin">8dp</dimen>

res/values-v14/dimens.xml:

<dimen name="widget_margin">0dp</dimen>

另一种办法是,在默认的 9-patch 背景中预留多余的留白,并为 API level >= 14 的平台提供没有留白的 9-patch 资源。

AppWidgetProvider 类

AppWidgetProvider 继承自 BroadcastReceiver,以便处理 widget 广播。AppWidgetProvider 只接收 widget 相关的广播,例如 widget 的更新、删除、开启和禁用。 当这些广播事件发生时,widget 会收到下列方法调用:

onUpdate()
当 widget 需要更新时。更新的间隔时间在上述的 updatePeriodMillis 属性中定义。
onDeleted(Context, int[])
每次该 widget 被删除时。
onEnabled(Context)
当该 widget 的实例第一次被创建时。例如,如果用户对同一个 widget 增加了两次(两个实例),那么该事件将只会在第一次增加时触发。
onDisabled(Context)
当该 widget 的最后一个实例被删除时触发。
onReceive(Context, Intent)
接收到任意广播时触发,并且会在上述的方法之前被调用。通常情况你不需要实现该方法,因为默认的 AppWidgetProvider 实现已经过滤了所有的 App Widget 广播, 并根据情况调用上述的方法。

注意: 在 Android 1.5 中,onDeleted 方法不能被正确的调用。你可以通过实现 onReceive 方法来解决该问题。