[01] App Widgets

 

链接:http://developer.android.com/guide/topics/appwidgets/index.html

 

1.1    The Basics
1.2    Declaring an App Widget in the Manifest
1.3    Adding the AppWidgetProviderInfo Metadata
1.4    Creating the App Widget Layout
1.5    Using the AppWidgetProvider Class
1.6    Creating an App Widget Configuration Activity
1.7    Setting a Preview Image
1.8    Enabling App Widgets on the Lockscreen
1.9    Using App Widgets with Collections

 

 

  App Widgets are miniature application views that can be embedded in other applications (such as the Home screen) and receive periodic updates. These views are referred to as Widgets in the user interface, and you can publish one with an App Widget provider. An application component that is able to hold other App Widgets is called an App Widget host. 

  翻译:App Widgets是内嵌在其他应用(例如手机桌面)中的迷你应用,能够周期性地进行更新。如同UI中的Widgets一样,你可以通过 App Widget provider制作一个 App Widget,一个可以容纳其他 App Widgets的应用组件被称为 App Widget host。

  收获:App Widgets不一定要放在桌面上,是可以内嵌进其他应用的。除了 snapshot的 Demo目前还没有接触到这种方式,算是一个思路的扩展,多了一种应用间交互的方式。

 

 

1.1  The Basics

 

  创建一个 App Widget需要:AppWidgetProviderInfo、AppWidgetProvider、View layout,以上三者是必需的。另外可以额外添加一个 Activity,每当用户添加一个 App Widget这个 Activity就会启动,并在初始化的时候对该 App Widget进行设置。 

  我的理解:AppWidgetProviderInfo定义了 App Widget的元数据,例如尺寸、刷新时间、初始化layout等; AppWidgetProvider继承自 BroadcastReceiver,可以捕捉到 App Widget的增删更新等事件,并进行相应处理; View Layout提供 App Widget的界面。

 

 

1.2  Declaring an App Widget in the Manifest

 

  因为 AppWidgetProvider继承自 BroadcastReceiver,所以需要向系统注册这个广播,例如:

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

  <meta-data>用于描述 AppWidgetProviderInfo,android:resource指向 AppWidgetProviderInfo的位置。

 

 

1.3  Adding the AppWidgetProviderInfo Metadata

 

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

 

  minWidth、minHeight描述 App Widget的尺寸,这个尺寸和 App Widget最终在屏幕上所占的区域有关。计算方法是 minWidth|minHeight = 70 * n - 30

  updatePeriodMillis描述自动更新时间,到时间会调用onUpdate()方法。官方建议时长最好大于1小时,否则会比较耗电。手机处于黑屏待机状态时,会自动唤醒手机进行onUpdate()操作,如果希望待机时不更新,应该使用 AlarmManager,并设置提醒种类为 ELAPSED_REALTIME 或 RTC。

  previewImage描述在 App Widget列表里的预览效果图。

  initialLayout描述 App Widget初始化时的布局。

  configure描述 App Widget绑定的可选 Activity。这个参数还没用到过,先略过。

  resizeMode描述 App Widget是否可拉伸,Android3.1之后加上了这个功能,App Widget可以在长按后进行横向或纵向拉伸操作。

  widgetCategory描述 App Widget是在主屏还是在锁屏时显示,Android4.2之后加上了这个功能。开发者可以通过 getAppWidgetOptions()方法得到 App Widget是在哪个界面,当处于锁屏界面时,系统会忽略minWidth和minHeight的设置。横向会占满屏幕,纵向在没有设置resizable的情况下,竖屏手机会填充锁屏UI之外的区域。文档的原文是:1. If the widget does not mark itself as vertically resizable (android:resizeMode="vertical"), then the widget height will always be "small"; 2. If the widget marks itself as vertically resizable, then the widget height shows up as "small" on portrait phones displaying an unlock UI. In all other cases, the widget sizes to fill the available height. 我的理解是竖屏手机屏幕上不管是否设置了android:resizeMode="vertical",都只会占满屏幕上解锁界面剩下的部分。

  initialKeyguardLayout描述 App Widget在锁屏情况下的布局,与widgetCategory相对应,都是Android4.2添加的参数。

 

 

1.4  Creating the App Widget Layout

 

   同普通Layout写法一样,区别是 App Widget Layout是布局在 RemoteView上的,只支持如下的 Layout种类:FrameLayout、LinearLayout、RelativeLayout、GridLayout;和这些 Widget类型:AnalogClock、Button、Chronometer、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView、AdapterViewFlipper;加上ViewStub。

  关于 App Widget的margin属性,如果使用4.0以上版本编译,系统会预留好margin距离。之前在做项目的时候,我把一个4*1 Widget的minHeight属性设置为72dp,结果在系统上会显示为4*2 Widget,当时一直不明白为什么会这样,没办法改为了64dp才正常。现在明白了,同样的minHeight数值,在不同 Android版本上编译会导致显示差异。

 

