今天写的是AppWidget,这是我工作之后接触的第一个模块。乘着有时间整理下知识点。
先附上DEMO:http://download.csdn.net/detail/violetjack0808/9433218
这里实现的是一个用来计数的AppWidget桌面小部件。效果图如下:
下面是实现步骤
1.创建AppWidget配置文件
在res/xml目录下创建app_widget_config.xml文件,用来配置widget属性。
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/widget_ui" android:minHeight="140dip" android:minWidth="140dip" android:updatePeriodMillis="6890000" > </appwidget-provider>
这个xml是用来描述所要创建的appWidget的一些描述信息的,所描述的属性分别有最小高度、最小宽度、刷新间隔和关联布局文件布局文件。
特别要说的是AppWidget的大小是以格为单位来计算的,手机屏幕被分为一个4*4的格阵,当宽高到达一定值时就会显示并占满相应的格数,不会出现自定义不规则的大小的。网上资料显示说N格的长宽为(74*N)-2。我设定的140*140显示的是2*2的界面。
在res/layout中创建widget_ui.xml这个布局文件。这里画的布局就会是我们在手机桌面上看到的widget的样子啦~
<?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/tvTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="this is a widget" /> <TextView android:id="@+id/tvResult" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="result = 0"/> <Button android:id="@+id/btnAdd" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="add one"/> </LinearLayout>
3.创建AppWidgetProvider
在src目录下创建AppWidget类继承自AppWidgetProvider,用以实现widget的交互。
当我看到onReceive方法觉得奇怪,这不是广播接收器吗?结果进去一看发现AppWidgetProvider就是继承自BroadcastReceiver的
public class AppWidgetProvider extends BroadcastReceiver
所以,widget的工作原理就是通过广播来实现对它的控制的。
3.1 AppWidgetProvider的生命周期:
onEnabled 当AppWidget实例第一次被创建时调用。
onReceive 接收广播事件。
onUpdate 到达指定的更新时间或用户向桌面添加widget时候调用。
onDeleted 当AppWidget被删除时调用。
onDisabled 当最后一个AppWidget被删除时调用。
3.2.RemoteViews和PendingIntent
在onUpDate方法中用到了RemoteViews与PendingIntent, 是因为AppWidget和其原本的App并不在同一个进程中,而是运行在HomeScreen进程当中,因此,在控件监听器的绑定,更新等操作都会与以前基本的方法有所不同。
PendingIntent是一个特殊的Intent,实际上它像一个邮包,其中包裹着真正的Intent,当邮包未打开时,Intent是被“挂起”的,所以并不执行,只有当邮包拆开时才会执行。它与Intent的区别在于:Intent 是及时启动,intent 随所在的activity 消失而消失。 进程A创建PendingIntent,发送给进程B,进程B此事并不执行,直到用户出发某一事件时,包裹被拆开,里面的Intent真正执行。所以Intent什么时候被执行是不知道的,由用户出发事件来决定。
RemoteViews表示了一系列view对象,即AppWidget所有的控件。它的应用方法如下源码所示,使用了views.setOnClickPendingIntent(所需控件的ID,定义的pendingintent)方法来为控件绑定监听器。
/** * AppWidget学习demo * 个人心得: * onUpDate方法用于创建和更新AppWidget,将所需的ID通过intent进行传递。 * onReceive方法通过接收intent传送的广播事件实现AppWidget的变化操作。 */ public class AppWidget extends AppWidgetProvider { private static int i = 1; private static final String ACTION = "click"; //当AppWidget实例第一次被创建时调用。 @Override public void onEnabled(Context context) { super.onEnabled(context); } //接收广播事件。 @Override public void onReceive(Context context, Intent intent) { Log.e("widget", "onReceive + " + intent.getAction()); super.onReceive(context, intent); ComponentName thisWidget = new ComponentName(context,AppWidget.class);//定义容器 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_ui);//定义RemoteViews //定义AppWidgetManager,用于之后更新AppWidget AppWidgetManager appWidgetManager = AppWidgetManager .getInstance(context); //通过接收广播的Intent来进行widget的UI操作 if (intent.getAction().equals(ACTION)) { views.setTextViewText(R.id.tvResult, "result = " + i); i++; } //更新AppWidget appWidgetManager.updateAppWidget(thisWidget, views); } //到达指定的更新时间或用户向桌面添加widget时候调用。 public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) { Log.e("widget", "onUpdate"); for (int id : appWidgetIds) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_ui); Intent intent1 = new Intent("click");//定义intent PendingIntent pendingIntentOne = PendingIntent.getBroadcast(context, 0, intent1, 0);//用PendingIntent将Intent包裹起来 views.setOnClickPendingIntent(R.id.btnAdd, pendingIntentOne);//按钮点击事件监听 //更新AppWidget appWidgetManager.updateAppWidget(id, views); } } //当AppWidget被删除时调用。 @Override public void onDeleted(Context context, int[] appWidgetIds) { super.onDeleted(context, appWidgetIds); } //当最后一个AppWidget被删除时调用。 @Override public void onDisabled(Context context) { super.onDisabled(context); } }
4.注册广播
上面提到了,AppWidgetProvider其实就是广播,所以我们需要在Manifest文件中注册广播。
这里注意Action的一致。关于广播的知识可以看我的Blog:Android广播Broadcast的学习(附demo)
代码:
<application .... > ..... <receiver android:name=".AppWidget" > <meta-data android:name="android.appwidget.provider" android:resource="@xml/app_widget_config" /> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="click" /> </intent-filter> </receiver> .... </application>注意:resource要关联我们之前写的app_widget_provider.xml文件;action中第一个必须写,第二个就是自定义action了。
这样一个计数的桌面小部件就完成啦~