翻译Dev Guide 之 App Widget

今天要做一个App Widget,在网上搜了一下,发现一篇翻译的DevGuide,但是没有翻译完整,所以自己来翻译一下,顺便也加深理解,水平有限翻译得可能不好望见谅。以后有时间会陆续翻译其他内容,不过还是建议大家尽量读英文文档。

 

App Widget

    AppWidget是可以嵌入其他应用程序(比如HomeScreen),并且可以接收周期性更新的微型应用程序视图。这些视图在UI中被当成Widget,你可以用APPWidgetProvider发布。一个可以容纳其他AppWidgets的应用程序组件被称为App Widget宿主。下面的截图展示了一个Music App Widget。


基础知识

    为了创建一个AppWidget,你需要如下东西:

1.AppWidgetProviderInfo对象,描述了AppWidget的元数据,比如它的Layout,更新频率和AppWidgetProvider类,这些需要定义在XML文件中定义。

2.AppWidgetProvider类的实现,定义了基于广播事件的基本方法,允许你通过编程与AppWidget交互,当此App Widget被更新,启用,禁用或删除时你将会收到广播。

3.View Layout,用XML定义了App Widget的初始化Layout。

4.另外,你可以实现一个可选的AppWidgetConfigurationActivity,它将在用户添加你的AppWidget时启动,并允许用户在创建时更改App Widget的设置。

