第六部分 Widget日历
本例实现了Widget日历,用来显示当前的日期,时间等信息。要学习开发Widget开发,需要了解AppWidget的框架结构。基于该框架,开发者可以在Ophone及模拟器上开发"外形"
类似传统的Widget小应用程序,并将其嵌入到其他应用中。
注意:Widget设计好UI后根据需要来完成更新,如果程序不需要使用定时来更新AppWidget,则最好不要使用定时更新,因为它会增加电量和CUP资源的开销。我们可以定义一个线程,当需要
时才更新,这样可以让Widget达到最高效率
AppWidget框架主要包括以下类:
AppWidgetProvider: 该类继承自BroadcastReceiver,在AppWidget应用update、enable、disable、deleted时接受通知。其中,onUpdate、onReceive用来接收更新通知。
AppWidgetProviderInfo:该类主要用于AppWidget的大小、更新频率和初始界面等信息,以XML文件形式存在于res/xml目录下。
AppWidgetManager:该类负则管理AppWidget,及向AppWidgetProvider发送通知。
RemoteViews:一个可以在其他应用进程中运行的类,是构造AppWidget的核心。

Widget开发流程

res.layout.widget_layout.xml 

 <?xml version="1.0" encoding="utf-8"?> 

 <LinearLayout 

 xmlns:android=" 
http://schemas.android.com/apk/res/android" 

 android:orientation="vertical" 

 android:layout_width="wrap_content" 

 android:layout_height="wrap_content" 

 android:minHeight="146dp" 

 android:minWidth="146dp" 

 android:background="@drawable/bgblack" 

 android:id="@+id/Base" 

 > 

 <LinearLayout//需要3个TextView来显示日期 

 android:layout_width="wrap_content" 

 android:layout_height="wrap_content" 

 android:orientation="vertical" 

 android:layout_gravity="center" 

 android:paddingTop="5dip" 

 > 

 <TextView 

 android:layout_width="wrap_content" 

 android:layout_height="wrap_content" 

 android:id="@+id/Month" 

 android:layout_gravity="center" 

 android:textStyle="bold" 

 android:textSize="18sp" 

 android:textColor="#FFFFFF" 

 ></TextView> 

 <TextView 

 android:layout_width="wrap_content" 

 android:layout_height="wrap_content" 

 android:layout_gravity="center" 

 android:id="@+id/Date" 

 android:textStyle="bold" 

 android:textSize="70sp" 

 android:textColor="#FFFFFF" 

 ></TextView> 

 <TextView 

 android:layout_width="wrap_content" 

 android:layout_height="wrap_content" 

 android:id="@+id/WeekDay" 

 android:layout_gravity="center" 

 android:textSize="15sp" 

 android:textStyle="bold" 

 android:textColor="#FFFFFF" 

 ></TextView> 

 </LinearLayout> 

 </LinearLayout> 


 res.xml.widget.xml 

 <?xml version="1.0" encoding="utf-8"?>//

一个能反映出AppWidgetProviderInfo信息的布局文件。AppWidget的像素大小取决于它所占方块多少(计算公式(块数*74)-2),因此
取高度为146像素,宽度是146像素

<appwidget-provider 

 xmlns:android=" 
http://schemas.android.com/apk/res/android" 

 android:initialLayout="@layout/widget_layout" //设置AppWidget的布局文件为widget_layout布局 

 android:minWidth="146dip" 

 android:minHeight="146dip" 

 android:updatePeriodMillis="3600000"> 

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

 //res.layout.widget_layout_small.xml
 //<?xml version="1.0" encoding="utf-8"?>
 //<LinearLayout
 //xmlns:android="http://schemas.android.com/apk/res/android"
 //android:orientation="vertical"
 //android:layout_width="wrap_content"
 //android:layout_height="wrap_content"
 //android:minHeight="72dip"
 //android:minWidth="72dip"
 //android:background="@drawable/bgblacksmall"
 //android:id="@+id/SmallBase"
 //>
 //<LinearLayout
 //android:layout_width="wrap_content"
 //android:layout_height="wrap_content"
 //android:orientation="vertical"
 //android:layout_gravity="center"
 //android:paddingTop="5dip"
 //>
 //<TextView
 //android:layout_width="wrap_content"
 //android:layout_height="wrap_content"
 //android:id="@+id/Month"
 //android:layout_gravity="center"
 //android:textStyle="bold"
 //android:textSize="12sp"
 //android:gravity="center"
 //android:textColor="#FFFFFF"
 //></TextView>
 //<TextView
 //android:layout_width="wrap_content"
 //android:layout_height="wrap_content"
 //android:layout_gravity="center"
 //android:id="@+id/Date"
 //android:textStyle="bold"
 //android:textSize="30sp"
 //android:textColor="#FFFFFF"
 //></TextView>
 //</LinearLayout>
 //</LinearLayout>public class TodayDate extends AppWidgetProvider{//

