桌面小部件App widget的使用
AppWidget(窗口小部件)是基于BroadcastReceiver组件机制再开发而来的,为此他首先需要遵循BroadcastReceiver的开发流程进行开发,其次是根据他自身提供的AppWidgetProvider、AppWidgetProvderInfo、AppWidgetManger来进行开发。为此要开发一个AppWidget大致流程如下:
1.在res文件夹中新建一个xml文件:(该文件名在下面的第四步有用到,作为appWidget的资源)
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="200dp" android:minHeight="50dp" android:updatePeriodMillis="0" android:initialLayout="@layout/appwidget_layout" > </appwidget-provider>
属性说明:
updatePeriodMillis:定义了App Widget框架调用AppWidgetProvider(继承该类的类)的onUpdate方法的频率,google建议这个值设置为最小1小时,否则设备会被频繁唤醒,而且还建议如果要设置短时的更新时间,推荐使用AlarmManager类中的工具来调用onUpdate。这里该值设置为0,则表示不进行对onUpdate的更新调用,只在添加到桌面时调用。
initialLayout 属性标识了初始布局文件(显示在桌面的布局文件)
configure 属性定义了当用户添加App Widget时调用的Activity。
previewImage定义了App Widget的缩略图,当用户从widget列表(安卓4.1的窗口小部件)中选择时,显示的就是这张图。如果没设置,用户将看见的是你的应用的默认图标。
resizeMode 属性标识了widget重新布局的规则。你可以使用该属性来让widget能够在水平、竖直、或两个方向上均可变化。可用的值包括horizontal、vertical、none。如果是想在两个方向上均能拉伸,可设置为horizontal|vertical,当然,需要Android3.1以上版本。
2.在layout文件夹中新建布局文件作为第一步中initialLayout方法中所指定的布局:
<?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:layout_width="wrap_content" android:layout_height="wrap_content" android:text="显示在桌面的widget" /> </LinearLayout>
说明:这里只在桌面上显示一行文字。
3.自定义一个类继承 AppWidgetProvider,代码如下:
public class MyAppWidget extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); System.out.println("接收receiver中注册的广播事件时调用该方法"); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); System.out.println("在达到指定的更新时间之后或者当用户向桌面添加App Widget时会调用该方法"); } @Override public void onDeleted(Context context, int[] appWidgetIds) { super.onDeleted(context, appWidgetIds); System.out.println("当App Widget被删除时,会调用该方法"); } @Override public void onEnabled(Context context) { super.onEnabled(context); System.out.println("当一个App Widget的实例第一次被创建时,会调用该方法"); } @Override public void onDisabled(Context context) { super.onDisabled(context); System.out.println("当最后一个App Widget实例被删除后,会调用该方法"); } }
4.在AndroidManifest.xml文件中对app Widget(和广播类似)进行注册:
<receiver android:name="MyAppWidget" android:label="terst" android:icon="@drawable/ic_launcher"> <intent-filter > <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> //接收该广播,系统的widget广播 还可以在receiver中继续添加自定义广播等等 </intent-filter> <meta-data android:name="android.appwidget.provider" //标识appwidget使用的资源 android:resource="@xml/appwidget_info"/> //指定资源文件 也就是上面步骤中我们新建的xml文件夹中的appwidget.xml文件 </receiver>
说明:
<receiver>标签name 用于指明App Widget使用的AppWidgetProvider(被继承的类)
label 和 icon用于指定在窗口小部件中显示文字和图标
效果如下:
当我们从窗口小部件中拖拽到桌面上显示时,效果如下:
但是 注意点:
由于App Widget和我们的应用程序运行在不同的进程当中(App Widget当中的View运行在Home Screen主屏幕进程当中),
所以,无法按照之前的惯用的方法绑定监听器,那么怎么添加按钮且监听呢?看下面的代码:
1.首先在App Widget桌面显示的布局文件中加入按钮:
appwidget_layout.xml中加入:
<Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/btn_widget" android:text="桌面显示的按钮" />
2.添加按钮监听器:
在重写的onUpdate方法中添加如下代码:
@Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); System.out.println("在达到指定的更新时间之后或者当用户向桌面添加App Widget时会调用该方法"); //绑定监听,因为只有在拖拽到桌面上才需要监听 for(int i=0;i<appWidgetIds.length;i++){//appWidgetIds里面装的是拖拽出来的所有widget System.out.println("远程视图需要的包名:"+context.getPackageName()); //不能直接设置监听,所以需要通过远程视图进行设置,appwidget_layout为显示在桌面的视图 RemoteViews rv=new RemoteViews(context.getPackageName(), R.layout.appwidget_layout); PendingIntent intent=PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);//转到MainActivity rv.setOnClickPendingIntent(R.id.btn_widget, intent);//为btn_widget设置单击事件为intent appWidgetManager.updateAppWidget(appWidgetIds[i], rv);//更新数据到这个小部件 }//为每个拖拽出来的小部件设置单击事件 }
说明:为widget中的按钮添加单击事件需要通过RemoteViews远程视图类作为中间者添加监听,最后要使用appWidgetManager实现更新数据。
效果如下:
说明:当点击按钮时就会跳转到MainActivity类
在桌面显示的按钮可以通过RemoteViews来设置,那么其他的组件呢?代码如下,通过按钮发送广播,接收后改变文本控件显示的内容 代码都是在上面的代码中进行添加的
1.在AndroidManifest.xml文件中注册自定义广播(因为按钮在widget中,所以需要在widget中注册):
<receiver android:name="MyAppWidget" android:label="terst" android:icon="@drawable/ic_launcher"> <intent-filter > <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info"/> <intent-filter > <action android:name="myBroadcast"/> //添加的自定义广播 </intent-filter> </receiver>
2.在(桌面显示的布局)appwidget_layout.xml文件中添加了一个按钮,如下:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_sendBroad"
android:text="发送广播" />
3.MyAppWidget.java类中的代码:
public class MyAppWidget extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); System.out.println("接收广播事件"); String action = intent.getAction();//获取活动,xml文件中注册的广播 if(action.equals("myBroadcast")){ System.out.println("接收到自定义的广播"); //通过RemoteViews对象改变布局中的组件内容 RemoteViews rv=new RemoteViews(context.getPackageName(), R.layout.appwidget_layout); rv.setTextViewText(R.id.text, "接收广播后改变文本内容");//修改id为text的内容为~ 修改图片也是同样的道理 ComponentName componentName=new ComponentName(context, MyAppWidget.class);//因为这里没有appWidgetIds对象,所以需要这样更新数据 AppWidgetManager.getInstance(context).updateAppWidget(componentName, rv);//更新数据 } } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); System.out.println("在达到指定的更新时间之后或者当用户向桌面添加App Widget时会调用该方法"); //绑定监听,因为只有在拖拽到桌面上才需要监听 for(int i=0;i<appWidgetIds.length;i++){//appWidgetIds里面装的是拖拽出来的所有widget System.out.println("远程视图需要的包名:"+context.getPackageName()); //不能直接设置监听,所以需要通过远程视图进行设置,appwidget_layout为显示在桌面的视图 RemoteViews rv=new RemoteViews(context.getPackageName(), R.layout.appwidget_layout);//相当于桌面显示的布局文件 PendingIntent intent=PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);//转到MainActivity rv.setOnClickPendingIntent(R.id.btn_widget, intent);//为btn_widget设置单击事件为intent //为发送广播的按钮设置单击事件,点击后发送一条自定义的广播 rv.setOnClickPendingIntent(R.id.btn_sendBroad, PendingIntent.getBroadcast(context, 0, new Intent("myBroadcast"), 0)); appWidgetManager.updateAppWidget(appWidgetIds[i], rv);//更新数据到这个小部件 }//为每个拖拽出来的小部件设置单击事件 } @Override public void onDeleted(Context context, int[] appWidgetIds) { super.onDeleted(context, appWidgetIds); System.out.println("当App Widget被删除时,会调用该方法"); } @Override public void onEnabled(Context context) { super.onEnabled(context); System.out.println("当一个App Widget的实例第一次被创建时,会调用该方法"); } @Override public void onDisabled(Context context) { super.onDisabled(context); System.out.println("当最后一个App Widget实例被删除后,会调用该方法"); } }
效果如下:当点击发送广播按钮时会将文本显示内容改掉