/* 接下来的内容是创建一个简单的App Widget的步骤/

在Manifest中声明一个App Widget

首先,在你的应用程序的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是必须的,指定了AppWidget所用的AppWidgetProvider。

<intent-filter>必须包含一个拥有android:name属性的<action>元素,它指定了这个AppWidgetProvider接收ACTION_APPWIDGET_UPDATE广播。这是你唯一必须显示声明的广播,其余AppWidget广播将由AppWidgetManager在必要时自动向AppWidgetProvider发送。

<meta-data>元素指定了AppWidgetProviderInfo资源,需要以下属性:

   android:name - 指定metadata的名字,使用android.appwidget.provider识别的数据作为AppWidgetProviderInfo描述符。

   android:resource,指定AppWidgetProviderInfo资源的路径。

添加AppWidgetProviderInfo元数据

AppWidgetProviderInfo定义了AppWidget的基本特性,比如最小的布局尺寸,初始化布局资源,更新频率和在创建的时候启动的一个configurationActivity(可选)。在XML文件中用一个单独的<appwidget-provider>元素定义AppWidgetProviderInfo对象,并保存到项目的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>

minWidth和minHeight指定了AppWidget的layout要求的最小空间。缺省的AppWidgets所在窗口的桌面位置基于有确切高度和宽度的单元网格。如果AppWidget的最小长宽和这些网格单元的尺寸不匹配,那么这个App Widget将收缩到最接近的单元尺寸。

因为桌面布局方向(由此,单元的尺寸)可以变化,按照拇指规则,你应该假设最坏情况单元尺寸是74像素高和宽。不过,你必须从最后的尺寸中减去2以把像素计算过程中产生的任何的整数舍入误差考虑在内。要找到像素密度无关的最小宽度和高度,使用这个公式:
(number of cells * 74) - 2
遵循这个公式,你应该使用72dp为每一个单元高度,294dp为四个单元宽度。

注意,为了使你的app能跨设备,你的App Widget最小尺寸应该永远不要大于4 x 4单元。

updatePerdiodMillis 属性定义了AppWidget框架调用onUpdate()方法来从AppWidgetProvider请求一次更新的频度。实际更新时间并不那么精确,而且我们建议更新频率越低越好-也许每小时不超过一次以节省电源。你也许还会允许用户在配置中调整这个频率-一些人可能想每15分钟一次股票报价,或者一天只要四次。

注意,如果更新的时候机器正好在休眠,那么他会被唤醒去更新。如果你更新频率不高于每小时一次,那对电池寿命可能不会造成严重问题。但是如果你需要更频繁的更新或者/并且不需要在休眠时进行更新,你可以用不会唤醒机器的闹钟alarm来代替。为此,用AlarmManager,通过一个你的AppWidgetProvider能接收的Intent设置一个alarm,把闹钟类型设为ELAPSED_REALTIME或者RTC,这样就只会在机器醒着时提供alram,然后把updatePeriodMillis设为0.

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

configure参数定义了添加App Widget时启动的Activity,以便用户设置AppWidget的属性,这个参数是可选的。

previewImage指定了AppWidget配置完后的长相的预览,用户选择它后就可看到。如果不支持,用户将会看到你的应用程序的登录图标。这个域对应manifest中<receiver>的android:previewImage参数。

resizeMode指定了widget调整大小时依据的规则。你可以用它让homescreen的wiget可以从水平,垂直或者两个轴来调整大小。(Android3.1)

创建App Widget Layout

你必须在XML中为你的AppWidget定义初始化Layout并保存在/res/layout/目录。你可以使用下面列出的View objects来设计你的AppWidget,但之前请阅读AppWidget Design Guidelines

App Widget Layout基于RemoteViews,后者并不支持每种layout或者view widget。

一个RemoteViews对象(由此,一个App Widget)支持下列layout:

FrameLayout

LinearLayout

RelativeLayout

和以下widget:

AnalogClock

Button

Chronometer

ImageButton

ImageView

ProgressBar

TextView

ViewFlipper

不支持它们的子类。

使用AppWidgetProvider类

你必须通过在清单文件中使用<receiver>元素,来声明你的AppWidgetProvider类实现为一个广播接收器。

AppWidgetProvider 类扩展BroadcastReceiver 为一个简便类来处理AppWidget广播。AppWidgetProvider只接收和这个App Widget相关的事件广播,比如这个AppWidget被更新,删除,启用,以及禁用。当这些广播事件发生时,AppWidgetProvider将接收到下面的方法调用:

onUpdate(),以在AppWidgetProviderInfo中updatePeriodMillis为间隔被调用,当用户添加时也会被调用,因此它应该执行基础的设置,比如为视图定义事件处理器并启动一个临时的服务Service,如果需要的话。但是,如果你已经声明了一个ConfigurationActivity,这个方法 在用户添加App Widget时将不会被调用,而只在后续更新时被调用。ConfigurationActivity应该在配置完成时负责执行第一次更新。

onDeleted(Context, int[]),当从App Widget宿主删除App Widget时被调用。

onEnabled(Context),当App Widget实例第一次被创建时被调用。比如,如果用户添加了你的AppWidget的两个实例,它只会在第一个创建时被调用。如果你需要打开一个新的数据库或者执行其他的对于所有AppWidget实例来说只需要发生一次的操作,这里是一个实现的好地方。

onDisabled(Context),当你的App Widget的最后一个实例被从AppWidget宿主删除时它将被调用。在这里你应该把任何在onEnabled()中进行的工作清理干净,比如删除一个临时数据库。

onReceive(Context,Intent),接收到每个广播都会调用此函数,并且在调用以上回调函数之前。你通常不需要实现这个方法,因为默认的AppWidgetProvider实现会恰当地过滤所有AppWidget广播并调用回调函数。

注意: 在Android 1.5中,有一个已知问题,onDeleted()方法在该调用时不被调用。为了规避这个问题,你可以像Grouppost中描述的那样实现onReceive() 来接收这个onDeleted()回调。

最重要的AppWidgetProvider回调是onUpdate(),因为在任意一个AppWidget被添加到宿主时它会被调用(除非你使用了ConfigurationActivity)。如果你的App Widget接受任何用户交互事件,那么你需要在这个回调中注册相应的事件处理器。如果你的AppWidget不会创建临时文件或者数据库,或者执行其他需要清理的工作,那么onUpdate()也许是你唯一需要定义的回调函数。比如,如果你想要一个包含一个Button的AppWidget,点击之后将登录一个Activity,你可以使用下面的AppWidgetProvider实现:

public class ExampleAppWidgetProvider extends AppWidgetProvider{
   
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
        final int N = appWidgetIds.length();
       
        //为每个属于此provider的App Widget执行此循环程序
        for(int i=0; i<N; i++){
            int appWidgetId = appWidgetIds[i];
           
            //创建一个登录ExampleActivity的Intent
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context,0,intent,0);
           
            //取得App Widget的layout并为Button绑定一个点击监听器
            RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.appwidget_provider_layout);
            views.setOnClickPendingEvent(R.id.button, pendingIntent);
           
            //通知AppProviderManager在现在的appwidget上执行一次更新
            appWidgetManager.updateAppWidget(appWidgetId,views);
            }
        }
    }

这个AppWidgetProvider仅定义了onUpdate()方法,其目的是为定义一个用于登录一个Activity的PendingIntent,并且用setOnClickPendingEvent(int,PendingIntent)将其绑定到此AppWidget的Button。注意它包含了一个遍历appWidgetIds所有项的循环,appWidgetIds是一个定义了所有被这个provider创建的AppWidget的ID的数组。这样,如果用户创建了这个App Widget的不止一个的实例,那么他们会同时被更新。不过,对于所有的AppWidget实例,只有一个updatePeriodMillis 时间表被管理。比如,如果这个更新时间表被定义为每隔两个小时,而且AppWidget的第二个实例是在第一个后面一小时添加的,那么它们将按照第一个所定义的周期来更新而第二个被忽略(它们将都是每2个小时进行更新,而不是每小时)。

注意,因为AppWidgetProvider是BroadcastReceiver的扩展,不能保证你的进程在回调函数返回后仍然继续运行。如果你的AppWidget的设置过程会持续几秒钟(也许在执行web请求),并且你要求你的进程继续,那么可以考虑在onUpdate()方法里start一个Service,在里面你可以对AppWidget进行你自己的更新而不必担心由应用程序无响应错误导致AppWidgetProvider关闭。

接收App Widget广播Intent

AppWidgetProvider 只是一个简便类。如果你想直接接收App Widget广播,你可以实现自己的BroadcastReceiver 或者重写 onReceive(Context, Intent)回调函数。你需要注意的4个Intent如下:
       ACTION_APPWIDGET_UPDATE
       ACTION_APPWIDGET_DELETED
       ACTION_APPWIDGET_ENABLED
       ACTION_APPWIDGET_DISABLED

创建一个App Widget ConfigurationActivity

如果你希望用户在添加一个新的AppWidget时调整设置,你可以创建一个AppWidgetConfigurationActivity。这个Activity将会自动被AppWidget宿主启动,并且允许用户在App Widget创建时调整可用设置,比如颜色,大小,更新周期或者其他功能性设置。

Configuration Activity应该和普通Activity一样在Manifest中声明。但是,它会被AppWidget宿主用ACTION_APPWIDGET_CONFIGURE启动,所以这个Activity需要接收这个Intent。比如:

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

同时,这个Activity必须以android:configure参数在AppWidgetProviderInfoXML文件中声明,比如:

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

注意这个活动是用全名声明的,因为它将从你的程序包外被引用。

这就是你需要为开始ConfigurationActivity做的所有准备。现在你需要一个真实的Acativity。但是,当你实现这个Activity的时候还有两个重要的事情需要记住;

   1.AppWidget宿主调用Configuration Activity,然后ConfiguirationActivity必须返回一个结果,包括由启动这个Activity的Intent传递来的App WidgetID(用EXTRA_APPWIDGET_ID存储在Intent extras中)。

   2.AppWidget被创建的时候不会调用onUpdate()方法(当ConfigurationActivity被启动的情况下系统不会发送ACTION_APPWIDGET_UPDATE广播)。AppWidget第一次被创建的时候,ConfigurationActivity负责从AppWidgetManager请求一次更新。但是,接下来的更新会调用onUpdate()——它只是跳过了第一次而已。

从Configuration Activity更新App Widget

在App Widget使用Configuration Activity的情况下,当配置完成后由ConfigurationActivity负责更新App Widget。你可以通过直接从AppWidgetManager请求一次更新来实现。

下面是恰当地更新App Widget和关闭Configuration Activity的程序的总结:

   1.首先,从启动这个Activity的Intent获得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.执行你的AppWidget配置

   3.配置完成后,调用getInstance(Context)获得一个AppWidgetManager的实例:

        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

   4.调用updateAppWidget(int, RemoteViews),用一个RemoteViews布局来更新AppWidget。

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

   5.最后,创建要返回的Intent,设为这个Activity的返回值,然后finish这个Activity。

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

提示:第一次打开你的ConfigurationActivity时,把返回值设为RESULT_CANCELED。这样,如果用户在Activity运行完前退出了,AppWidget宿主会被告知配置被中断,从而不会加入App Widget。

设置预览图片

Android3.0引入了previewImage域,指定了App Widget长相的预览。预览将会从widgetpicker展示给用户,如果不支持,则会使用App Widget的图标。

下面是你如何在XML中进行设置:

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

为了帮助替你的AppWidget创建预览图片(指定previewImage域),android模拟器包含了一个叫“WidgetPreview”的应用程序。启动这个应用程序以创建一个预览图片,选择你的应用程序的AppWidget,设置你希望你的预览图片出现的方式,然后保存并放到你的应用程序的drawable资源里。

Using App Widgets withCollections暂不涉及
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值