0%

Android桌面小组件Widget开发入门 AppWidgetProvider的使用

2011.05.23 更新: 经过半天的测试,确定1.6以上版本Widget更新时间为半小,但是添加Widget后第一次启动有延迟,是37分左右,后面每次更新都是间隔30分钟。 想必大家都用过桌面小组件,像天气预报,时钟之类的,与Activity不同,它的主体是一个显示在桌面上可移动的视图,而并不是一个独立的程序。 先看例子的效果图: 不太好看,但是没关系,UI不是重点,重点是怎么把它写出来。 解释一下,一个红色的界面,上面有个TextView,显示一个数字,这个数字每秒加1,点击数字会跳出一个Activity.OK,下面来实现。 先介绍下结构: Main.java 主程序,一个Activity,点击桌面的Widget上文字后跳出,也可以在程序列表上点图标启动。 UpdateService.java 一个更新桌面Widget的服务Service,每隔一秒更新视图 Widget.java 这个才是桌面Widget需要用到的程序,继承自AppWidgetProvider layout/main.xml Main Activity和Widget用到的布局文件,Activity和Widget都用这个作而已(简单起见) xml/widget.xml Widget的配置文件,配置显示的宽、高,更新时间,指定布局文件(main.xml) 下面贴代码: 先是AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.pocketdigi.widget" android:versionCode="1"
    android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <receiver android:label="@string/app_name" android:name=".Widget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:resource="@xml/widget" android:name="android.appwidget.provider" />
        </receiver>
        <activity android:name=".Main">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".UpdateService" />
    </application>
</manifest>

AppWidgetProvider类继承于BroadcastReceiver,所以这里定义的时候跟BroadcastReceiver是一样的,用receiver标签 android.appwidget.action.APPWIDGET_UPDATE是固定值,在添加widget到桌面时会发送该广播 android.appwidget.provider设置的是AppWidgetProvider配置的XML文件 再下面是一个actvity,一个service,不再解释 Main.java就不贴了,在这里就是自动生成的Hello World Widget.java:

package com.pocketdigi.widget;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;

public class Widget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {
        // TODO Auto-generated method stub
        //添加到桌面和更新界面时执行,但是1.6以后没有了updatePeriodMillis属性,所以会看不到更新界面执行的效果
        System.out.println("onUpdate");
        Intent intent=new Intent(context,UpdateService.class);
        context.startService(intent);
        //启动服务
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        // TODO Auto-generated method stub
        //被删除时执行
        System.out.println("删除");
        Intent intent=new Intent(context,UpdateService.class);
        context.stopService(intent);
        //停止后台服务
        super.onDeleted(context, appWidgetIds);
    }
    
}

UpdateService.java;

package com.pocketdigi.widget;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.widget.RemoteViews;

public class UpdateService extends Service {
    boolean is_run = false;
    boolean flag=true;
    //标记线程是否已经启动,已启动就不会再次启动
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        // TODO Auto-generated method stub
        super.onStart(intent, startId);
        final RemoteViews updateViews = new RemoteViews(this.getPackageName(),
                R.layout.main);
        final ComponentName thisWidget = new ComponentName(this, Widget.class);
        final AppWidgetManager manager = AppWidgetManager.getInstance(this);

        if (!is_run) {
            //如果已经启动,不执行
            new Thread() {
                //因为XML中的updatePeriodMillis属性无效,为了测试更新界面的效果
                // 所以,我在这新建了一个线程,在里面无限循环更新APP Widget
                //第一天学这个,不知道这样会不会很费电,不知道还有什么其他方法,以后如果发现再补充
                public void run() {
                    int i = 0;
                    is_run=true;
                    while (flag) {
                        updateViews.setTextViewText(R.id.tv, String.valueOf(i));
                        updateViews.setInt(R.id.tv, "setTextColor",
                                R.color.black);
                        updateViews.setFloat(R.id.tv, "setTextSize", 20.0f);
                        // setXX方法,三个参数分别为操作控件在XML中的ID,
                        // 执行的方法名(该控件ID通过findViewById得到的view,该view的方法,上面执行的就是TextView的setTextColor和setTextSize方法)
                        // 方法的参数,setXX需要根据该参数类型确定,如setTextColor的参数是int,就调用setInt,setTextSize的参数是Float,就调用setFloat
                        Intent intent = new Intent(UpdateService.this,
                                Main.class);
                        PendingIntent pendingintent = PendingIntent
                                .getActivity(UpdateService.this, 0, intent, 0);
                        updateViews.setOnClickPendingIntent(R.id.tv,
                                pendingintent);
                        // 以上几行相当于调用TextView的setOnClickListener方法,在点击时启动Main这个Activity
                        manager.updateAppWidget(thisWidget, updateViews);
                        // 更新
                        i++;
                        try {
                            sleep(1000);
                            // 休息1秒
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        }

    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        System.out.println("onDestory");
        flag=false;
        //服务停止时停止线程
        super.onDestroy();
    }


}

Main.xml,其实就是把自动生成的Hello World的TextView加个ID:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/red"
    >
<TextView  android:id="@+id/tv"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
</LinearLayout>

widget.xml:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:minWidth="50dip"
  android:minHeight="50dip"
  android:updatePeriodMillis="5000"
  android:initialLayout="@layout/main"
  >
</appwidget-provider>

前两个分别是宽,高的最小值,我在测试的时候,没发现修改这两个值对Widget显示的宽高有什么影响,希望知道的同学留下言 updatePeriodMillis更新间隔时间,单位毫秒,根据实际测试,以及网上的资料,如果android版本在1.6及以上,不支持这个属性,好像默认半小时左右更新 最后是Widget小组件的界面xml文件 另外,在strings.xml中加了两个颜色值:

    <color name="red">#ff0000</color>
    <color name="black">#000000</color>

OK,就是这样,测试一下吧!附上打包的源代码,为了提高人气,回得可见,望理解: [download id=”16”]