app Widget 介绍及其例子一

Android—AppWidget

一.什么是AppWidget

      Android帮助文档中将所有的控件都叫做Widget,实际上我们平常说的Widget都是泛指AppWidget,即在手机桌面上放置的控件,比如时钟,日历等。具体定义如下:

       应用程序窗口小部件(Widget)是微小的应用程序视图,可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。

二.操作

      通过在HomeScreen中长按,在弹出的对话框中选择Widget部件来进行创建,长按部件后并拖动到垃圾箱里进行删除。同一个Widget部件可以同时创建多个。
三.基本概念

1.AppWidgetProviderInfo对象

      这个对象为AppWidget提供元数据,包括布局、更新频率等信息,这个对象定义在xml文件中,不需要自己生成,时系统自己生成的。

2.AppWidgetProvider

      这个类定义了AppWidget的基本生命周期函数,具体如下:

onReceive(Context, Intent)   接收广播事件
onUpdate(Context , AppWidgetManager, int[] appWidgetIds)  到达指定的更新时间或用户向桌面添加widget时候调用
onEnabled(Context)  当AppWidget实例第一次被创建时调用
onDeleted(Context, int[] appWidgetIds)  当AppWidget被删除时调用
onDisabled(Context)  当最后一个AppWidget被删除时调用

四.创建AppWidget的基本步骤

1.定义AppWidgetProviderInfo:在res中新建文件夹xml,在其中定义AppWidget_ProviderInfo.xml文件,主要设置的参数如下:
minWidth: 定义Wdiget组件的宽度
minHeight: 定义Wdiget组件的高度
updatePeriodMillis: 更新的时间周期
initialLayout: Widget的布局文件

代码示例:

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

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

      android:minWidth="80dp"  

      android:minHeight="32dp"  

      android:updatePeriodMillis="86400000"  

      android:initialLayout="@layout/widget_provider"   >  

</appwidget-provider> 

2.为AppWidget指定布局和样式,即此AppWidget在手机桌面上显示的样式,其实就是一个布局xml文件。需要注意的是使用的组件必须是RemoteViews所支持的,目前原生API中支持的组件如下:
FrameLayout
LinearLayout
RelativeLayout

AnalogClock
Button
Chronmeter
ImageButton
ImageView
ProgressBar
TextView
如果使用了除此之外的组件,则在Widget创建时会导致android.view.InflateExceptionn异常。这就导致有一些功能或样式无法实现,如很基本的list或文本编辑框都是无法直接实现的。

3.实现AppWidgetProvider。

      覆写其中的回调函数,后边会详细介绍。

4.在AndroidManifest.xml中声明(用receiver标签声明)

<receiver android:name="MyWidgetProvider">  //名字就是自己写的AppWidgetProvider

            <intent-filter>  

                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>   //接收什么样的广播,默认的action有4

                                                                                                                   //种,对应除onReceive外的4种生命周期函数,也可以自己写。

            </intent-filter>  

            <meta-data android:resource="@xml/AppWidget_ProviderInfo"  //在xml文件夹下建立的AppWidgetProviderInfo文件

                               android:name="android.appwidget.provider"></meta-data>   //name固定这么写

 </receiver>  

五.在AppWidget中使用控件

1.一个重要的概念

      AppWidget和其原本的App并不在同一个进程中,而是运行在HomeScreen进程当中,因此,在控件监听器的绑定,更新等操作都会与以前基本的方法有所不同。

2.PendingIntent

(1)概念

      PendingIntent是一个特殊的Intent,实际上它像一个邮包,其中包裹着真正的Intent,当邮包未打开时,Intent是被“挂起”的,所以并不执行,只有当邮包拆开时才会执行。

(2)执行过程

      进程A创建PendingIntent,发送给进程B,进程B此事并不执行,直到用户出发某一事件时,包裹被拆开,里面的Intent真正执行。所以Intent什么时候被执行是不知道的,由用户出发事件来决定,整个过程类似回调函数的原理。

(3)创建

有三种方法创建PendingIntent:

getActivity(Context context,int requestCode,Intent intent,int flags)        这时的PendingIntent作用是启动一个新的Activity

getBroadcast(Context context,int requestCode,Intent intent,int flags)   这时的PendingIntent作用是发送一个广播

getService(Context context,int requestCode,Intent intent,int flags)       这时的PendingIntent作用是启动一个Service

3.RemoteViews

(1)RemoteViews表示了一系列view对象,即AppWidget所有的控件。

(2)RemoteViews所标示的对象都运行在另外的进程中。

(3)应用

      RemoteViews.setOnClickPendingIntent(R.id.widgetButtonId,pendingIntent);      //第一个参数就是需要绑定的控件,

                                                                                                                                 //第二个参数是点击后触发执行的PendingIntent。

4.为控件绑定监听器的实现(点击按钮,开启新的Activity)

   @Override  

    public void onUpdate(Context context, AppWidgetManager appWidgetManager,  

            int[] appWidgetIds) {      //appWidgetManager用来管理appWidget,而appWidgetIds[]指的是,每当新建一个appWidget,

                                                  //系统就会给新建的appWidget一个ID,而这个数组中储存的就是这些ID。

        // TODO Auto-generated method stub   

        final int N = appWidgetIds.length;  

        for (int i = 0; i < N; i++) {  

         //创建Intent 对象

         Intent intent=new Intent(context,TargetActivity,.class);

         //创建PendingIntent  对象,即把intent装进pendingIntent中

         PendingIntent  pendingIntent=new PendingIntent.getActivity(context,0,intent,0);

         //通过布局文件得到RemoteViews 对象

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

         //绑定监听事件,即当widgetButtonId控件有onClick事件时,会执行pendingIntent(就是之前所说的拆邮包)

         remoteViews.setOnClickPendingIntent(R.id.widgetButtonId,pendingIntent); 

         //使用appWidgetManager更新AppWidge。第一个参数指明更新的是哪一个appWidgetId,第二个参数指明了更新的远程Views。

         appWidgetManager.updateAppWidget(appWidgetIds[i],remoteViews);    

        }  

         super.onUpdate(context,appWidgetManager,appWidgetIds);

    }  

 六.接收AppWidget中的广播(AppWidget与AppWidgetProvider的互动)

1.机制

      其实,AppWidgetProvider就是一个广播接收器。在AppWidgetProvider中,pendingIntent传来后由onReceive方法接收,之后检查intent对象中的action,由action来决定分发给哪一个函数。Android默认设置了四种action,对应了除onReceive外剩下的四种方法。因此,我们可以理解为AppWidgetProvider中的方法其实又依赖于onReceive。

2.实现步骤

(1)在AndroidManifest.xml中为AppWidgetProvider注册intent-filter,如前介绍。

(2)使用getBroadcast方法创建一个PendingIntent对象。

(2)为AppWidget中的控件注册处理器(即绑定监听器)。

(4)在onReceive方法中接收广播消息。

3.代码示例

  @Override  

    public void onUpdate(Context context, AppWidgetManager appWidgetManager,  

            int[] appWidgetIds) {     

        // TODO Auto-generated method stub   

        final int N = appWidgetIds.length;  

        for (int i = 0; i < N; i++) {  

         Intent intent=new Intent();

         intent.setAction(UPDATE_ACTION);

         //创建PendingIntent  对象,即把intent装进pendingIntent中,当pendingIntent执行的时候会发送一个广播

         PendingIntent  pendingIntent=new PendingIntent.getBroadcast(context,0,intent,0);

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

         remoteViews.setOnClickPendingIntent(R.id.widgetButtonId,pendingIntent); 。

         appWidgetManager.updateAppWidget(appWidgetIds[i],remoteViews);    

        }  

         super.onUpdate(context,appWidgetManager,appWidgetIds);

    }  

      之后覆写onReceive方法,实现接收广播后需要执行的操作即可(如“七”将要介绍)。当点击button时,会触发pendingIntent的执行,这时候会发送一个广播,而onReceive会接收广播并执行相应操作。

七.更新AppWidget中控件的状态