当我们添加AppWidget应用或自动定时更新时,AppWidgetManager会广播动作名字是"android.appwidget.action.APPWIDGET_UPDATE"
的Intent,当onReceive()方法没有被重载时,onUpdate方法会接收到这些广播的Intent。类似BroadcastReceiver类,我们可以
重载AppWidgetProvider的onReceive方法,并在其中指定想要接收的Intent

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
 RemoteViews updateView = buildUpdate(context);
 appWidgetManager.updateAppWidget(appWidgetIds, updateView);
 super.onUpdate(context, appWidgetManager, appWidgetIds);
 }
 private String[] months = {"一月", "二月", "三月", "四月","五月", "六月", "七月", "八月","九月", "十月", "十一月", "十二月"};
 private String[] days = {"星期日", "星期一", "星期二", "星期三","星期四", "星期五", "星期六"};
 private RemoteViews buildUpdate(Context context){
 RemoteViews updateView = null;
 Time time = new Time();
 time.setToNow();
 String month = months[time.month] + " " + time.year;
 updateView = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
 updateView.setTextViewText(R.id.Date, new Integer(time.monthDay).toString());
 updateView.setTextViewText(R.id.Month, month);
 updateView.setTextViewText(R.id.WeekDay, days[time.weekDay]);
 Intent launchIntent = new Intent();
 launchIntent.setComponent(new ComponentName("com.android.calendar", "com.android.calendar.LaunchActivity"));
 launchIntent.setAction(Intent.ACTION_MAIN);
 launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
 launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
 PendingIntent intent = PendingIntent.getActivity(context, 0, launchIntent, 0);
 updateView.setOnClickPendingIntent(R.id.Base, intent);
 return updateView;
 }
 }//public class TodayDateSmall extends AppWidgetProvider{
 //public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
 //RemoteViews updateView = buildUpdate(context);
 //appWidgetManager.updateAppWidget(appWidgetIds, updateView);
 //super.onUpdate(context, appWidgetManager, appWidgetIds);
 //} 
 //private String[] months = {"一月", "二月", "三月", "四月","五月", "六月", "七月", "八月","九月", "十月", "十一月", "十二月"}; 
 //private RemoteViews buildUpdate(Context context) {
 //RemoteViews updateView = null;
 //Time time = new Time();
 //time.setToNow();
 //String month = months[time.month];
 //updateView = new RemoteViews(context.getPackageName(), R.layout.widget_layout_small);
 //updateView.setTextViewText(R.id.Date, new Integer(time.monthDay).toString());
 //updateView.setTextViewText(R.id.Month, month);
 //Intent launchIntent = new Intent();
 //launchIntent.setComponent(new ComponentName("com.android.calendar", "com.android.calendar.LaunchActivity"));
 //launchIntent.setAction(Intent.ACTION_MAIN);
 //launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
 //launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
 //PendingIntent intent = PendingIntent.getActivity(context, 0, launchIntent, 0);
 //updateView.setOnClickPendingIntent(R.id.SmallBase, intent);
 //return updateView;
 //}
 //}AndroidManifest.xml
 <?xml version="1.0" encoding="utf-8"?>//因为使用了receiver及"adroid.appwidget.action.APPWIDGET_UPDATE"所以需要声明权限
 <manifest 
 xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.yarin.android.TodayDate"
 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=".TodayDate">
 <intent-filter>
 <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
 </intent-filter>
 <meta-data 
 android:name="android.appwidget.provider" 
 android:resource="@xml/widget">
 </meta-data>
 </receiver>
 <receiver 
 android:label="@string/app_name_small" 
 android:name=".TodayDateSmall">
 <intent-filter>
 <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
 </intent-filter>
 <meta-data
 android:name="android.appwidget.provider" 
 android:resource="@xml/widget_small">
 </meta-data>
 </receiver>
 </application>
 <uses-sdk android:minSdkVersion="5" />
 </manifest> 

 续:
 1、上例中只是更新显示了一个界面,在Widget中同样可以通过setOnClickPendingIntent方法来监听一个事件的处理,下面代码中当我们点击了"widget_button"按钮时,会弹出Toast提示
 public class widget extends AppWidgetProvider{
 public void onReceive(Context context, Intent intent){
 super.onReceive(context,intent);
 if(intent.getAction().equals("com.android.myapp.widget.CLICK")){
 Toast.makeText(context ,"It works!!", Toast.LENGTH_SHORT).show();
 }
 }
 public void onUpdate(Context context,AppWidgetManager mgr, int[] appWidgetIds){
 final int N = appWidgetIds.length;
 for(int i=0; i<N; i++){
 int [] appWidgetId = appWidgetIds;
 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
 Intent clickintent = new Intent("com.android.myapp.widget.CLICK");
 PendingIntent pendingIntentClick = PendingIntent.getBroadcast(context, 0,clickintent,0);
 views.setOnClickPendingIntent(R.id.widget_butt,PendingIntentClick);
 mrg.updateAppWidget(appWidgetId,views);
 }
 }
 }


2、在Widget中启动一个Activity,

RemoteViews rv = new RemoteViews(context.getPackageName(),R.layout.mywidget);
 Intent intentActivity = new Intent(context, MusicPlayer.class);//此处启动一个音乐播放界面
 PendingIntent pendingIntentActivity = PendingIntent.getActivity(context,0,intentActivity,0);
 rv.setOnClickPendingIntent(R.id.button,pendingIntentActivity);
 appWidgetManager.updateAppWidget(appWidgetId,rv);
 context.startActivity(intentActivity);//当点击"button"按钮时启动自动Activity


3、Wdiget启动Service

RemoteViews rv = new RemoteViews(context.getPackageName(),R.layout.mywidget);
 Intent intentService = new Intent("Service名字");
 context.startService(intentService);
 appWidgetManager.updateAppWidget(appWdigetgetId,rv);