一.开发Widget步骤
1.添加Widget的描述文档(xml)--对此Widget的相关设置,如宽和高,更新时间,显示Layout,Activity连接相关
代码如下:
<?xml version="1.0" encoding="utf-8"?> <!--一定要声明为appwidget-provider,则说明是widget应用程序--> <appwidget-provider android:minWidth="320dp" android:minHeight="80" android:updatePeriodMillis="10000" <!--widget的显示布局界面--> android:initialLayout="@/layout/activity_main"> </appwidget-provider> |
2.创建一个继承自AppWidgetProvider的类,以实现对Widget的更新,其实AppWidgetProvider继承自BroadcastReceiver。
3.定义组件layout文件
4.配置Manifest
(1)添加Receiver,选择继承自AppwidgetProvider的类
(2)为继承自AppwidgetProvider类的类添加元数据meta-data
<meta-data
android:name="android.appwidget.provider"
<!--必须为android.appwidget.provider,描述此应用是个Widget程序-->
android:resource="@xml/widget_info"/>
<!--声明描述widget的xml文件位置-->
(3)添加Activity---如果有链接的话:
Intent-Filter:action--ACTION_APPWIDGET_CONFIGURE
在Info(xml)文件中声明configuration = “Activity所在包”
代码如下
<application <receiver android:name="appWidget"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info"/> </receiver> </application> |
二.代码如下
res/xml/widget_info:Widget程序配置文件 <?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="80dp" android:minHeight="100dp" android:updatePeriodMillis="1000" <!--更新时间-,ms级别-> android:initialLayout="@layout/activity_main" Android:configure = “一个配置widget的界面,不是继承自AppWidget的那个类”> <!--初始化布局界面,widget显示界面--> </appwidget-provider> |
相关属性 --android:minWidth/minHeight---声明widget的最小宽和高,参照下面的设计标准即可(4*1 3*3 。。。。。。) --android:updatePeriodMillis 声明好久更新一次widget,通过调用onUpdate(),单位是ms。 不过,这个属性声明的时间实际上并不会太准,所以最好是使用一个alarm来实时更新widget,并且设置updatePeriodMillis=”0”即可。 --android:configure 配置widget的一个activity --android:widgetCategory 声明appwidget是否能被放置到桌面和锁屏界面上--android:InitialKeyguardLayout 指定锁屏界面的layout和initialLayout原理是一样的。 |
res/layout/activity_widget <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" >
<TextView android:id="@+id/showText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:text="TextView" />
</RelativeLayout> |
Src/com.ccp.wj.appwidget/AppWidget.java public class appWidget extends AppWidgetProvider { private static String []text = {"ccp", "wj", "chj"}; private static int count =0; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // TODO Auto-generated method stub if(count >= 2)count = 0; count++; update( context, appWidgetManager, appWidgetIds); super.onUpdate(context, appWidgetManager, appWidgetIds); }
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // TODO Auto-generated method stub RemoteViews rvs = new RemoteViews(context.getPackageName(),R.layout.activity_main); rvs.setTextViewText(R.id.showText, text[count]); appManager.updateWidget(appWidgetIds, rvs);//更新 }
}
|
Widget与布局通过RemoteViewsonic通信 |
Res/AndroidManifest.xml <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <receiver android:name="appWidget"> <intent-filter> <!--public static final String ACTION_APPWIDGET_UPDATE Added in API level 3 Sent when it is time to update your AppWidget. This may be sent in response to a new instance for this AppWidget provider having been instantiated, the requested update interval having lapsed, or the system booting. The intent will contain the following extras:
See Also AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) Constant Value: "android.appwidget.action.APPWIDGET_UPDATE" --> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info"/> </receiver> | ||
注:只有在第一次安装程序时才会加载widget |
二.设计Widget
竖直方向vertical
Ceil piexl
4*1 320*100
4*2 320*200
3*3 240*300
2*2 160*200
水平方向horizon
4*1 424*74
3*3 318*222
2*2 212*148
三.指定一个Configure界面
即添加widget时,不是马上添加widget而是进入配置界面
1.在manifest中声明Activity
<activity android:name=".ExampleAppWidgetConfigure"> |
2.在appwidget_info.xml中声明android:configure
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" |
四.为AppWidget的控件添加相应事件
调用setOnClickPendingIntent(int, PendintIntent)
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { |
五.定时刷新widget
因为要考虑到省电等问题,所以在widget_info.xm文档中标识的android:updatePeriodMillis的时间并不能保证及时刷新,一般来说是30分钟,我们一般在onUpdate()中添加一个Service来保证widget的实时刷新,具体实现方法如在其他Activity中更新widget一样
更新widget
1.首先,从运行的Activity中获取widget的id Intent intent = getIntent();Bundle extras = intent.getExtras();if (extras != null) { 2.配置appWidget的设置 3.配置完毕,通过getInstance(context)获取一个AppWidgetManager的实例。 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 4.通过updateAppWidget(int ,RemoteViews)由RemoteViews更新appWidget。 RemoteViews views = new RemoteViews(context.getPackageName(), 5.最后创建一个返回Intent,设置一个Activity Result并且finish掉这个Activity。 Intent resultValue = new Intent(); Tip:当你的configure Activity第一次打开,设置Activity的RESULT_CANCEL,这样,如果用户在配置之前就退出了,那么app widget host就会提醒这个配置取消了,并且不会被添加。 |
2.实现实时更新
--在onUpdate()中添加一个alarm,定时更发送广播并在广播接收器中更新widget
代码如下
appWidgetProvider @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // TODO Auto-generated method stub super.onUpdate(context, appWidgetManager, appWidgetIds); Log.d(tag, "onUpdate--widget刷新"); final int N = appWidgetIds.length; // Perform this loop procedure for each App Widget that belongs to this provider updateWidget(context,N,appWidgetManager,appWidgetIds); //每分钟刷新一次时间 if(setCount<1) setRefershWidgetTime(appWidgetIds); } /** * 定时刷新widget的时间 * @param appWidgetIds */ public void setRefershWidgetTime(int[] appWidgetIds){ AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(StringHelper.WIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); int triggerTime = 60*1000; am.setRepeating(AlarmManager.RTC, System.currentTimeMillis()+triggerTime, triggerTime, pi); setCount++; } |
BroadcastReceiver package com.ccp.wj.doit.receiver; import com.ccp.wj.doit.utils.DateUtils; import com.ccp.wj.doit.utils.StringHelper; import com.ccp.wj.doit.widget.AppWidgetConfigureActivity; import com.ccp.wj.doitweatherforcast.R; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.widget.RemoteViews; public class WidgetUpdateReceiver extends BroadcastReceiver { private int h,m; private int[]imgId=new int[4]; @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Bundle b = intent.getExtras(); if(b!=null){ int appWidgetIds[]= b.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS); updateWidgetTime(context,appWidgetIds); } } private void updateWidgetTime(Context context, int[] appWidgetIds) { // TODO Auto-generated method stub int N = appWidgetIds.length; for (int i=0; i<N; i++) { int appWidgetId = appWidgetIds[i]; widgetInit(); // Create an Intent to launch ExampleActivity // Get the layout for the App Widget and attach an on-click listener // to the button RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout_4x1); views.setImageViewResource(R.id.clock_0, imgId[0]); views.setImageViewResource(R.id.clock_1, imgId[1]); views.setImageViewResource(R.id.clock_2, R.drawable.widget_white); views.setImageViewResource(R.id.clock_3, imgId[2]); views.setImageViewResource(R.id.clock_4, imgId[3]); //get the appWidgetManager AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); appWidgetManager.updateAppWidget(appWidgetId, views); } }
private void widgetInit() { // TODO Auto-generated method stub h = DateUtils.getHours(); m = DateUtils.getMinutes(); String hour[]=new String[2],minutues[]=new String[2],hh,mm; if(h < 10){ imgId[0]=StringHelper.clock_img_id[0]; imgId[1]=StringHelper.clock_img_id[h]; } else{ hh=String.valueOf(h); hour[0]=hh.substring(0, 1); hour[1]=hh.substring(1); imgId[0] = StringHelper.clock_img_id[Integer.parseInt(hour[0])]; imgId[1]=StringHelper.clock_img_id[Integer.parseInt(hour[1])]; } if(m<10){ imgId[2]=StringHelper.clock_img_id[0]; imgId[3]=StringHelper.clock_img_id[m]; } else{ mm = String.valueOf(m); minutues[0]=mm.substring(0, 1); minutues[1]=mm.substring(1); imgId[2] = StringHelper.clock_img_id[Integer.parseInt(minutues[0])]; imgId[3]=StringHelper.clock_img_id[Integer.parseInt(minutues[1])]; } }
}
|
六.多个widget放置在桌面时响应事件混乱
当在屏幕上有多个widget时,无论在哪个widget上点击,响应事件的总是最后一个widget,原因就是PendingIntent传值时出现了错误,每次添加的新PendingIntent把原来的PendingIntent的替换掉了、
PendingIntent pi = PendintIntent.getBroadcast(context, 0, intent,0);
我们固定把第二个参数设置了为0.而第二个参数表示的是requestCode表示发送器的私有请求码,相当于pendingIntent的一个Id,PendingIntent会根据这个请求码来判断是否存在相同的PendingIntent,如果存在则替换,所以出现了以上情况。
建议PendingIntent pendingIntent = PendingIntent.getBroadcast(context,appWidgetId,intent,0);
七.获取widget的所有id--appWidgetIds
appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName("com.ccp.wj.doitweatherforcast","com.ccp.wj.doit.widget.DoItWeatherWidget")); |