1.AppWidget中的更新操作是使用RemoteViews的一系列方法进行的。(比如更换图片:RemoteViews.setImageViewResource等)

2.更新后应记得使用AppWidgetManager通知AppWidget进行更新。

3.代码示例

说明:这里实现的即是点击某按钮,发送一个广播,在onReceive方法里实现更新操作,即需要覆写onReceive方法。

  @Override  

    public void onReceive(Context context,Intent intent) {     

           String action=intent.getAction();

           if(UPDATE_ACTION.equals(action)){

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

                   //将id为imageId的控件中的图片更新为image1

                   remoteViews.setImageViewResource(R.id.imageId,R.drawable.image1);

                   AppWidgetManager  appWidgetManager=AppWidgetManager.getInstance(context);

                   ComponentName componentName=new ComponentName (context,MyAppWidgetProvider.class); 

                  //通知AppWidgetProvider更新

                  appWidgetManager.updateAppWidget(componentName,remoteViews);    

           }  else{

                  super.onReceive(context,intent);

     }  

}

 下面介绍一个简单的例子

首先在清单文件声明AppWidgetProvider

 <receiver android:name="ExampleAppWidget" >
            <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>标签用于指明App Widget使用的AppWidgetProvider

<intent-filter>标签必须包括一个含有android:name属性的<action>标签。该属性用于指明AppWidgetProvider接收APPWIDGET_UPDATE广播。这是你唯一需要显示声明的广播。当有需要时,AppWidgetManager自动发送AppWidgetProder所需的各种广播。

<meta-data>标签标识了AppWidgetProviderInfo资源,它需要以下属性:

    android:name:使用android.appwidget.provider来标识AppWidgetProviderInfo。

   android:resource:标识AppWidgetProviderInfo的资源位置。


然后

添加AppWidgetProviderInfo元数据

AppWidgetProviderInfo定义了一个App Widget的必要属性,例如最小布局范围、初始布局、更新频率、以及在创建时显示的配置Activity(可选)。

AppWidgetProviderInfo使用<appwidget-provider>标签来定义,并保存在res/xml文件夹中。

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/example_appwidget"
    android:minHeight="72dp"
    android:minWidth="294dp"
    android:updatePeriodMillis="86400000" >
</appwidget-provider>

l minWidth与minHeight属性表示了App Widget所需的最小布局区域。

默认的主屏中,App Widget要想确认其位置,需要通过基于网格的具有固定宽度和高度的单元。如果App Widget的最小宽度和高度无法匹配给定的单元,它将会自动扩展到最接近的单元大小。

由于主屏的布局方向是可变的,你应该考虑最坏的情况(每单元的宽和高都是74dp)。然而,为了防止在扩展时产生整数计算错误,你还需要减去2。因此,你可以用以下公式来计算最小宽度和高度(单位dp):(单元数量×74)-2。

同时,为了保证你的App Widget能够在各种设备上正常使用,它们的宽度和高度必须不超过4×4个单元。

        lupdatePeriodMillis属性定义了App Widget框架调用AppWidgetProvider的onUpdate方法的频率。对于实际的更新,我们不建议采用该值进行实时处理。最好是越不频繁越好——为了保证电量,一小时不超过一次为好。当然,你也可以允许用户对更新频率进行设置。

注意,如果更新触发时设备正处于休眠状态,设备将唤醒以执行该操作。如果你的更新频率不超过一小时一次,这不会对电池的寿命产生多大的影响。但如果你需要更频繁地更新却又不想要在设备休眠时执行,那你可以使用定时器来执行更新。要达到这种目的,可以在AlarmManager中设置一个AppWidgetProvider能接收的Intent。将类型设为ELAPSED_REALTIME或RTC。由于AlarmManager只有当设备处于唤醒状态时才会被调用,我们只要设updatePeriodMillis为0即可。

        linitialLayout属性标识了初始布局文件。

        lconfigure属性定义了当用户添加App Widget时调用的Activity。(这是可选的)

        lpreviewImage定义了App Widget的缩略图,当用户从widget列表中选择时,显示的就是这张图。如果没设置,用户将看见的是你的应用的默认图标。

        lautoAdvanceViewId属性是在Android3.0引入的,用于标识需要被host(launcher)自动更新的widget的子视图。

        l resizeMode属性标识了widget重新布局的规则。你可以使用该属性来让widget能够在水平、竖直、或两个方向上均可变化。可用的值包括horizontal、vertical、none。如果是想在两个方向上均能拉伸,可设置为horizontal|vertical,当然,需要Android3.1以上版本。

