widget开发以桌面日历widget为例:
在这个例子中主要分为四个内容:
①、继承AppWidgetProvider类,命名为CalendarWidgetProvider,在该类中完成接收接收广播、启动更新时间的服务、更新widget显示。
②、在AndroidManifest.xml中声明广播接收类,和相应的action。
③、在res下创建xml文件夹,并在其中创建AppWidgetProviderInfo数据元的xml文件。在该xml文件中主要设定Widget大小和更新时间,由于系统默认是30分钟更新且设定小于30分钟更新不会生效,所以在CalendarWidgetProvider采用接收系统时间改变的广播用于更新Widget。
④、定义用于更新时间的Service类UpdateService,该类继承了Service,该服务中主要完成发送time_ticker广播。
⑤、定义一个用于获得日期和时间的工具类。
首先定义一个继承AppWidgetProvider的类CalendarWidgetProvider,AppWidgetProvider类是BroadcastReceiver的子类,所以它实质上是一个广播接收者。在CalendarWidgetProvider类中,主要完成三个功能:启动发送time_ticker的广播的服务,接收广播,更新widget显示。在该类中,首先应override onReceive() 方法,用于接收广播,在桌面日历应用中应接收四种广播,该广播都在AndroidManifest.xml文件中注册。
<receiver
android:name="com.nwd.android.calendarwidget.CalendarWidgetProvider"
android:enabled="true"
android:icon="@drawable/ic_launcher" >
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/calendarwidgetinfo" />
<intent-filter>
<action android:name="android.intent.action.DATE_CHANGED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="com.nwd.android.calendarwidget.timetick" />
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
<
receiver
>
中的name标志了接收广播的类
:
CalendarWidgetProvider
。
<
meta-data>adnroid:resource
指定了widget的大小等特性。calendardrawwidgetinfo文件如下:
<?xml version="1.0" encoding= "UTF-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="200dp"
android:minHeight="100dp"
android:updatePeriodMillis="0"
android:initialLayout="@layout/main" >
</appwidget-provider>
CalendarWidgetProvider
类如下
:
public class CalendarWidgetProvider extends AppWidgetProvider {
// 系统日期改变接收的广播的action
private static final String DATE_CHANGED = Intent.ACTION_DATE_CHANGED ;
// 系统时间改变接收的广播的action
private static final String TIME_CHANGED = Intent.ACTION_TIME_CHANGED ;
// 接收UpdateService转发的TIME_TICK的广播
private static final String TIME_TICK = "com.nwd.android.calendarwidget.timetick" ;
// public static String TAG = "CalendarWidgetProvider";
private RemoteViews mRv;
public static final boolean DEBUG = true;
private static final JLog LOG = new JLog("CalendarWidgetProvider" ,
CalendarWidgetProvider.DEBUG, JLog. TYPE_DEBUG);
@Override
public void onEnabled(Context context) {
// TODO Auto-generated method stub
super.onEnabled(context);
LOG.print("CalendarWidgetProvider.onEnabled()" );
context.startService( new Intent(context, UpdateService.class));
}
/**
* 接收到广播根据相应的action转换相应的View
*/
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
super.onReceive(context, intent);
String action = intent.getAction();
LOG.print("CalendarWidgetProvider.onReceive() " + action);
if (mRv == null) {
mRv = new RemoteViews(context.getPackageName(), R.layout.main);
}
DateUtil util = DateUtil. getInstance(context);
// 获取日期字符串
String date = util.getDate();
// 获取星期几
String weekDay = util.getWeekDay();
// 接收到DATE_CHANGED的系统广播
if (DATE_CHANGED .equals(action)) {
LOG.print("CalendarWidgetProvider.onReceive() " + date);
updateDate( mRv, weekDay, date);
LOG.print(date);
}
// 接收到TIME_CHANGED的系统广播
if (TIME_CHANGED .equals(action)) {
LOG.print("CalendarWidgetProvider.onReceive() " + date);
updateDate( mRv, weekDay, date);
updateTime(context, mRv);
LOG.print(date);
}
// 接收到TIME_TICK的系统广播
if (TIME_TICK .equals(action)) {
updateTime(context, mRv);
updateDate( mRv, weekDay, date);
}
AppWidgetManager appWidgetManager = AppWidgetManager
. getInstance(context);
int[] appIds = appWidgetManager.getAppWidgetIds(new ComponentName(
context, CalendarWidgetProvider.class));
appWidgetManager.updateAppWidget(appIds, mRv);
LOG.print(date);
}
/**
* 更新widget上面所有的view
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int i = 0; i < appWidgetIds.length; i++) {
if (mRv == null) {
mRv = new RemoteViews(context.getPackageName(), R.layout.main);
}
// Intent intent = new Intent(context, UpdateService.class);
// PendingIntent pendingIntent = PendingIntent.getService(context, 0,
// intent, 0);
DateUtil util = DateUtil. getInstance(context);
String date = util.getDate();
String weekDay = util.getWeekDay();
updateTime(context, mRv);
updateDate( mRv, weekDay, date);
// 设置更新时间
// AlarmManager alarm = (AlarmManager) context
// .getSystemService(Context.ALARM_SERVICE);
// alarm.setRepeating(AlarmManager.RTC_WAKEUP,
// System.currentTimeMillis(), 60 * 1000, pendingIntent);
//设置widget的点击事件,当点击时进入Calendar
Intent LaunchCalendarIntent = new Intent();
LaunchCalendarIntent.setClassName( "com.android.calendar",
"com.android.calendar.LaunchActivity" );
PendingIntent luanchPendingIntent = PendingIntent.getActivity(
context, 0, LaunchCalendarIntent, 0);
mRv.setOnClickPendingIntent(R.id.time_linearlayout,
luanchPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, mRv);
LOG.print(date);
}
}
// 更新widget上面的日期
public void updateDate(RemoteViews rv, String weekDay, String date) {
rv.setTextViewText(R.id. date, "" + date);
rv.setTextViewText(R.id. week, "" + weekDay);
}
// 更新widget上面的时间
public void updateTime(Context context, RemoteViews rv) {
DateUtil util = DateUtil. getInstance(context);
int hour = util.getHour();
int minute = util.getMinute();
if (!util.is24()) {
if (hour > 12) {
hour -= 12;
}
if (hour == 0) {
hour = 12;
}
}
if (minute < 10) {
rv.setTextViewText(R.id. minute, "0" + minute);
} else {
rv.setTextViewText(R.id. minute, "" + minute);
}
rv.setTextViewText(R.id. hour, "" + hour);
}
}
在onEnabled()方法中,启动服务
UpdateService,该服务主要用来广播time_tick。onReceive() 方法根据不同的广播改变widget的显示。upDate()方法用来完成widget的更新,同时,在该方法中设置了整个widget的点击事件,当widget被点击时,会调用系统的日历,进入日历的显示界面。
在该widget项目中还定义了两个类,上面提到的的服务UpdateService和一个工具类DateUtil。两个类的作用分别是:发time_tick广播,获取当前日期时间的工具。
UpdateService
类如下:
public class UpdateService extends Service {
public static final boolean DEBUG = true;
private static final JLog LOG = new JLog("UpdateService" , UpdateService.DEBUG , JLog.TYPE_DEBUG);
private BroadcastReceiver receiver;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null ;
}
/**
* 创建服务并动态注册系统的ACTION_TIME_TICK服务
*/
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
LOG.print("UpdateService.onCreate()" );
// 实例化接收系统ACTION_TIME_TICK的BroadcastReceiver
receiver = new BroadcastReceiver(){
private static final String ACTION_TIME_TICK = Intent.ACTION_TIME_TICK ;
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
LOG.print("UpdateService.onReceive()" );
if(intent.getAction().equals(ACTION_TIME_TICK)){//注意:使用的是equals方法,不是"=="
Intent tmp = new Intent();
tmp.setAction("com.nwd.android.calendarwidget.timetick" );
LOG.print("TIME_TICK" );
context.sendBroadcast(tmp);
}
}
};
this.registerReceiver(receiver , new IntentFilter(Intent.ACTION_TIME_TICK ));
}
/**
* 在onDestroy()方法里面重启UpdateService
*/
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
//this.unregisterReceiver(receiver);
Intent intent = new Intent();
intent.setClass( this, UpdateService.class);
startService(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
return START_STICKY ;
}
}
DateUtil
类如下:
public class DateUtil {
private Context context;
// 记录系统时间格式(分为12小时制和24小时制)
private SimpleDateFormat formatter;
private static DateUtil instance;
/**
* 生成单例模型并初始化系统时间格式
* @param context
*/
private DateUtil(Context context){
this.context = context;
formatter = new SimpleDateFormat("MM/dd");
}
public boolean is24(){
return DateFormat.is24HourFormat( context);
}
/**
* 获取时间的小时数,默认为24小时制
* @return
*/
public int getHour(){
Calendar c = Calendar. getInstance();
return c.get(Calendar.HOUR_OF_DAY);
}
/**
* 获取时间的分钟数
* @return
*/
public int getMinute(){
Calendar c = Calendar. getInstance();
return c.get(Calendar.MINUTE);
}
/**
* 获取当前年份
* @return
*/
public int getYear(){
Calendar c = Calendar. getInstance();
return c.get(Calendar.YEAR);
}
/**
* 获取当前月份
* @return
*/
public int getMonth(){
Calendar c = Calendar. getInstance();
return c.get(Calendar.MONTH);
}
/**
* 获取所在月的天数
* @return
*/
public int getDayOfMonth(){
Calendar c = Calendar. getInstance();
return c.get(Calendar.DAY_OF_MONTH);
}
/**
* 重新设置系统时间格式
*/
public void setDateFormat(SimpleDateFormat formatter){
this.formatter = formatter;
}
public static DateUtil getInstance(Context context){
if(instance == null){
instance = new DateUtil(context);
return instance ;
} else{
return instance ;
}
}
/**
* 获取系统日期
* @return 返回格式如2014/6/24
*/
public String getDate(){
Date curDate = new Date(System.currentTimeMillis());
String date = formatter.format(curDate);
return date;
}
/**
*
* @return 当前日期是星期几
*/
public String getWeekDay(){
String weekDay = "";
Calendar c = Calendar. getInstance();
int day = c.get(Calendar.DAY_OF_WEEK);
String weekdays[] = context.getResources().getStringArray(R.array.weekdays);
switch(day){
case 1:
weekDay = weekdays[0];
break;
case 2:
weekDay = weekdays[1];
break;
case 3:
weekDay = weekdays[2];
break;
case 4:
weekDay = weekdays[3];
break;
case 5:
weekDay = weekdays[4];
break;
case 6:
weekDay = weekdays[5];
break;
case 7:
weekDay = weekdays[6];
break;
default:
}
return weekDay;
}
}
最后Layout布局文件如下:
<?xml version="1.0" encoding= "utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/time_linearlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:background="@android:color/holo_blue_bright"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="129dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/hour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="60dp"
android:layout_gravity="center_vertical"
android:text="@string/hour"
android:textColor="@android:color/white"
android:textSize="60sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/semicolon"
android:textColor="@android:color/white"
android:textSize="60sp" />
<TextView
android:id="@+id/minute"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/minute"
android:textColor="@android:color/white"
android:textSize="60sp" />
</LinearLayout >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="43dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/week"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginLeft="20dp"
android:text="@string/week"
android:textColor="@android:color/white"
android:textSize="25sp" />
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="95dp"
android:text="@string/date"
android:textColor="@android:color/white"
android:textSize="25sp" />
</LinearLayout >
</LinearLayout>
widget显示的效果