• 从零开始系统深入学习android(实践让我们开始写代码Android框架学习7.App Widgets)


    第7章 App Widgets

    App Widgets是一个应用程序的微型视图,可以嵌入到其他应用程序(如主屏幕)并且能够定期更新。你可以发布一个应用程序的App Widget,而这些视图称为窗口的用户界面。一个应用程序组件,可以支持其他应用程序的App Widgets称为App Widget的主机(host)。下面的截图是显示音乐的App Widget。

    该文档将介绍如何在应用程序里发布和使用App Widget。

    7.1 基础知识

    要创建一个App Widget,您需要了解以下几点:

    ◆AppWidgetProviderInfo对象:
    描述了一个App Widget的元数据,如在App Widget的布局,更新频率,和AppWidgetProvider类。都应在XML中定义。
    ◆AppWidgetProvider类的实现:
    定义一个基于广播事件与App Widget的接口方法。通过它,您将收到广播对App Widget进行更新,启用,禁用和删除。
    ◆视图布局:
    在XML中初步定义App Widget布局。
    此外,还可以实现App Widget可配置的Activity。当用户添加您的App Widget,并允许他或她在创建时修改设置时启动这个可配置的Activity。
    该文档将介绍如何在应用程序里发布和使用App Widget。

    7.2 在Manifest.xml中声明App Widgets

    首先,在您的应用程序的AndroidManifest.xml文件中应声明AppWidgetProvider类。例如代码清单7-1所示:

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

    代码清单 7-1

    <receiver>节点需要的android:name属性,在App Widget中指定使用AppWidgetProvider。 <intent-filter>节点必须包含一个<action>节点的name属性。此属性指定AppWidgetProvider接受ACTION_APPWIDGET_UPDATE广播。这是唯一的广播,你必须明确声明。在AppWidgetManager自动发送其他App widget广播到AppWidgetProvider是必要的。<meta-data>节点指定的AppWidgetProviderInfo的资源,需要以下属性:

    android:name - 指定的元数据的名称。使用android.appwidget.provider识别作为

    ◆AppWidgetProviderInfo描述符的数据。

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

    7.3 添加AppWidgetProviderInfo元数据

    AppWidgetProviderInfo是定义App Widget的本质,例如其最小的布局尺寸,初始布局资源,如何更新App Widget,和(可选)配置Activity,在创建时发起。在XML资源文件中定义AppWidgetProviderInfo对象,使用<appwidget-provider>节点和在项目的res / xml/文件夹中保存。如代码清单7-2所示:

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

    代码清单 7-2

    以下是<appwidget-provider>属性的摘要,在这之前最好你已经阅读了第一部分的Widget设计章节:

    ◆App Widget默认的情况下minWidth和MinHeight属性值指定最小占据的空间,AppWidgets默认是在Home屏幕位置,在其窗口基础上的单元格中有一个明确高度和宽度的网格。如果一个App Widget的最小宽度或高度值不匹配单元格的尺寸,则App Widget尺寸向上舍入到最接近的单元格大小。

    注:为了使App Widget更容易移植在不同设备,App Widget的最小尺寸不应大于4×4单元格

    ◆minResizeWidth和minResizeHeight属性指定App Widget的绝对最小尺寸。这些值应该指定尺寸下,否则应用程序组件将无法辨认或以其他方式使用。在android3.1,允许用户使用这些属性调整控件大小,可能是小于默认尺寸界定的minwidth和minheight属性。

    ◆updateperiodmillis属性定义,往往在App Widget框架,应从appwidgetprovider请求通过调用onupdate()回调方法更新。实际上不能保证更新的准确性,我们建议尽可能的少更新或者不超过每小时一次,以此来节省电池。你也可以在configuration-some中允许用户调整频率,比如证劵报价机,一些用户可能想要15分钟更新一次,一些则想要每天只更新四次

    注:如果该设备是睡着的,而它是一个更新的时间(定义updatePeriodMillis)时,则该设备将被唤醒以执行更新。如果你不超过每小时更新一次,这可能不会对电池寿命造成重大的问题。但是,如果您需要频繁更新或你并不需要更新,而设备是睡着的,你就可以根据报警代替执行,则不会唤醒设备执行更新。要做到这一点,设置一个Intent,当您的AppWidgetProvider收到的报警时,使用AlarmManager。设置报警类型有ELAPSED_REALTIME或RTC,这在收到报警时,该设备被唤醒。然后设置updatePeriodMillis为零(“0”)。

    ◆initialLayout的属性指向布局资源,它定义了App Widget的布局。

    ◆在Activity启动时对属性进行配置定义,用户添加App Widget,以便让用户配置App Widget属性。这是可选的。

    ◆在配置previewImage属性后将指定一个App Widget图标是什么样子,当选择这个App Widget时用户可以进行预览。如果没有提供图标,用户却认为laucher是您的应用程序图标。这个字段对应android:previewImage进行在 <receiver>元素的AndroidManifest.xml文件中。在android3.0引入。

    ◆在Android 3.0引入,该autoAdvanceViewId属性指定的App Widget子视图的视图ID。

    ◆在Android 3.1引入,该resizeMode属性指定其中一个可以调整规则的Widget。您可以使用此属性使主屏幕Widget的调整方式,如水平,垂直,或两轴。用户长按一个Widget,会显示其调整的界面,然后拖动水平和/或垂直的控键,改变布局网格的大小。resizemode属性值包括"horizontal", "vertical", 和"none"。两者都有如“horizontal | vertical”。

    7.4 创建App Widget布局

    你必须为你的App widget定义初始布局,你可以在XML定义并保存在项目的res/layout/目录中。你可以使用下列的View对象来设计你的App widget,但在你开始设计你的App widget之前,请阅读和理解App widget的设计准则。 如果你熟悉XML的布局,创建App widget的布局很简单。然而,你们必须知道App widget的布局都是基于RemoteViews类,它不支持各种布局或view widget。
    一个RemoteViews对象支持以下布局类:

    FrameLayout

    LinearLayout

    RelativeLayout

    以下View支持widget:

    AnalogClock

    Button

    Chronometer

    ImageButton

    ImageView

    ProgressBar

    TextView

    ViewFlipper

    ListView

    GridView

    StackView

    AdapterViewFlipper

    但是这些类的子类却都不支持。

    1. 添加边距到App Widgets

    widget通常不应该扩展到屏幕边缘,不应与其他widget视图共同刷新,所以你应该在你的widget框中增加边距。自Android 4.0起,App widget提供了widget之间的自动填充框架和App widget的包围盒,以便用户在home屏幕更好的调整其他widget和图标。要获得这种功能,你需要吧应用程序的targetSdkVersion设置为14或更高。
    早期版本,编写一个布局很容易,并可以自定义边距,在Android4.0或以上版本并没有额外的边距,步骤如下:
    ◆设置应用程序的targetsdkversion值为14或更高。
    ◆创建一个如下布局,引用dimension资源,如代码清单7-3所示:

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

    代码清单 7-3

    ◆创建两个dimensions 的资源,一个在res /values/提供Android 4.0之前的自定义边距,一个在res/values-v14/没有为Android4.0widgets提供额外的padding:
    res/values/dimens.xml:

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

    res/values-v14/dimens.xml:

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

    7.5 使用AppWidgetProvider类

    首先,你必须在AndroidManifest<receiver>节点里声明 的AppWidgetProvider类的实现(参见本章的“7.2在Manifest.xml中声明App Widgets” )。

    AppWidgetProvider继承broadcastreceiver用来处理App widget广播非常方便。 AppWidgetProvider只接收和App widget相关的事件广播,如App widget进行更新时,相关的App widget进行删除,启用和禁用。这些广播事件发生时,AppWidgetProvider将调用以下的方法:
    ◆onUpdate() 

    这种在AppWidgetProviderInfo由updatePeriodMillis属性定义的时间间隔来使AppWidget更新。当用户添加App widget时,这种方法也被调用,所以它应该进行基本的设置,如定义View事件的处理,如果有必要,还应启动临时service。不过,如果你已经声明配置的Activity,当用户添加App widge这种方法则不会调用,而是后续更新调用。配置的Activity完成后,它的作用就是执行首次更新。

    ◆onAppWidgetOptionsChanged()

    当wodget第一次被调整时被调用。你能使用这个回调来显示或隐藏内容。你可以通过getAppWidgetOptions()来获得大小范围,它返回一个Bundle,你可以使用下面String键,来获得值:

    AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH—当前宽度的最小值,单位是DP

    AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT—当前高度的最小值,单位是DP

    AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH—当前宽度的最大值,单位是DP

    AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT—当前高度的最大值,单位是DP

    Android4.1从在引入这个方法。请注意

    ◆onDeleted(Context, int[])

    每次一个App Widget从App Widget主机中删除时被调用。

    ◆onEnabled(Context)

    首次创建App widget实例时调用。例如,如果用户为同一个App widget添加了两个实例,这也只调用一次。如果你需要打开一个新的数据库或进行其他的设置,那么在这个地方实例是非常好的。

    ◆onDisabled(Context)

    当最后一个App widget的实例时从App widget主机中删除时被调用,使用onDisabled(Context)方法进行清理,比如删除临时数据库。

    ◆onReceive(Context, Intent)

    可以理解为一个通用广播接收接口,上面的每个方法的回调。你通常不需要实现这个方法,因为默认的AppWidgetProvider实现过滤器所有App widget广播,并适当调用以上的方法。

    当每个App widget添加到一个主机时,最重要的是AppWidgetProvider onUpdate()方法回调(除非你使用一个配置的Activity)。如果你的App widget接受任何用户交互事件,那么在回调时,你需要注册事件处理器。如果你的App widget不能创建临时文件或数据库,或者执行其他的工作,那就需要清楚,onUpdate()方法,可能是你唯一需要定义的回调方法。例如,如果你想要一个App widget上有一个按钮,当点击时启动一个Activity,你可以这样实现AppWidgetProvider,如代码清单7-4所示:

    public class ExampleAppWidgetProvider extends AppWidgetProvider {
     
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            final int N = appWidgetIds.length;
     
            for (int i=0; i<N; i++) {
                int appWidgetId = appWidgetIds[i];
     
                    Intent intent = new Intent(context, ExampleActivity.class);
                PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
                RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
                views.setOnClickPendingIntent(R.id.button, pendingIntent);
                appWidgetManager.updateAppWidget(appWidgetId, views);
            }
        }
    }

    代码清单 7-4

    上面代码清单7-4中的AppWidgetProvider只定义onupdate()方法,其目的是定义一个PendingIntent启动一个Activity并使用使用setonclickpendingintent(int,pendingintent)附加到App widget按钮。注意,在appWidgetIds中它包括一个循环遍历每个条目,这是一个数组的id标识,确定每个App widget。这样,如果用户创建多个App widget的实例,然后他们都同时更新。然而,只有一个updateperiodmillis时间表将管理所有的App widget。例如,如果更新计划被定义为每两个小时,在第一个后等待一小时在添加第二个实例,那么它们两个都将使用第一个的周期而第二个更新周期会被忽略。读者可以参考ApiDemos\src\com\example\android\apis\appwidget的例子。

    AppWidgetProvider就是一个方便的类而已。如果你想直接接收App widget广播,你可以实现自己的BroadcastReceiver或覆盖的onReceive(Context, Intent) 方法。你需要注意以下几个intent: 

    ACTION_APPWIDGET_UPDATE

    ACTION_APPWIDGET_DELETED

    ACTION_APPWIDGET_ENABLED

    ACTION_APPWIDGET_DISABLED

    ACTION_APPWIDGET_OPTIONS_CHANGED

    7.6 创建一个App Widget配置的Activity

    如果你想要一个用户,当他增加了一个新的App widget时来配置设置,那么你可以创建一个App widget配置Activity。当前的Acitivity将自动启动的App widget的主机,并允许用户在创建时配置App widget的颜色,大小,更新周期或其他功能的设置。 这个配置Activity应该在Android manifest文件中声明是一个标准的Activity。然而,它将通过App widget主机使用ACTION_APPWIDGET_CONFIGURE Action来启动,所以这个Activity需要接收这种Intent。如代码清单7-5所示:

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

    代码清单 7-5

    此外,Activity必须在AppWidgetProviderInfo XML文件中声明android:configure属性(参见前面小节“添加AppWidgetProviderInfo元数据”)。例如,需要配置的Activity声明如代码清单7-6所示:

    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        ...
        android:configure="com.example.android.ExampleAppWidgetConfigure" 
        ... >
    </appwidget-provider>

    代码清单 7-6

    注意,这个Activity是声明完全限定的命名空间,因为它是从外部包引用的。
    你需要的是启动一个配置Activity。现在你所需要的是实际的Activity。然而,当你实现Activity有两个重要的事情要记住:

    ◆这个App widget主机调用配置Activity,而配置Activity应该总是返回一个结果码。这个结果码应该包括App widget  ID。

    ◆当创建App widget时OnUpdate()方法将不会被调用(配置Activity启动时,系统将不发送ACTION_APPWIDGET_UPDATE广播)。

    这是配置Activity的职责,它请求从App widget首次创建AppWidgetManager时更新。然而,onUpdate()方法将调用后续更新,它仅在第一次跳过。

    请参阅以下的代码片段,看它怎样返回配置和更新的App widget后的结果。

    当一个App widget使用配置Activity时,这个Activity配置完成后负责更新App widget。你可以从AppWidgetManager通过请求直接更新。
    1. 从通过Intent启动的Activity中获得App widget的ID:

    Intent intent = getIntent();
    Bundle extras = intent.getExtras();
    if (extras != null) {
        mAppWidgetId = extras.getInt(
                AppWidgetManager.EXTRA_APPWIDGET_ID, 
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    2. 执行你的App widget配置.
    3. 当配置完成后,通过调用getInstance(context)获得一个AppWidgetManager实例:

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

    4. 通过调用updateAppWidget(int,RemoteViews)来使用RemoteViews布局更新App widget:

    RemoteViews views = new RemoteViews(context.getPackageName(),
    R.layout.example_appwidget);
    appWidgetManager.updateAppWidget(mAppWidgetId, views);

    5. 最后,创建返回的intent,其设置Activity的结果,并终止该Activity:

    Intent resultValue = new Intent();
    resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
    setResult(RESULT_OK, resultValue);
    finish();

    7.7 设置一个预览的图片

    Android3.0之后引入了previewImage属性,它指定一个Appwidget缩略图。下面让我们看下代码清单7-7,看看是如何设置的

    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
      ...
      android:previewImage="@drawable/preview">
    </appwidget-provider>

    代码清单 7-7

    为了帮助您的Appwidget(指定在previewImage领域)创建预览图像,在Android模拟器中包含一个应用程序被称为“Widget Preview”。要创建预览图像,则要启动该应用程序,选择你的应用程序的Appwidget,并设置你希望的预览图像,然后将它保存,并将其放置在你的应用程序的drawable资源下。

  • 相关阅读:
    input失效后,怎么改变它默认就有的灰色
    弹性盒布局-宽度自动分配-图片自适应
    时钟效果收集
    1秒加1
    tab切换☆☆☆☆☆
    音乐播放的动画效果
    css3-文字旋转
    红黑树
    Ruby2.0后版本的debug工具: byebug
    [转]DSL-让你的 Ruby 代码更优秀
  • 原文地址:https://www.cnblogs.com/tianjian/p/2716870.html
Copyright © 2020-2023  润新知