翻译官方文档的widget文章(未完成)

原地址:
http://developer.android.com/guide/topics/appwidgets/index.html

我拙劣的英文加上谷歌翻译。。。。

=============LET`S ROLL===============================

------------------------------------------
widget

应用程序Widgets是可以嵌入在其他应用程序(如主屏幕)的小型应用程序视图,并接受定期更新。在用户界面,这些views被称为小工具,你可以使用App Widget provider发布应用部件。一个能够容纳其他应用程序的小工具的应用程序组件,被称为App Widget host。下面是音乐小部件。


------------------------------------------
The Basics

要建立一个小部件,你需要如下东东:

AppWidgetProviderInfo 对象
    描述Widget的metadata信息, 比如App Widget的layout, update frequency(升级频率), 还有AppWidgetProvider类. 这个东东应该定义在一个xml文件中。(我注:res/xml)

实现 AppWidgetProvider小部件提供者
在监听广播的基础上,定义widget的基本方法,允许你编写widget的程序接口。
通过此类,你可以收听到这个widget被updated, enabled, disabled and deleted的广播。


View layout
    在xml文件中定义的widget初始布局(我注:res/layout)。

还有,你可以编写一个widget configuration(设置) activity。当用户第一次建立这个widget时,会跳转到这个activity,来配置activity(我注:类似于活动壁纸的配置)。

以下部分就是描述怎样实现这些组件

----------------------------------------------
在Manifest中声明小部件

第一步 在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>


<receiver>元素必须有name属性,才能为widget指定provider

<intent-filter>元素必须有带name属性的<action>元素。这样才能指定接收到更新widget的广播。这是你唯一需要明确声明的的广播。有必要的话,AppWidgetManager会自动的发送所有其他widget的广播给AppWidgetProvider。

<meta-data> 元素指定了AppWidgetProviderInfo资源并要求如下属性:

android:name - 指定 metadata 名称. 使用android.appwidget.provider来标识描述AppWidgetProviderInfo的数据。
android:resource - 指定AppWidgetProviderInfo资源的位置.
(看来要把原文附上了。土逼啃啼妞。。。。。)


---------------------------------------------------------------------------
Adding the AppWidgetProviderInfo Metadata

AppWidgetProviderInfo 定义了widget的一些必须的数值,比如最小长宽,初始化的布局,更新频率( 我注:Android 1.6以后不可用,强制最小更新时间30分钟以上,可以自己写service,注意省电),(可选的)配置activity。
在res/xml用一个xml文件中的<appwidget-provider>元素来定制AppWidgetProviderInfo。

举例:
<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>


----------------
这是<appwidget-provider>中属性的概述
minWidth和minHeight定义了widget在主屏上默认占用的宽和高。默认主屏是由定义好长宽的单元格组成的。如果最小长宽不能匹配单元格,widget坐标会包含最近的一个单元格。
关于widget的大小信息可以参见 App Widget Design Guidelines

注:为了能让你的widget在竖屏的设备工作,widget的大小不应大于4*4单元。

minResizeWidth和minResizeHeight
指定了widget的绝对最小尺寸。这两个值应该比不可用或者非法的值小。这两个属性允许用户让widget的大小比指定的最小值还要小。在android3.1中有介绍。
A App Widget Design Guidelines 查看更多关于widget尺寸的信息。

updatePeriodMillis属性
决定了widget的AppWidgetProvider的update()方法多长时间被回调一次。为了电池的寿命我们建议越少更新越好,最好间隔大于1小时。你可以让用户来配置多长时间更新一次。
注:如果在休眠时遇到了updatePeriodMillis指定更新的时候,设备会被唤醒来更新。

如果更新间隔不小于1小时,那么不会引起电池寿命问题。如果你想在系统休眠时不更新,可以用alarm机制来更新,这样不会唤醒设备。如果你要这样做,设置一个AppWidgetProvider能收到的带intent的alarm。将alarm类型设置成 ELAPSED_REALTIME或RTC,这样只有系统在活动状态才能传递alarm。然后把updatePeriodMillis设置为0(不更新)。

initialLayout属性指向的布局资源决定了widget的布局。

configure属性决定了添加widget时用户设置widget的activity,这是可选的。(可参考[url=http://developer.android.com/guide/topics/appwidgets/index.html#Configuring][/url])

previewImage属性
指定了当用户在widget列表里选择时,widget的预览图。不提供的话,会用应用的图标代替。这个属性对应了 AndroidManifest.xml中widget 的provider
的android:previewImage属性。更多可参考Android 3.0的介绍。

autoAdvanceViewId属性
指定了小部件建立时自动升级的子view的view ID。(我注:可能有误,因为没用过这个东东)。

resizeMode属性
指定了widget可否被重设大小。你可以用这个属性让小部件在桌面上横、竖或都能重设大小。用户可以按住小部件,横或者竖拖动来重设小部件在网格中的大小。

resizeMode属性
包括“horizontal” "vertical""none""horizontal|vertical"
Android 3.1中有介绍。

以下为Android 4.0以后更新的属性

minResizeHeight 属性
指定了小部件可以调整的最小高度(dp为单位)。如果这个数值比minHeight 大或者resizeMode不是vertical resizing ,则这个数值无效。

minResizeWidth 属性
指定了小部件可以调整的最小宽度(dp为单位)。如果这个数值比minWidth 大或者resizeMode不是horizontal resizing ,则这个数值无效。


widgetCategory 属性  4.2新增
申明了是那种appWidget,桌面上的,锁屏的,或者都是。
值有 "home_screen" "keyguard"
如果要两种都显示,要确保符合两种widget类的设计。具体可以参考:
http://developer.android.com/guide/topics/appwidgets/index.html#lockscreen
默认值是 "home_screen"

initialKeyguardLayout 属性 4.2新增
指向锁屏widget的布局。与android:initialLayout工作的方式相同,都是在appWidget初始化和更新之前,能马上显示的一个布局。

查看 AppWidgetProviderInfo 类来获得更多详细信息。

---------------------------------------------------------------------------

建立widget的布局
你必须在res/layout中定义小部件的初始布局。你可以使用如下控件,但请先阅读
App Widget Design Guidelines.

你必须知道小部件的布局都是基于RemoteViews,它并不支持所有种类的布局或者控件。一个RemoteViews对象(也可一说widget)支持如下类型的布局:

FrameLayout
LinearLayout
RelativeLayout
GridLayout


和如下类型的控件:

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

上述以外都不支持( 我注:重要。但是随着Android的升级,上述列表不断的在增加。


为小部件添加边距

为了不和其他小部件以及屏幕接上,你应该为小部件添加边距。

在Android4.0中,系统会自动给小部件们添加边距。为了利用这个推荐的功能,你可以把targetSdkVersion设为14以上。(我注:扯淡。。。。)

为了早期的平台,写一个带有自定义边距的单独的布局是相对容易的,并且对于4.0以上的版本没有额外的边距:

设置targetSdkVersion为14以上。建立下面布局中的其中一个,带有相关边距尺寸的:


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


建立两个尺寸资源,一个在res/values/为了Android4.0以前的版本,一个在res/values-v14/,没有额外边距的Android4.0小部件。


    res/values/dimens.xml:

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

    res/values-v14/dimens.xml:

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

另一种方法是在不同的背景图片上直接作出边距,然后适应不同的版本。


---------------------------------------------------------------------------

使用AppWidgetProvider类

AppWidgetProvider类继承自BroadcastReceiver,用来处理widget的广播。
AppWidgetProvider只接受关于widget的广播,比如 更新,删除,可用,不可用。当收到广播后,会回调下列方法:

onUpdate()

此方法根据AppWidgetProviderInfo定义的updatePeriodMillis来更新widget。当用户添加widget时,也会调用这个方法。所以这个方法应该实现必要的安装,设置时间处理句柄,启动service等。但是,如果你已经定义了widget的配置activity,那么添加widget时不会调用这个方法,之后的更新才会调用。

onAppWidgetOptionsChanged()
这个方法在widget第一次放置好和每次widget调整大小时调用。
你可以利用这个方法依靠widget的尺寸来显示或隐藏一些内容。通过getAppWidgetOptions()返回的Bundle来获得尺寸范围,bundle还包括(下面四个不翻了,比较直白):

OPTION_APPWIDGET_MIN_WIDTH—Contains the lower bound on the current width, in dp units, of a widget instance.
OPTION_APPWIDGET_MIN_HEIGHT—Contains the lower bound on the current height, in dp units, of a widget instance.
OPTION_APPWIDGET_MAX_WIDTH—Contains the upper bound on the current width, in dp units, of a widget instance.
OPTION_APPWIDGET_MAX_HEIGHT—Contains the upper bound on the current width, in dp units, of a widget instance.

这个回调添加于4.1.更老版本的设备不会调用这个方法。


onDeleted(Context, int[])
    从App Widget host删除后调用。
onEnabled(Context)
    widget实例第一次create之后调用。比如,用户添加两个同样的widget,这个方法只在第一次添加时调用。如果你想在第一次添加widget时新建个数据库或者设置什么,这个方法比较合适。

onDisabled(Context)
    最后一个widget实例被App Widget host删除时调用。可以做一些与onEnabled(Context)对应的清理工作。

onReceive(Context, Intent)
    每次收到广播,这个方法会比上面那些方法更先调用。一般你不用实现这个回调,因为AppWidgetProvider 会自己过滤广播,然后适当的调用上面那些方法。


最重要的回调就是onUpdate() 。如果你的widget接受UI交互,你应该在onUpdate()里处理。如果你不需要建立一个临时文件或数据库,或者其他一些需要clean-up的工作,你可以只定义这一个回调。下面是一个widget例子,点击一个widget上的button启动一个activity:



public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;

        // 遍历所属这个ExampleAppWidgetProvider 的所有widget
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

            // 获取widget的layout,绑定监听
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // 告诉AppWidgetManager来update当前这个widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}



虽然上面的onUpdate()给每个widget都单独加了回调,但是updatePeriodMillis 是统一的。比如updatePeriodMillis 是两个小时,第一个widget添加后一小时添加第二个widget,那么
第二个小部件的updatePeriodMillis 会被忽略,第二个widget的更新时间与第一个相同。



由于AppWidgetProvider 是BroadcastReceiver的子类,所以不能在里面跑耗时的应用。如果需要做耗时的工作,考虑在onUpdate中启动一个Service。在service中你可以自己更新widget而不用担心ANR。案例: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.html

--------------------------------------------------

接收 App Widget broadcast Intents

AppWidgetProvider 只是一个提供方便的类。如果你想直接接收widget的广播,你可以自己写个BroadcastReceiver 然后在 onReceive(Context, Intent) 中处理回调。下面这几个Intent需要注意:

ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED



---------------------------------------------------------------
建立一个widget配置activity

如果你想在用户添加新的widget的时候,弹出一个配置activity,配置一些颜色、尺寸、更新时间。(我注:其实这个activity干吗都行)

首先,要在AndroidManifest.xml里面声明这个activity。activity需要过滤APPWIDGET_CONFIGURE这个action来接受intent。

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


appwidget-provider 也需要配置上对应哪个configure activity
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:configure="com.example.android.ExampleAppWidgetConfigure" 
    ... >
</appwidget-provider>

注意这个configure的声明要写activity的整个命名空间,因为有可能是别的包的activity。


以上是config activity的简单介绍。此外在实现activity时还有两个重点需要注意:
1.
App Widget host调用config activity 而且config activity应该每次都返回一个结果。结果应该包括widget ID(这个ID是从启动这个activity的intent中的extras获取的,KEY是EXTRA_APPWIDGET_ID)。
2.
系统在启动config activity之后,不会发送ACTION_APPWIDGET_UPDATE ,所以widget的onUpdate也不会被调用。所以config activity就有责任告诉AppWidgetManager 小部件已经第一次建立好了。之后,每次更新都会调用onUpdate(),只有第一次不会。

下面的代码段演示了怎样返回结果并更新widget。

从config activity中更新widget
config activity配置结束之后,应该直接调用AppWidgetManager更新widget。

1.

从启动activity的intent中获取widget ID


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




2.配置widget
3.配置结束后,初始化appWidgetManager 实例
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);


4.使用RemoteViews更新widget
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);


5.
建立返回结果的intent,设置返回值,结束activity
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();



注:当你的config activity第一次刚打开,先返回值设置为RESULT_CANCELED。这样,如果用户在配置完成之前退出,App Widget host 就会知道配置没有完成,这样widget就不会被添加。

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.html查看更多

----------------------------------------------------------------------------------
设置预览图

Android 3.0 加入了预览图的属性,在用户选择widget时候可以显示预览图。属性为空的话,就会默认使用widget的图标。
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
  ...
  android:previewImage="@drawable/preview">
</appwidget-provider>


Android模拟器提供了一个专门来生成widget预览图的应用"Widget Preview."(我注:真的有哦~)。在虚拟机上运行这个程序,选择widget,就可以生成相应的预览图。保存后可以直接放在资源中。

--------------------------------------------------------
让widget出现在锁屏界面上

Android 4.2之后,通过 AppWidgetProviderInfo. 的 android:widgetCategory( "home_screen" 或 "keyguard"或者两者都有) 属性,可以让小部件出现锁屏界面上。

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


当你使用了keyguard|home_screen之后,你可能会要为home和lockscreen分开定制不同的布局。这就需要实时在代码中可靠的获取widget所在的环境。通过 getAppWidgetOptions()返回的bundle包含了KEY  OPTION_APPWIDGET_HOST_CATEGORY,值有WIDGET_CATEGORY_HOME_SCREEN 或 WIDGET_CATEGORY_KEYGUARD。
在 AppWidgetProvider中,判断类型的例子如下:

AppWidgetManager appWidgetManager;
int widgetId;
Bundle myOptions = appWidgetManager.getAppWidgetOptions (widgetId);

// Get the value of OPTION_APPWIDGET_HOST_CATEGORY
int category = myOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);

// If the value is WIDGET_CATEGORY_KEYGUARD, it's a lockscreen widget
boolean isKeyguard = category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;



当你有了widget类型后,可以加载不同的布局
int baseLayout = isKeyguard ? R.layout.keyguard_widget_layout : R.layout.widget_layout;


你应该和使用android:initialLayout一样,使用android:initialKeyguardLayout 来给
widget加载完成之前放一个“加载中”布局。

----------------------------------------------------------------



尺寸方针

当widget被添加在锁屏上时,framework会忽略minWidth, minHeight, minResizeWidth, minResizeHeight 这些属性。在桌面加载才会需要这些属性。
widget的宽度会和所获得的区域宽度一样。
高度:
如果widget没有标记为 纵向尺寸可重置(android:resizeMode="vertical"),widget的高度会是“small”。
在手机的竖向模式,“small”是指解锁UI显示意外的空间。在平板和横向模式中,“small”是基于每个设备的。
如果标记了android:resizeMode="vertical",widget在手机竖屏解锁界面,显示出来的是“small”。其他情况下,widget的尺寸会填满可用的高度。

------------------------------------------------------------------------

使用集合

widget使用RemoteViewsService来让content provider.之类的后端集合数据显现。RemoteViewsService提交给widget数据分为以下几种形式:

ListView
竖向滚动list (如Gmail的widget)

GridView
二维滚动网格 (如收藏夹的widget)

StackView
类似于扑克牌的栈视图。

AdapterViewFlipper
一种基于adapter的ViewAnimator 。在两个或多个view之间动画切换。一次只能显示一个view。


As stated above, these collection views display collections backed by remote data. This means that they use an Adapter to bind their user interface to their data. An Adapter binds individual items from a set of data into individual View objects. Because these collection views are backed by adapters, the Android framework must include extra architecture to support their use in app widgets. In the context of an app widget, the Adapter is replaced by a RemoteViewsFactory, which is simply a thin wrapper around the Adapter interface. When requested for a specific item in the collection, the RemoteViewsFactory creates and returns the item for the collection as a RemoteViews object. In order to include a collection view in your app widget, you must implement RemoteViewsService and RemoteViewsFactory.

你可能感兴趣的:(widget)