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”]