1.5  Using the AppWidgetProvider Class

 

  AppWidgetProvider本身继承自 BroadcastReceiver,接收关于 App Widget的广播。

    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                if (appWidgetIds != null && appWidgetIds.length > 0) {
                    this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
                }
            }
        }
        else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
                final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                this.onDeleted(context, new int[] { appWidgetId });
            }
        }
        else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
            this.onEnabled(context);
        }
        else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
            this.onDisabled(context);
        }
    }

 

  通过不同的action来分配 App Widget的响应。添加第一个widget的时候会调用onEnable(),更新时会调用onUpdate(),删除时会调用onDelete(),最后一个widget被删除时会调用onDisable()。onAppWidgetOptionsChanged()方法是在4.1加上的,当widget初次添加或改变尺寸后会被调用。

  onUpdate()是最为重要的一个回调方法,因为每一个 App Widget被添加的时候都会调用(除非声明 Configure Activity,在这种情况下 Configure Activity负责初次的更新),用户交互事件的处理也需要放在onUpdate()里。如果widget不需要创建临时文件或数据库,也不需要清理操作,那么onUpdate()可能是唯一会用到的回调方法。

  需要注意的是:1. 如果用户添加了多个 App Widget,这些widgets会以第一个widget的初次更新时间为准同步更新,后续widgets的更新周期会被覆盖同步;2. AppWidgetProvider本身继承自 BroadcastReceiver,在回调方法return后,系统会认为该对象不再活跃,如果把一些耗时操作放在这些回调方法里,有可能会ANR,同时很可能出现异步操作在完成前整个进程就被系统干掉的情况。解决方法是在onUpdate()中新启一个 Service来处理耗时操作,并在 Service中进行widgets的更新。

 

 

1.6  Creating an App Widget Configuration Activity

 

  上一节提到过,使用 App Widget Configuration Activity可以替代onUpdate()执行第一次更新操作。那么这个 Activity是怎么做到的呢?

  之前我以为是利用一个 Activity执行各种原本应该在 AppWidgetProvider中执行的操作,写了一个 Demo之后才发现这个 Activity只是在 App Widget进行初始化的时候才会用到。具体流程是:点击添加widget -- 系统自动唤起 App Widget Configuration Activity -- 设置完成后activity退出 -- widget被添加到桌面。这个 Activity的好处就是,可以让用户自定义widget的一些参数,例如widget大小、显示内容等等,而不需要一开始就写死在onUpdate()里。

  必须在manifest中对这个 Activity进行如下设置,并在 AppWidgetProviderInfo中声明该 Activity。

<activity android:name=".ExampleAppWidgetConfigure">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
    </intent-filter>
</activity>

 

 

 

1.7  Setting a Preview Image

 

  设置在选择添加 App Widget桌面控件时的预览图。直接写在 AppWidgetProviderInfo中就可以了。

 

 

1.8  Enabling App Widgets on the Lockscreen

 

  Android4.2之后,引入了一个新的功能,就是可以把 App Widget添加到锁屏界面。声明"keyguard"代表可以应用在锁屏界面上。

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
   ...
   android:widgetCategory="keyguard|home_screen">
</appwidget-provider>

  开发者可以使用两套layout布局文件分别应用在主屏和锁屏界面,系统提供getAppWidgetOptions()方法,获取当前 App Widget是在哪种界面。  

  在锁屏界面下,系统会忽略widget中设置的minWidth、minHeight、minResizeWidth属性,显示规则(竖屏手机):1. 横向总是会填满可填充区域;2. 纵向会填充解锁UI剩下的区域。

 

 

1.9  Using App Widgets with Collections

 

  看完官网上的文档,我觉得描述的有些不清晰。这一节就用简单阐述一下自己的理解。

  通常状况,要在一个 Activity里放置一个 ListView,我们怎么做?首先在layout中添加一个 ListView,然后在 Activity里自定义一个 Adapter,并在这个 Adapter里接收一组数据用作展示内容,在getView()方法里设置每一条子 View的layout并填充数据,最后使用onNotifyDatasetChanged()方法刷新界面。

  在 App Widget上这么做会遇到什么问题?1. ListView还是那个 ListView,但必须用一个 RemoteView对象来展示;2. 既然是 RemoteView对象,那就不能用setOnClickListener()了对吧;3. 新的 Collection对象还能用之前的 BaseAdapter吗?每个子 View包括这些子 View里的控件怎么添加点击响应事件?

  通过看源码和官方 WeatherWidget的范例我们可以发现,RemoteViewsService类和其中的 RemoteViewsFactory接口就是用来应对这种情况的。对于普通的 View,Activity -- ListView -- BaseAdapter -- meta data;那么对于 RemoteView来说, 就是 RemoteView -- ListView -- RemoteViewsFactory -- meta data。这里的 RemoteViewsFactory就是一个适配器,作用是把每一条元数据通过getViewAt()方法转换成相应的 RemoteView对象,使得能够展示在 App Widget上。最后用一张文档里的配图说明 App Widget中对于以集合形式展现的 RemoteView是如何刷新的。

 

 

 

 

posted @ 2013-09-15 10:20  海通  阅读(418)  评论(0编辑  收藏  举报