实现一个桌面小部件,分为三个步骤:
1、继承一个AppWidgetProvider或者一个BoardcastReceiver,用来实现自己的小部件更新接收广播。
建议使用AppWidgetProvider,这样也可以在AppWidgetProvider中添加新的action,用来使小部件根据其他的广播更新信息。
在AppWidgetProvider代码中:
public void onReceive(Context context, Intent intent) {
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_OPTIONS_CHANGED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
&& extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
Bundle widgetExtras = extras.getBundle(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS);
this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
appWidgetId, widgetExtras);
}
} else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
this.onEnabled(context);
} else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
this.onDisabled(context);
} else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] oldIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
int[] newIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (oldIds != null && oldIds.length > 0) {
this.onRestored(context, oldIds, newIds);
this.onUpdate(context, AppWidgetManager.getInstance(context), newIds);
}
}
}
}
在代码中,广播接收器根据不同的action执行不同的方法,在AppWidgetProvider中,这些方法都是空方法,所以,我们既可以自己继承BoardcastReceiver
来实现我们的AppWidgetProvider也可以通过继承AppWidgetProvider来实现在父类中的空方法,这些都是可以的!!!
在广播我们需要在manifest进行注册:
这里假设我们的使用< ExampleAppWidgetProvider extends AppWidgetProvider >
public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
<receiver android:name="ExampleAppWidgetProvider" >
<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>
根据注册的信息,我们的广播只会接收到小部件更新的广播,不明白为什么不一起加上呢!!
这里的meta-data用来描述一个AppWidgetProviderInfo,其实现实在xml中完成的:
res/xml/example_appwidget_info.xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/preview"
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen">
appwidget-provider>
updatePeriodMillis表示的是小部件自动更新的时间间隔,单位是毫秒,这里设置的是24小时,这个值应该是有最小限制的,如果设置为0,你就只能自己发送广播让小部件更新了,
网上说framework自动更新小部件的最小间隔为半个小时,在文档中没有看到!!!不过在任何情况下,我们都可以手动更新我们的小部件(通过定时发送Intent,或者直接监听时间变化的广播)
previewImage属性表示小部件在未添加的时候的预览图,如果不设置的话,默认会使用应用的图标!!!这也是很多自定义的小部件在预览的时候看到都是小机器的缘故!!
initialLayout属性代表的是小部件的真实布局,是一个layout文件!!
widgetCategory属性表示小部件是否允许被现实在主界面或者锁屏界面,在5.0之前的版本,小部件可以显示在锁屏界面,但是在5.0之后,小部件不允许被显示在锁屏在界面!!
configure属性表示在小部件被创建的时候进行的配置Activity,
在AppWidgetHostActivity中,可以看到源代码:
void configureAppWidget(int requestCode, int appWidgetId, ComponentName configure) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
intent.setComponent(configure);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
SharedPreferences.Editor prefs = getPreferences(0).edit();
prefs.putInt(PENDING_APPWIDGET_ID, appWidgetId);
prefs.commit();
startActivityForResult(intent, requestCode);
}
从这里就可以看到,我们自己的ConfigureActivity的action已经被确定了,即为:AppWidgetManager.ACTION_APPWIDGET_CONFIGURE
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case DISCOVER_APPWIDGET_REQUEST:
handleAppWidgetPickResult(resultCode, data);
break;
case CONFIGURE_APPWIDGET_REQUEST:
handleAppWidgetConfigureResult(resultCode, data);
}
}
void handleAppWidgetConfigureResult(int resultCode, Intent data) {
int appWidgetId = getPreferences(0).getInt(PENDING_APPWIDGET_ID, -1);
Log.d(TAG, "resultCode=" + resultCode + " appWidgetId=" + appWidgetId);
if (appWidgetId < 0) {
Log.w(TAG, "was no preference for PENDING_APPWIDGET_ID");
return;
}
if (resultCode == RESULT_OK) {
AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
addAppWidgetView(appWidgetId, appWidget);
} else {
mHost.deleteAppWidgetId(appWidgetId);
}
}
//如果配置没有完成,在自定义的配置activity中设置结果码不为ok,就不会成功添加小部件了!!
void addAppWidgetView(int appWidgetId, AppWidgetProviderInfo appWidget) {
// Inflate the AppWidget's RemoteViews
AppWidgetHostView view = mHost.createView(this, appWidgetId, appWidget);
// Add it to the list
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
mAppWidgetContainer.addView(view, layoutParams);
registerForContextMenu(view);
}
小部件布局:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/widget_margin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="@drawable/my_widget_background">
…
LinearLayout>
FrameLayout>
配置Activity:
<activity android:name=".ExampleAppWidgetConfigure">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
intent-filter>
activity>
小部件的点击事件通过使用RemoteView+PendingIntent实现,小部件的更新实现在onUpdate方法中实现
有一点需要特别注意,由于AppWidgetProvider是继承了BoardcastReceiver,所以它的进程生命周期与广播接收器的进程生命周期是一致的。即,只有在广播发生时才会进入AppWidgetProvider中的各个方法。所以在从网络上更新信息时,需要注意,应该从新启动一个service来帮助完场信息的更新。
在下一篇博客中,我将用一个实例,来说明如何实现一个桌面小部件!!!
这是我的微信公众号,如果可以的话,希望您可以帮忙关注一下,这将是对我最大的鼓励了,谢谢!!