App Widget应用程序窗口小部件-田少嵩

App Widget是应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。你可以通过一个App Widget Provider来发布一个Widget

AppWidgetProvider 继承自 BroadcastReceiver,它能接收 widget 相关的广播,例如 widget 的更新、删除、开启和禁用等

AppWidgetProvider中的广播处理函数如下:

onUpdate()
  widget 更新时被执行。
  同样,当用户首次添加 widget 时,onUpdate() 也会被调用,这样 widget 就能进行必要的设置工作(如果需要的话) 。但是,如果定义了 widget configure属性(android:config,后面会介绍),那么当用户首次添加 widget 时,onUpdate()不会被调用;之后更新 widget 时,onUpdate才会被调用。

onAppWidgetOptionsChanged()
  widget 被初次添加 或者 当 widget 的大小被改变时,执行onAppWidgetOptionsChanged()。你可以在该函数中,根据 widget 的大小来显示/隐藏某些内容。可以通过 getAppWidgetOptions() 来返回 Bundle 对象以读取 widget 的大小信息,Bundle中包括以下信息:
  OPTION_APPWIDGET_MIN_WIDTH -- 包含 widget 当前宽度的下限,以dp为单位。
  OPTION_APPWIDGET_MIN_HEIGHT -- 包含 widget 当前高度的下限,以dp为单位。
  OPTION_APPWIDGET_MAX_WIDTH -- 包含 widget 当前宽度的上限,以dp为单位。
  OPTION_APPWIDGET_MAX_HEIGHT -- 包含 widget 当前高度的上限,以dp为单位。

onAppWidgetOptionsChanged() Android 4.1 引入的。

onDeleted(Context, int[])
  widget 被删除时被触发。

onEnabled(Context)
  当第1widget 的实例被创建时触发。也就是说,如果用户对同一个 widget 增加了两次(两个实例),那么onEnabled()只会在第一次增加widget时触发。

onDisabled(Context)
  当最后1widget 的实例被删除时触发。

onReceive(Context, Intent)
  接收到任意广播时触发,并且会在上述的方法之前被调用。


同一个Widget部件可以同时创建多个。

1、首先需要提供一个定义了Widget界面布局的XML文件(位于res/layout/..),需要注意的是使用的组件必须是RemoteViews所支持的,目前原生API中支持的组件如下: 
FrameLayout 
LinearLayout 
RelativeLayout 
AnalogClock 
Button 
Chronmeter 
ImageButton 
ImageView 
ProgressBar 
TextView 

*如果使用了除此之外的组件,则在Widget创建时会导致android.view.InflateExceptionn异常。

2、新建AppWidgetProvderInfo。也就是提供一个xml文件来定义Widget的基本属性,放置到res/xml/..目录下。 


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

           android:configure="com.demo.widget.MyWidgetConfiguration" >  

     </appwidget-provider>  

minWidth: 定义Wdiget组件的宽度 
minHeight: 定义Wdiget组件的高度 
updatePeriodMillis: 更新的时间周期 
initialLayout:指定桌面组件的布局文件 
configure: 如果需要在启动前先启动一个Activity进行设置,在这里给出Activity的完整类名(后面会说到,与一般Activity的实现有些许差别) 

3接下来就是创建一个继承自AppWidgetProvider的子类,AppWidgetProvider实际上就是一个BroadcastReceiver,里面提供了以下函数: 

onReceive(Context, Intent) 
onUpdate(Context , AppWidgetManager, int[] appWidgetIds) 
onEnabled(Context) 
onDeleted(Context, int[] appWidgetIds) 
onDisabled(Context) 
可通过重写以上函数来监听Widget状态的变化并进行相应的处理。

public class MyAppWidgetProvider extends AppWidgetProvider {  

    private static final String CLICK_NAME_ACTION = "com.terry.action.widget.click";  

     public static final String TAG = "widget";   

     public static  RemoteViews rv;  

     /**  

     * 更新(Widget的更新与Activity不同,必须借助于RemoteViews和AppWidgetMananger。)  

    */    

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

           Log.i(TAG, "onUpdate");  

           int N = appWidgetIds.length; // 可能启动了多个Widget,appWidgetIds记录了这些Widget的ID    

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

               int appWidgetId = appWidgetIds[i];  

               rv = new RemoteViews(context.getPackageName(), R.layout.main);  

              Intent intentClick = new Intent(CLICK_NAME_ACTION);  

               PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,  

