创建主屏幕控件----App Widget

创建屏幕控件----App Widget

        Android SDK给开发人员提供了可以在移动应用程序的传统边界外面提供功能,这就是App Widget。开发人员可使用App Widget API创建可加入到主屏幕中的小型控件。这些控件简单但功能强大,可向用户提供有关应用程序的补充信息,并提醒用户在必要时启动应用程序。

       对有些类型的应用程序(如需要将状态或更新告知用户的应用程序)来说,App Widget很有用。天气应用程序可能包含这样的App Widget,即显示指定地区的当前天气状况;任务管理应用程序可能包含这样的App Widget,即告诉用户其待办事项中的下一个事项是什么以及当前还有多少个事项要办;图库应用程序可能包含这样的App Widget,即以幻灯片的方式列出图库中所有的图片。

要实现简单的App Widget,必须利用前面学习的很多技能。创建App Widget的步骤如下:

1、创建App Widget配置文件;

2、创建App Widget布局资源文件;

3、实现App Widget提供器;

4、实现Android服务,在必要时更新App Widget;

5、在Android清单文件中注册App Widget和相关的服务。

下面我们来更详细地学习每个步骤:

1、配置App Widget的属性

       必须在一个独立的XML文件中指定App Widget的定义和配置属性,然后在Android清单文件中引用他们。下面是一些用于定义App Widget的常见属性:

       大小:App Widget的宽度和高度,以独立于密度的像素(dp或dip)为单位,它对应于正确显示App Widget需要的主屏幕网格单元数。Android主屏幕被划分成网格单元格,每个单元格为74×74像素。在每个单元格中,只能有一项内容,如App Widget或应用程序快捷方式,这可确保各项内容不会重叠。

       更新频率:系统两次调用App Widget提供器来更新App Widget的内容之间相隔的时间,单位为毫秒。

       初始布局:最初添加App Widget时使用的布局文件,以后可使用代码对初始布局进行修改。

       配置活动:首次显示App Widget前将启动的活动,该活动将配置App Widget的各个方面。

首先在资源文件夹/res/xml中添加一个名为example_appwidget_info.xml的XML文件:

<?xml version="1.0" encoding="utf-8"?>

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

       android:minWidth="60dp"

       android:minHeight="30dp"

       android:updatePeriodMillis="10000"

       android:initialLayout="@layout/example_appwidget" >

</appwidget-provider>

 

注意,Google给了这样一个说明:

public int updatePeriodMillis
Since: API Level 3

How often, in milliseconds, that this AppWidget wants to be updated. The AppWidget manager may place a limit on how often a AppWidget is updated.

This field corresponds to the android:updatePeriodMillis attribute in the AppWidget meta-data file.

Note: Updates requested with updatePeriodMillis will not be delivered more than once every 30 minutes.

这说明android:updatePeriodMillis="1080000"App Widget最短更新间隔为30分钟,所以updatePeriodMillis如果设置小于30分钟,App Widget将默认30分钟执行一次。

       这个文件定义了一个App Widget,它每隔3小时更新一次,大小为4×2个单元格。如果您进行计算就会发现我们定义的宽和高不是前面定义的网格单元格大小74dp的整数倍,原因是虽然通常认为网个单元格大小为74dp,但计算App Widget的大小时,必须将最终结果减去2,例如74×2 = 148,减去2后为146。如果不这样做,App Widget占据的单元格数可能不符合预期。

       另外,这个App Widget最初使用一个预定义布局,这是使用android:initialLayout = “@layout/widget”指定的。下面我们创建这个布局,它表示App Widget的用户界面。

2、创建App Widget布局资源文件

       App Widget有独特的布局要求。首先App Widget是通过接口RemoteViews绘制的,这限制了可显示的用户界面控件的类型;其次App Widget必须与其定义中配置的大小一致。当视图由另一个进程显示时,将使用RemoteViews对象。App Widget正是这样的,他是在App Widget宿主进程而不是在应用程序主进程中显示的。RemoteViews对象可以使用的布局和视图对象收到了限制,下面是可以再App Widget中使用的布局和视图对象:

LinearLayout;

FrameLayout;

RelativeLayout;

TextView;

ImageView;

Button;

ImageButton;

ProgressBar;

AnalogClock;

Chronometet。

       不能使用这些控件的子类,这意味着布局的设计受到限制。一种改善App Widget功能的典型的方式是,在需要更强大的功能或更复杂的屏幕时启动一个应用程序活动。

下面我们开始设计App Widget的布局,创建一个布局文件/res/layout/example_appwidget.xml:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

       android:layout_width="match_parent"

       android:layout_height="match_parent"

       android:orientation="vertical" >

       <TextView 

              android:id="@+id/widgetTextId"

              android:layout_width="match_parent"

              android:layout_height="wrap_content"

              android:text="firstWidgetText"

              android:background="#000000"/>

</LinearLayout>

 

3、实现App Widget提供器

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

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

onUpdate(Context , AppWidgetManager , int[])

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

