小组件开发最佳实践(Android)

应用程序小组件是一个微型的应用程序视图,可以嵌入其他应用程序(例如主屏幕)中并接收定期更新。本文档介绍了如何使用App Widget provider发布Android小组件。

概述

应用程序视图在用户界面中称为组件(Widget),您可以使用小组件提供程序(App Widget provider)发布这些视图。能够容纳其他小组件的应用程序组件称为App Widget宿主(如Launcher),下图为时钟和天气的小组件示例,更多有关小组件设计规范的内容,请参见 App Widgets Overview

小组件示例

为了创建小组件,您还需要先了解以下信息。

  • AppWidgetProviderInfo

    描述应用程序小组件的元数据,例如小组件的布局、更新频率、指定AppWidgetProvider类等。

  • AppWidgetProvider

    用来处理小组件的广播事件,当更新、启用、禁用、删除小组件时,您可以收到广播。

  • View layout

    以XML定义的小组件的初始布局

  • 其他

    还可以为您的小组件配置活动。启动活动后,允许用户在该活动里修改小组件设置。

创建小组件

  1. 在应用程序的清单文件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属性,该属性指定App Widget使用的是AppWidgetProvider类。

    • intent-filter元素必须包含具有android:name属性的action元素。此属性指定AppWidgetProvider类接受的广播类型为ACTION_APPWIDGET_UPDATE(这是您须明确声明的唯一广播)。AppWidgetManager根据用户需要,自动将所有其他App Widget广播发送到AppWidgetProvider。

    • meta-data元素指定AppWidgetProviderInfo资源,并需要设置以下属性。

      • android:name指定元数据名称,使用android.appwidget.provider将数据标识为AppWidgetProviderInfo描述符。

      • android:resource 指定AppWidgetProviderInfo的资源位置。

  2. 添加AppWidgetProviderInfo元数据。

    AppWidgetProviderInfo定义了小组件的基本配置(例如最小布局尺寸、初始布局资源、更新频率、(可选)在创建时启动的配置Activity等)。使用单个元素在XML资源中定义AppWidgetProviderInfo对象,并将其保存在项目的res/xml文件夹中。

    <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">
    </appwidget-provider>

    代码配置说明如下。

    • minWidthminHeight属性指定默认情况下App Widget占用的最小尺寸(为了使您App的小组件适配多种屏幕,此处的最小尺寸不得大于4 x 4单元)。更多信息请参见应用程序小组件设计指南

    • updatePeriodMillis属性定义App Widget框架通过调用onUpdate()回调方法从AppWidgetProvider请求更新的频率。使用该值不能保证实际的更新会准时进行,我们不建议频繁地更新(每小时不超过一次),以便节省电池电量。

    • initialLayout属性指向定义App Widget布局的布局资源。

    • configure属性(可选属性)定义了用户添加App Widget时要启动的活动,以便配置App Widget属性。

    • previewImage属性指定配置后的应用小组件的预览,用户在选择应用小组件时可见。如果未提供,用户看到的为您应用程序的启动器图标。

    • resizeMode属性指定可以调整窗口小组件大小的规则,例如水平、垂直或双向调整大小等。

    • minResizeHeightminResizeWidth属性指定窗口小组件可以调整大小的最小高度与最小宽度(单位为dp)。

    说明

    更多元素属性的介绍请参见AppWidgetProviderInfo

  3. 创建小组件布局。

    使用XML为您的小组件定义初始布局,并将其保存在工程的res/layout目录中。

    小组件布局基于RemoteViews,一个RemoteViews对象(通常就是一个小组件)可以支持以下布局类和视图组件。

    • 布局类

      • FrameLayout

      • LinearLayout

      • RelativeLayout

      • GridLayout

      说明

      仅支持这些类,不支持这些类的子类。

    • 视图组件

      • AnalogClock

      • Button

      • Chronometer

      • ImageButton

      • ImageView

      • ProgressBar

      • TextView

      • ViewFlipper

      • ListView

      • GridView

      • StackView

      • AdapterViewFlipper

    • 其他

      RemoteViews还支持ViewStub,这是一个不可见的零尺寸视图,可用于在运行时延迟布局资源的渲染。

    下面以设计布局类FrameLayout为例介绍,更多信息请参见App Widget设计指南

    1. 进入res/layout/目录,创建一个xml的布局文件(例如:appwidget_provider_layout.xml)。

    2. 添加布局类FrameLayout,相关代码如下。

      <?xml version="1.0" encoding="utf-8"?>
      <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="match_parent"
          android:layout_height="match_parent">
      
          <Button
              android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="OK"
              tools:ignore="HardcodedText" />
      </FrameLayout>
  4. 添加小组件之间的距离。

    为了更好地提升用户体验,从Android 4.0及以上版本,系统会自动在小组件框架和应用小组件的边界框之间提供填充。Android 4.0以下则需要开发者自行设置(由于目前市场上Android 4.0以下机型较少,此处不作介绍,可自行查找资料)。

  5. 创建AppWidgetProvider类。

    最后您还需要创建在清单中声明的ExampleAppWidgetProvider类。例如,如果您想要一个用于单击的按钮小组件,使用AppWidgetProvider实现的示例代码如下。

    /*
     * Copyright (C) 2008 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    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);
                Intent intent = new Intent();
                PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
    
                // Get the layout for the App Widget and attach an on-click listener
                // to the button,其中appwidget_provider_layout为创建的xml布局文件名称
                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()方法,并定义启动活动的PendingIntent,使用setOnClickPendingIntent(int,PendingIntent)将其添加到小组件的按钮上。

    关于AppWidgetProvider的使用说明如下。

请求接口

小组件开发过程中常用到的请求接口如下。

  • 获取设备列表(已添加到小组件)

    path:/iotx/ilop/queryComponentProduct
    version:1.0.0
    params:@{}
  • 获取设备属性

    path:/iotx/ilop/queryComponentProperty
    version:1.0.0
    params = @{@"productKey":productKey,@"iotId":iotId,@"query":@{@"dataType":@"BOOL”, @"I18Language":@"zh-CN"}}
  • 更新设备属性

     path:/iotx/ilop/updateComponentProduct
    version:1.0.0
    params:更改后的设备list
  • 获取场景列表(已添加到小组件)

    path:/living/appwidget/list
    version:1.0.0
    params:@{}
  • 执行场景

    path:/scene/fire
    version:1.0.1
    params:@{@"sceneId":sceneId}
  • 更新小组件场景

    path:/living/appwidget/create
    version:1.0.0
    params = @{@"sceneIds": @[]}

更多关于接口的说明请参见场景服务物的模型服务

监听设备属性更新

如果您需要开发控制设备的小组件,通过小组件实现手机对设备的查看和控制。那么您还需要了解App端物模型(属性、事件、服务)相关的知识点。下面以如何用物模型SDK监听设备属性变更事件为例,提供相关的代码示例供您参考。

下面以使用物模型SDK监听设备属性变更事件为例,提供相关的代码示例供您参考。更多内容请参见物模型SDK

PanelDevice panelDevice = new PanelDevice(iotId);
panelDevice.subAllEvents(new IPanelEventCallback() {
    @Override
    public void onNotify(String s, String s1, Object o) {
        Log.d(TAG, "onNotify: " + s);
        Log.d(TAG, "onNotify: " + s1);
        Log.d(TAG, "onNotify: " + JSON.toJSONString(o));
        // 更新界面
    }
}, new IPanelCallback() {
    @Override
    public void onComplete(boolean b, Object o) {
        /* */
    }
});
panelDevice.init(this, new IPanelCallback() {
    @Override
    public void onComplete(boolean b, Object o) {
        Log.e(TAG, "panelDevice.init:" + b);
    }
});