创建App Widget的布局

这里很简单只有一个Textview
<?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="widget"
        android:background="#00ffff"
       />

</LinearLayout>

要创建你的App Widget的初始布局,你可以使用以下View对象。

创建布局不是很麻烦,重点是,你必须记住,这个布局是基于RemoteViews的,不是所有的布局类型与View都支持。

一个RemoteViews对象可以支持以下布局类:

FrameLayout

LinearLayout

RelativeLayout

以及一下widget类

AnalogClock

Button

Chronometer

ImageButton

ImageView

ProgressBar

TextView

ViewFlipper

注:这些类的子类是不支持的。

为App Widget添加边距

作为一个widget,它不应该扩展到屏幕的边缘,同时在视觉上,不应该与其它widget相混淆。因此,你需要为你的widget在四个方向上添加边距。

在Android4.0中,所有的widget都自动添加了内边距,并且提供了更好的对齐方案。要利用这种优势,建议设置应用的targetSdkVersion到14或更高。

为了支持以前的设备,你也可以为早些的平台写一个包含边距的布局,而对于Android4.0以上的,则不设置:

        1.       设置targetSdkVersion到14或更高

        2.       创建一个布局:


最后

使用AppWidgetProvider类

AppWidgetProvider类继承了BroadcastReceiver,它只监听与App Widget有关的广播事件,如更新、删除、启用、禁用。当这些事件发生时,AppWidgetProvider将接收到以下事件:

        l onUpdate()

由updatePeriodMills定义的时间间隔触发。当然,添加Widget的时候也会,因此,应该在此处执行一些必要的配置,如定义View的事件处理handler,有必要的话,还会启动一个临时的Service。然而,如果你声明了一个配置Activity,该方法将不会在此时被调用。

  l onDelete(Context, int[])

    当App Widget从host中移除时会被调用。

       l  onEnabled(Context)

    当App Widget的实例被第一次创建时,该方法将被调用。如果你建了两个实例,那该方法也只会被调用一次。

       l  onDisabled(Context)

    当最后一个App Widget的实例被删除时,该方法被调用。你可以在此处清除之前在onEnabled里执行的操作。

         l onReceive(Content, Intent)

      每次接收到广播都会被调用,而且执行的顺序在上述方法之前。通常,你不需要实现该方法,因为AppWidgetProvider已经对各种不同类型的广播进行了过滤及分发。

注:在Android1.5中,有一个已知的问题,使得在某些情况下,onDeleted不会被调用。这时,你就需要实现onReceive()了。

AppWidgetProvider的最重要的回调方法是onUpdate。如果你的App Widget不需要创建任何临时文件或数据库,或执行任何的清理工作,这应该是你唯一需要定义的。比如说,如果你的App Widget只包含一个按钮,它的功能仅仅是当按钮点击时打开一个Activity,你可以使用以下的实现:

public class ExampleAppWidget extends AppWidgetProvider {

	@Override
	public void onDeleted(Context context, int[] appWidgetIds) {
		// TODO Auto-generated method stub
		super.onDeleted(context, appWidgetIds);
	}

	@Override
	public void onDisabled(Context context) {
		// TODO Auto-generated method stub
		super.onDisabled(context);
	}

	@Override
	public void onEnabled(Context context) {
		// TODO Auto-generated method stub
		super.onEnabled(context);
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		super.onReceive(context, intent);
	}

	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
			int[] appWidgetIds) {
		// TODO Auto-generated method stub
		super.onUpdate(context, appWidgetManager, appWidgetIds);
	}
	
}



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值