如何实现一个桌面小部件(一)

实现一个桌面小部件,分为三个步骤:
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来帮助完场信息的更新。

在下一篇博客中,我将用一个实例,来说明如何实现一个桌面小部件!!!

这是我的微信公众号,如果可以的话,希望您可以帮忙关注一下,这将是对我最大的鼓励了,谢谢!!

如何实现一个桌面小部件(一)_第1张图片

你可能感兴趣的:(Android,桌面小部件,android,小部件,AppWidget)