[置顶] Android中AppWidget的学习整理(附demo)

今天写的是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的界面。


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)方法来为控件绑定监听器。


3.3 源码

/**
 * 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了。


这样一个计数的桌面小部件就完成啦~

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