1.应用场景:需求,便利需求,想快捷进行信息获取或操作手机,例如调节亮度,查看彩票,查看日历……
2.功能实现:如何为自己的程序提供一个桌面小控件,为程序提供一个功能操作的快捷方式?
思考:
a.组件类型:目前android系统只会调用四大组件,那么桌面小控件是哪类组件呢?
b.生命周期:窗口小部件是如何启动的?谁负责启动它?在什么时候启动?
c.UI设计:View来自哪里?在哪里进行设计UI?
d.业务处理:如何更新数据?数据来自哪里?如何响应点击触摸事件?
结论:
a.桌面小控件,需要继承AppWidgetProvider,而AppWidgetProvider又是BroadcastReceiver的子类,所以桌面小控件是广播接受者类型;
public class AppWidgetProvider extends BroadcastReceiver
A convenience class to aid in implementing an AppWidget provider. Everything you can do with AppWidgetProvider, you can do with a regular BroadcastReceiver
. AppWidgetProvider merely parses the relevant fields out of the Intent that is received in onReceive(Context,Intent)
, and calls hook methods with the received extras.
b.生命周期:用户添加获删除小控件时,系统发出广播,当AppWidgetProvider接受到广播时,从Intent中获取action,判断action是以下那种:
AppWidgetManager.ACTION_APPWIDGET_UPDATE,//创建对象时发的action;
AppWidgetManager.ACTION_APPWIDGET_DELETED,//删除对象时发的action;
AppWidgetManager.ACTION_APPWIDGET_ENABLED,//第一个对象被创建时,发的action;
AppWidgetManager.ACTION_APPWIDGET_DISABLED。//最后一个对象被销毁时发的action;
代码如下:
public void onReceive(Context context, Intent intent) {
// Protect against rogue update broadcasts (not really a security issue,
// just filter bad broacasts out so subclasses are less likely to crash).
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds != null && appWidgetIds.length > 0) {
this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
}
}
}
else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
this.onDeleted(context, new int[] { appWidgetId });
}
}
else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
this.onEnabled(context);
}
else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
this.onDisabled(context);
}
}
Log如下:
当第一个widget被创建出来的时候
06-15 09:31:13.025: INFO/System.out(570): onreceive
06-15 09:31:13.025: INFO/System.out(570): onEnabled
06-15 09:31:13.045: INFO/System.out(570): onreceive
06-15 09:31:13.045: INFO/System.out(570): onUpdate
当第二个相同的widget被创建的时候
06-15 09:32:24.895: INFO/System.out(570): onreceive
06-15 09:32:24.935: INFO/System.out(570): onUpdate
当第三个被创建的时候
06-15 09:32:55.995: INFO/System.out(570): onreceive
06-15 09:32:55.995: INFO/System.out(570): onUpdate
删除一个条目的时候
06-15 09:33:19.766: INFO/System.out(570): onreceive
06-15 09:33:19.766: INFO/System.out(570): onDeleted
最后一个widget被移除的时候
06-15 09:33:48.525: INFO/System.out(570): onreceive
06-15 09:33:48.525: INFO/System.out(570): onDeleted
06-15 09:33:48.545: INFO/System.out(570): onreceive
06-15 09:33:48.545: INFO/System.out(570): onDisabled
c.UI设计:View来自哪里?在哪里进行设计UI? 在清单文件注册广播接受者,在意图过滤器中指明action name,在元数据中指明name和resource;
<receiver android:name=".MyWidget" >
<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>
在资源文件中指定minWith,minHight,updatePeriodMillis和initialLayout;
example_appwidget_info 配置文件
android:minWidth="294dp" 最小宽度
android:minHeight="72dp" 最小高度
android:updatePeriodMillis="86400000" 代表的是widget自动更新的时间 单位是毫秒1000
最小值是30分钟 60*1000*30 = 1800000
如果需要在小于30分钟内更新widget的内容需要利用widget的管理服务
手动的更新 widget里面显示的内容
android:initialLayout="@layout/main" widget最开始的布局(界面).
d.业务处理:如何更新数据?数据来自哪里?如何响应点击触摸事件?
要更新数据,首先启动带特定功能的服务,所以创建一个service子类,在小控件的onEnable()方法中去startService,在onDisable()方法中stopService;
数据更新,需要通过Timer和TimerTask去执行,timer.schedule(task, 1000, 1000);
响应点击事件:给RemoteViews设置OnClickPendingIntent,指定某个view被点击时调用发出PendingIntent给启动服务或发送广播;
响应事件后需要更新数据:让AppWidgetManager通过ComponentName和RemoteViews去updatAppWidget();
A description of an Intent and target action to perform with it. Instances of this class are created with getActivity
, getBroadcast
, getService
; the returned object can be handed to other applications so that they can perform the action you described on your behalf at a later time.
Intent autoKillIntent = new Intent("com.example.killtask");
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, autoKillIntent, 0);
views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent);
appWidgetManager.updateAppWidget(proceeWidget, views);
总结:1.用什么组件? android.appwidget.AppWidgetProvider extends BroadcastReceiver;
2.用什么控件? android.widget.RemoteViews implements Parcelable, Filter;
3.用什么管理器? AppWidgetManager
4.用什么意图包装类?android.app.PendingIntent;
5.使用到什么通信技术?AppWidgetManager.getgetInstance(Context context).是如何得到桌面程序Launcher的IAppWidgetService服务的?
IPC技术,aidl:sService = IAppWidgetService.Stub.asInterface(b);
注意:由于该管理器是随时获取的,不需要进行长时间缓存,所有使用弱引用来包装;
获取AppWidgetManager的代码实现:
public static AppWidgetManager getInstance(Context context) {
synchronized (sManagerCache) {
if (sService == null) {
IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
sService = IAppWidgetService.Stub.asInterface(b);
}
//由于该管理器是随时获取的,不需要进行长时间缓存,所有使用弱引用来包装;
WeakReference<AppWidgetManager> ref = sManagerCache.get(context);
AppWidgetManager result = null;
if (ref != null) {
result = ref.get();
}
if (result == null) {
result = new AppWidgetManager(context);
sManagerCache.put(context, new WeakReference<AppWidgetManager>(result));
}
return result;
}
}