首先要在工程文件中的res文件夹中创建一个xml文件夹,在xml文件夹中创建一个appwidget_config.xml的配置文件,具体代码如下:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_ui"
android:minHeight="140dip"
android:minWidth="140dip"
android:updatePeriodMillis="6890000" >
</appwidget-provider>
这个xml是用来描述所要创建的appWidget的一些描述信息的,所描述的属性分别有最小高度、最小宽度、刷新间隔和关联布局文件布局文件。特别要说的是AppWidget的大小是以格为单位来计算的,手机屏幕被分为一个4*4的格阵,当宽高到达一定值时就会显示并占满相应的格数,不会出现自定义不规则的大小的。网上资料显示说N格的长宽为(74*N)-2。我设定的140*140显示的是2*2的界面。
在res文件夹中的layout文件夹中创建一个layout布局文件,用于绘制AppWidget的界面。我这里绘制了一个图片视图和3个按钮。具体代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#AA000000"
android:orientation="vertical" >
<ImageView
android:id="@+id/img_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#AAAAFFFF" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/btn_provious"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="-1"
android:textColor="#FFFFFF" />
<Button
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0"
android:textColor="#FFFFFF" />
<Button
android:id="@+id/btn_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="+1"
android:textColor="#FFFFFF" />
</LinearLayout>
</LinearLayout>
功能是点击不同的按钮会显示不同的图片。其界面图如下:
其实第三第四步是同时进行的,在工程的src文件夹中创建AppWidget.java文件继承自AppWidgetProvider,并重写父类中的方法。
这个类定义了AppWidget的基本生命周期函数,具体如下:
onEnabled(Context) 当AppWidget实例第一次被创建时调用。
onReceive(Context, Intent) 接收广播事件。
onUpdate(Context , AppWidgetManager, int[] appWidgetIds) 到达指定的更新时间或用户向桌面添加widget时候调用。
onDeleted(Context, int[] appWidgetIds) 当AppWidget被删除时调用。
onDisabled(Context) 当最后一个AppWidget被删除时调用。
个人心得:onUpDate方法用于创建和更新AppWidget,将所需的ID通过intent进行传递。onReceive方法通过接收intent传送的广播事件实现AppWidget的变化操作。这两个方法在AppWidget的编写中至关重要。
想要让AppWidget显示在手机界面上,还需要在AndroidManifest文件中进行注册。注册信息如下:
<receiver android:name="MyAppWidget" >
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_providerinfo" />
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="image.one" />
<action android:name="image.two" />
<action android:name="image.three" />
</intent-filter>
</receiver>
在receive中添加meta-data注册AppWidget的配置信息文件,添加intent-filter用于添加所需intent,这里的action按照需要添加。我需要3个intent来切换3张图片所以在这里定义了3个action。
到上面一步,我们在手机上添加自定义的AppWidget已经可以正常显示了,之后我们来实现功能逻辑。
正如上面所说的,我们用到了onUpDate与onReceive方法。
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {
for (int i = 0; i < appWidgetIds.length; i++) {
int appWidgetId = appWidgetIds[i];
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_ui);
Intent intent1 = new Intent("image.one");//定义intent
Intent intent2 = new Intent("image.two");
Intent intent3 = new Intent("image.three");
PendingIntent pendingIntentOne = PendingIntent.getBroadcast(context, 0, intent1, 0);//用PendingIntent将Intent包裹起来
PendingIntent pendingIntentTwo = PendingIntent.getBroadcast(context, 0, intent2, 0);
PendingIntent pendingIntentThree = PendingIntent.getBroadcast(context, 0, intent3, 0);
views.setOnClickPendingIntent(R.id.btn_provious, pendingIntentOne);//按钮点击事件监听
views.setOnClickPendingIntent(R.id.btn_play, pendingIntentTwo);
views.setOnClickPendingIntent(R.id.btn_next, pendingIntentThree);
//更新AppWidget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
在onUpDate方法中用到了RemoteViews与PendingIntent, 是因为AppWidget和其原本的App并不在同一个进程中,而是运行在HomeScreen进程当中,因此,在控件监听器的绑定,更新等操作都会与以前基本的方法有所不同。
PendingIntent是一个特殊的Intent,实际上它像一个邮包,其中包裹着真正的Intent,当邮包未打开时,Intent是被“挂起”的,所以并不执行,只有当邮包拆开时才会执行。它与Intent的区别在于:Intent 是及时启动,intent 随所在的activity 消失而消失。 进程A创建PendingIntent,发送给进程B,进程B此事并不执行,直到用户出发某一事件时,包裹被拆开,里面的Intent真正执行。所以Intent什么时候被执行是不知道的,由用户出发事件来决定。
RemoteViews表示了一系列view对象,即AppWidget所有的控件。它的应用方法如上源码所示,使用了views.setOnClickPendingIntent(所需控件的ID,定义的pendingintent)方法来为控件绑定监听器。
以下是onReceive方法,用于实现切换图片功能。
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
ComponentName thiswidget = new ComponentName(context,MyAppWidget.class);//定义容器
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.appwidget_ui);//定义RemoteViews
//定义AppWidgetManager,用于之后更新AppWidget
AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(context);
views.setImageViewResource(R.id.img_widget,
R.drawable.dmkamsndymzzz);
//通过Intent判断点击的哪个按钮,并作出反应
if (intent.getAction().equals("image.one")) {
//更新图片
views.setImageViewResource(R.id.img_widget,
R.drawable.syfj631139232);
}
if (intent.getAction().equals("image.two")) {
views.setImageViewResource(R.id.img_widget,
R.drawable.syrx2623204137);
}
if (intent.getAction().equals("image.three")) {
views.setImageViewResource(R.id.img_widget,
R.drawable.dmkamsndymzzz);
}
//更新AppWidget
appWidgetManager.updateAppWidget(thiswidget, views);
}
代码具体功能都已注释。从上源码中可以看到Intent在AppWidget中的作用。
由于AppWidget与以往的程序有所不同,需要花一段时间去理解。我觉得最重要的是理解PendingIntent和RemoteViews,了解Intent在AppWidget中的重要作用。