onDeleted(Context , int[])

       当App Widget从宿主中删除是被调用;

onEnabled(Context)

       当一个App Widget实例第一次创建时被调用。比如,如果用户添加两个你的App Widget实例,只在第一次被调用。如果你需要打开一个新的数据库或者执行其他对于所有的App Widget实例只需要发生一次的设置,那么这里是完成这个工作的好地方。

onDisabled(Context)

       当你的App Widget的最后一个实例被从宿主中国删除时被调用。你应该在onEnabled(Context)中做一些清理工作,比如删除一个临时的数据库。

onReceive(Context , Intent)

       当接收到每个广播时都会被调用,而且在上面的回调函数之前。你通常不需要实现这个方法,因为缺省的AppWidgetProvider实现过滤所有App Widget广播并恰当的调用上述方法。

最重要的AppWidgetProvider回调函数是onUpdated(),因为它是在每个App Widget添加进宿主时被调用的(除非你使用一个配置活动)。如果你的App Widget要接受任何用户交互事件,那么你需要在这个回调函数中注册时间处理器。如果你的App Widget不创建临时文件或数据库,或者不执行其他需要清理的工作,那么onUpdated()可能是你需要定义的唯一的回调函数。比如你想要一个带按钮的App Widget,当点击时启动一个活动,你可以使用下面的AppWidgetProvider实现:

package com.example.test04;

import java.util.Calendar;

import android.appwidget.AppWidgetManager;

import android.appwidget.AppWidgetProvider;

import android.content.Context;

import android.content.Intent;

import android.util.Log;

import android.widget.RemoteViews;

public class ExampleAppWidgetProvider extends AppWidgetProvider

{

       @Override

       public void onEnabled(Context context) 

       {

              super.onEnabled(context);

              Log.i("输出", "onEnabled...正在执行");

       }

       @Override

       public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) 

       {

              Log.i("输出", "onUpdate...正在执行");

              super.onUpdate(context, appWidgetManager, appWidgetIds);

              Log.i("输出", "onUpdate...正在执行");

       }

       @Override

       public void onDisabled(Context context) 

       {

              super.onDisabled(context);

              Log.i("输出", "onDisabled...正在执行");

       }

       @Override

       public void onDeleted(Context context, int[] appWidgetIds) 

       {

              super.onDeleted(context, appWidgetIds);

              Log.i("输出", "onDeleted...正在执行");

       }

       @Override

       public void onReceive(Context context, Intent intent) 

       {

              super.onReceive(context, intent);

              Log.i("输出", "onReceive...正在执行");

       }


      这个AppWidgetProvider仅定义了onUpdate()方法,为了定义一个PendingIntent,来启动一个活动并使用setOnClickPendingIntent(int , PendingIntent)方法把他附着到这个AppWidget的按钮上。注意它包含了一个遍历appWidgetIds中所有项的循环,这是一个Ids数组,每个ID用来标识由这个Provider创建的一个App Widget。这样,如果创建多余一个这个App Widget的实例,那么它们将被同步更新。不过,对于所有的App Widget实例,只有一个updatePeriodMillis时间表被管理。比如,如果这个更新时间表被定义为每隔两个小时,而且App Widget的第二个是在第一个后面一小时添加的,那么它们将按照第一个所定义的周期来更新而第二个被忽略。

注意:因为这个AppWidgetProvider是一个广播接收器BroadcastReceiver,不能保证你的进程在回调函数返回后仍然继续。

 

4、处理App Widget后台任务

     对于持续时间很长的操作,常规的解决方案是以异步方式执行它们。然后,对于App Widget,这种解决方案不可行,因为没有管理这种任务的底层活动。App Widget所属的进程随时可能终止,即使正在执行异步任务。因此,必须创建一个Android Service对象,并在服务中执行所需的后台操作。

     

  @Override

       public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) 

       {

              Log.i("输出", "onUpdate...正在执行");

              updateAppWidget(context, appWidgetManager , appWidgetIds);

              super.onUpdate(context, appWidgetManager, appWidgetIds);

              Log.i("输出", "onUpdate...正在执行");

       }

       public static void updateAppWidget(Context context , AppWidgetManager appWidgetManager , int[] appWidgetIds)

       {

              RemoteViews remoteViews = new RemoteViews(context.getPackageName() , R.layout.example_appwidget);

              remoteViews.setTextViewText(R.id.widgetTextId, Calendar.getInstance().getTime() + "");

              Log.i("输出", Calendar.getInstance().getTime() + "");

              appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);

       }


5、在Android清单文件中注册App Widget和相关的服务

<receiver android:name="com.example.test04.ExampleAppWidgetProvider">

                     <intent-filter>

                            <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>

                     </intent-filter>

                     <meta-data 

                            android:name="android.appwidget.provider"

                            android:resource="@xml/example_appwidget_info">

                     </meta-data>

              </receiver>

 

源码下载地址:源码下载

你可能感兴趣的:(创建主屏幕控件----App Widget)