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

来源:互联网 发布:加强网络舆情监管 编辑:程序博客网 时间:2024/06/16 06:25

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

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

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

这里写图片描述

0 0