AppWidget就是在桌面上见到的一个个小窗口,利用这个小窗口可以给用户提供一些方便快捷的操作,如通过桌面widget可以开发出桌面媒体播放器,如酷狗widget,本文详细介绍android 中的appwidget的开发。
开发效果图如下所示:
1.首先在res文件夹下新建一个名字为xml的文件夹,然后在xml目录下创建一个xml文件,这个xml是用来描述你所要创建的appWidget的一些描述信息的,比如高度、宽度、刷新间隔、布局文件等等,例如我们创建一个名为appwidget的xml属性文件,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="240dp"
android:minHeight="60dp"
android:updatePeriodMillis="1000"
android:initialLayout="@layout/layout_appwidget">
</appwidget-provider>
android:minWidth:定义窗口小部件的最小宽
android:minHeight:定义窗口小部件的最小高
android:updatePeriodMillis:定义窗口小部件自动更新的时间间隔,在后期的android版本,该属性作用不大,google已经设置为1天更新一次
android:initialLayout:定义窗口小部件的布局文件,由此可以看出我们还需要为窗口小部件创建一个布局文件
2.窗口小部件的布局layout_appwidget.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="vertical" >
<ImageView
android:id="@+id/wigdet_bg1"
android:background="@drawable/bg_image0"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/wigdet_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="appwidget"
android:autoLink="all"
android:background="#00000000"
android:ellipsize="marquee"
android:focusable="true"
android:gravity="center_horizontal"
android:marqueeRepeatLimit="marquee_forever"
android:paddingTop="5dp"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#000000"
android:textSize="18dp" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/wigdet_text"
android:layout_marginLeft="5dp"
android:layout_marginRight="10dp">
<ImageButton
android:id="@+id/wigdet_btnLeft"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:background="#00000000"
android:scaleType="fitXY"
android:src="@drawable/logo" />
<ImageButton
android:id="@+id/wigdet_btnRight"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#00000000"
android:scaleType="fitXY"
android:layout_alignParentRight="true"
android:layout_marginRight="3dp"
android:src="@drawable/widget_rightbtn" />
<ImageView
android:id="@+id/wigdet_bg2"
android:background="@drawable/bg_image1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/wigdet_btnRight"
android:layout_toRightOf="@id/wigdet_btnLeft"
android:layout_alignBottom="@id/wigdet_btnLeft"
android:layout_marginRight="3dp"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="47dp"
android:layout_toLeftOf="@id/wigdet_btnRight"
android:layout_toRightOf="@id/wigdet_btnLeft"
android:layout_alignBottom="@id/wigdet_btnLeft"
android:layout_marginRight="3dp">
<ImageButton
android:id="@+id/wigdet_imgbtn1"
android:layout_marginBottom="2dp"
android:layout_marginTop="3dp"
android:layout_weight="1"
android:layout_marginLeft="5dp"
android:scaleType="fitXY"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#00000000"/>
<ImageButton
android:id="@+id/wigdet_imgbtn2"
android:layout_marginBottom="2dp"
android:layout_weight="1"
android:layout_marginLeft="5dp"
android:scaleType="fitXY"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="3dp"
android:background="#00000000" />
<ImageButton
android:id="@+id/wigdet_imgbtn3"
android:layout_marginBottom="2dp"
android:layout_marginTop="3dp"
android:layout_weight="1"
android:layout_marginLeft="5dp"
android:scaleType="fitXY"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#00000000" />
<ImageButton
android:id="@+id/wigdet_imgbtn4"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="2dp"
android:layout_weight="1"
android:layout_marginTop="3dp"
android:scaleType="fitXY"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#00000000"/>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
该布局文件和android应用开发的其他布局设计一样。
3.通过manifest文件注册该窗口小部件
<!-- 桌面插件AppWidget 广播接收器 -->
<receiver android:name="com.spreadtrum.activity.SprdAppWidget">
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget"/>
<intent-filter >
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="com.spreadtrum.action.WIDGET"></action>
</intent-filter>
</receiver>
注册方式和广播接收器的注册方式相同,由此可以看出窗口小部件其实就是一个广播接收器,只是它是一个比较特殊的接收器,所有的窗口小部件都接收android.appwidget.action.APPWIDGET_UPDATE 动作的广播,该广播根据android:updatePeriodMillis设定的间隔时间发出广播,用于定时更新桌面上的所有窗口小部件。
上面注册过程中,元数据名称android:name必须为android.appwidget.provider,资源就是第一步中为窗口小部件设置的属性文件。可以通过在该广播接收器中注册自定义的动作以使窗口小部件接收自定义的广播,如上所示,为该widget添加了一个自定义com.spreadtrum.action.WIDGET动作的广播。
4.自定义一个继承于AppWidgetProvider 的类
覆写父类的一下方法:
//广播接收
public void onReceive(Context context, Intent intent)
//当用户向桌面添加AppWidget时被调用,同时接收android.appwidget.action.APPWIDGET_UPDATE广播
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
//删除一个AppWidget时调用
public void onDeleted(Context context, int[] appWidgetIds)
//AppWidget的实例第一次被创建时调用
public void onEnabled(Context context)
//最后一个appWidget被删除时调用
public void onDisabled(Context context)
具体类实现如下:
public class SprdAppWidget extends AppWidgetProvider{
private final static String TAG = "SprdAppWigdet";
private final static int[] imageBtnIds ={R.id.wigdet_imgbtn1,R.id.wigdet_imgbtn2,R.id.wigdet_imgbtn3,R.id.wigdet_imgbtn4};
private final static int defaultBgId = R.drawable.wos;
/**
* 接受广播事件
*/
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getAction().equals("com.spreadtrum.action.WIDGET")) {
//因为appwidget运行的进程和我们创建的应用不在一个进程中,所以我们也就不能像平常引用控件那样来获得控件的实例。只能通过RemoteViews远程的视图来引用,
//也就是说通过RemoteViews我们能够获得不在同一进程中的对象,这也就为我们编写appwidget的处理事件提供了帮助。
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.layout_appwidget);
ComponentName componentName =new ComponentName(context, SprdAppWidget.class);
//编写该广播下的业务逻辑,如果更换图像控件的图片等等。
AppWidgetManager.getInstance(context).updateAppWidget(componentName, remoteViews);
}
super.onReceive(context, intent);
}
/**
* 到达指定的更新时间或者当用户向桌面添加AppWidget时被调用
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// TODO Auto-generated method stub
final Context cxt = context;
Log4debug.i(TAG, "onUpdate", null);
//只能通过远程对象来设置appwidget中的控件状态
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.layout_appwidget);
//循环桌面上所有的该窗口小部件,(可以在桌面上同时添加多个相同的窗口小部件)
for (int i = 0; i < appWidgetIds.length; i++) {
//为窗口小部件上的按钮设置单击事件
Intent intent1 = new Intent(context, SprdAppMainActivity.class);
remoteViews.setOnClickPendingIntent(R.id.wigdet_btnLeft, PendingIntent.getActivity(context, 0, intent1, PendingIntent.FLAG_CANCEL_CURRENT));
Intent broadIntent = new Intent(context,PopupWindowActivity.class);
remoteViews.setOnClickPendingIntent(R.id.wigdet_btnRight,PendingIntent.getActivity(context, i, broadIntent, PendingIntent.FLAG_UPDATE_CURRENT));
//为窗口小部件上的图像控件设置图像
for (int j = 0; j < imageBtnIds.length; j++) {
remoteViews.setImageViewResource(imageBtnIds[j], defaultBgId);
}
//更新窗口小部件
appWidgetManager.updateAppWidget(appWidgetIds[i], remoteViews);
}
//网络已连接,启动内容更新线程
if (CommonUtil.checkNetWorkStatus(context)) {
new Thread (new Runnable() {
public void run() {
// TODO Auto-generated method stub
try {
................................
}
} catch (Exception e) {
// TODO Auto-generated catch block
Log4debug.e(TAG, "run", e.getMessage());
}
}
}).start();
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
/**
* 删除一个AppWidget时调用
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onDeleted(context, appWidgetIds);
}
/**
* AppWidget的实例第一次被创建时调用
*/
@Override
public void onEnabled(Context context) {
// TODO Auto-generated method stub
super.onEnabled(context);
}
/**
* 最后一个appWidget被删除时调用
*/
@Override
public void onDisabled(Context context) {
// TODO Auto-generated method stub
super.onDisabled(context);
}
}
PendingIntent 在前文中作了详细介绍,请参看PendingIntent,到此appwidget开发完成,当然可以在注册widget时候添加多个动作。