                       intentClick, 0);  

               rv.setOnClickPendingIntent(R.id.TextView01, pendingIntent);  

               appWidgetManager.updateAppWidget(appWidgetId, rv);  

           }     

     }    

     /**  

     * 第一个Widget组件启动时触发  

      */    

    public void onEnabled(Context context){    

           Log.i(TAG, "onEnabled");    

     }    

     /**  

      * 最后一个Widget组件关闭时触发  

      */    

     public void onDisabled(Context context){    

           Log.i(TAG, "onDisabled");    

     }    

     /**  

     * 任一Widget组件被删除时触发  

     */    

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

           Log.i(TAG, "onDeleted");    

    }    

     /**  

      * 以上函数触发前会先触发该函数,一般不需要重写  

      */    

    public void onReceive(Context context, Intent intent){    

           Log.i(TAG, "onReceive");    

           super.onReceive(context, intent);    

           if (rv == null) {  

              rv = new RemoteViews(context.getPackageName(), R.layout.main);  

          }  

          if (intent.getAction().equals(CLICK_NAME_ACTION)) {  

               rv.setTextViewText(R.id.TextView01, context.getResources()  

                      .getString(R.string.load));  

          }  

           AppWidgetManager appWidgetManger = AppWidgetManager.getInstance(context);  

           int[] appIds = appWidgetManger.getAppWidgetIds(new ComponentName(context, widgetProvider.class));  

           appWidgetManger.updateAppWidget(appIds, rv);  

    }       

 }    

 

其中需要注意的是,虽然RemoteViews参数都是一样的,但是对于每个Widget最好都新创建一个再进行传递,否则会导致一些错误。具体可参考AppWidget RemoteViews 内存溢出 。 

其他函数的可以根据需要实现。 

由于无法获取到RemoteViews创建的界面中的元素,对于Widget中组件的操作只能通过RemoteViews所提供的有限的函数进行,常用的有: 
setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) 
setProgressBar(int viewId, int max, int progress, boolean indeterminate) 
setTextViewText(int viewId, CharSequence text) 
setViewVisibility(int viewId, int visibility) 
详细函数列表可参考API中的RemoteViews类 

4、最后,更新AndroidManifest.xml。

<receiver android:name="MyWidgetProvider">    

<intent-filter>    

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

    <action android:name="com.terry.action.widget.click"></action>    

     </intent-filter>  

 <meta-data android:resource="@xml/widget_property" android:name="android.appwidget.provider"></meta-data>    

</receiver>  


5、提供Configuration Activity 
Configuration Activity是一个在Widget启动前先启动的Activity,方便用户对Widget的属性进行设置。 

res/xml/...下对应的"属性文件"中添加configure字段指定启动的Activity,并在AndroidManifest.xml中该Activity下提供一个action为android.appwidget.action.APPWIDGET_CONFIGURE 的IntenFilter。 

需要注意的是, 
如果设置了Configure属性,则必须在指定的Activity中进行如下处理: 
1.在onCreate中setContentView()函数前添加setResult(RESULT_CANCLE) ,这样如果在Activity初始化完成前按下了BACK按键,则Widget不会启动; 
2.在setContentView()函数之后(不一定要在onCreate中,在Activity退出前即可),添加如下设置以指定需要启动的Widget: 

int mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);    

Intent resultValue = new Intent();    

resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);    

setResult(RESULT_OK, resultValue);  

否则会导致退出ActivityWidget不启动。
3AppWidget 框架的主要类介绍  
 1) AppWidgetManger  

bindAppWidgetId(int appWidgetId, ComponentName provider) 通过给定的ComponentName 绑定appWidgetId  

getAppWidgetIds(ComponentName provider) 通过给定的ComponentName 获取AppWidgetId  

getAppWidgetInfo(int appWidgetId) 通过AppWidgetId 获取 AppWidget 信息  

getInstalledProviders() 返回一个List<AppWidgetProviderInfo>的信息  

getInstance(Context context) 获取 AppWidgetManger 实例使用的上下文对象  

updateAppWidget(int[] appWidgetIds, RemoteViews views) 通过appWidgetId 对传进来的 RemoteView 进行修改,并重新刷新AppWidget 组件 

updateAppWidget(ComponentName provider, RemoteViews views) 通过 ComponentName 对传进来的 RemoeteView 进行修改,并重新刷新AppWidget 组件

updateAppWidget(int appWidgetId, RemoteViews views) 通过appWidgetId 对传进来的 RemoteView 进行修改,并重新刷新AppWidget 组件

1、使用bindAppWidgetId缺少权限?

android.permission.BIND_APPWIDGET权限的apk要放在system/app目录下,

apkandroid.permission.BIND_APPWIDGET权限才能起作用。安装在data下是不会起作用的。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值