AppWidget就是我们平常在桌面上见到的那种一个个的小窗口,利用这个小窗口可以给用户提供一些方便快捷的操作。
AppWidget创建:
第一步:在res文件夹下新建一个xml文件夹,用来专门存放.xml格式文件,再在xml文件夹下创建一个xml文件,取名为appwidget.xml,其代码如下:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth = "294dp"
android:minHeight = "72dp"
android:updatePeriodMillis = "86400000"
android:initialLayout = "@layout/appwidgetlayout"
>
</appwidget-provider>
代码解析:
第一行是语言版本号以及编码类型;
第二行及以下是由appwidget-provider包裹的内容,而appwidget-provider内包裹的内容中前三行很容易理解,至于后两行,updatePeriodMillis是用来定时刷新AppWidget控件,最少不能低于30分钟,也就是30*60*1000=1800000,而initialLayout顾名思义就是启动布局,也就是AppWidget的布局,当然现在这个布局还不存在,需要创建。
创建该xml文件的目的在于定义AppWidget的属性,也就是定义AppWidget的总体外观和刷新时间以及布局来源。
第二步:在res文件夹下的layout文件夹下创建AppWidget的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<TextView android:id="@+id/txtapp" android:text="test" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:background="#ffffff"></TextView>
<Button android:id="@+id/btnSend" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Send"></Button>
</LinearLayout>
和普通的创建布局文件几乎没差。
第三步:编写一个继承于AppWidgetProvider类的子类:
public class AppWidget extends AppWidgetProvider
{
private final String broadCastString = "com.qlf.appWidgetUpdate";
/**
* 删除一个AppWidget时调用
* */
@Override
public void onDeleted(Context context, int[] appWidgetIds)
{
super.onDeleted(context, appWidgetIds);
}
/**
* 最后一个appWidget被删除时调用
* */
@Override
public void onDisabled(Context context)
{
super.onDisabled(context);
}
/**
* AppWidget的实例第一次被创建时调用
* */
@Override
public void onEnabled(Context context)
{
super.onEnabled(context);
}
/**
* 接受广播事件
* */
@Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
}
/**
* 到达指定的更新时间或者当用户向桌面添加AppWidget时被调用
* */
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
}
}
第四步,由于AppWidgetProvider是继承于BroadcastReceiver,所以其实AppWidgetProvider是一个广播,既然是广播就需要在AndroidManifest.xml中注册,如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qlf.widget" android:versionCode="1" android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="AppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/appwidget" />
</receiver>
</application>
<uses-sdk android:minSdkVersion="8" />
</manifest>
至此,一个简单的appwidget就完成了。
当然,仅仅这么的appwidget是不够的,我们需要appwidget跟程序进行交互。
于是问题就出来了,首先要知道的一点是AppWidget和主程序并不在一个进程中,也就是说主程序中的控件根本就无法在AppWidget中被找到,那么AppWidget就无法改变主程序内的控件的内容,于是就出现了一个对象,叫做RemoteViews,代码如下:
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidgetlayout);
remoteViews.setOnClickPendingIntent(R.id.btnSend, pendingIntent); //为小工具上的按钮绑定事件
首先是新建一个RemoteViews对象,传入两个参数,第一个参数是所在包名,第二个参数是AppWidget的布局文件ID。一开始我想不明白为什么要穿这两个参数,有点摸不到头脑,但是看了RemoteViews的方法之后,就觉得,这两个参数其实是一个功能,就是定位,首先是找到包,然后再找到包内的布局文件,为的是让RemoteViews的方法中能够顺利的找到AppWidget布局内的控件ID,比如说这里的R.id.btnSend。
然后RemoteViews的功能就很清晰了,就是为pendingIntent绑定按钮,或者是更新主程序内的TextView控件等等(方法未列出)。
然后问题又来了,就是PendingIntent是什么?PendingIntent叫做延时意图,而它其实并不是一个完整的意图,或者说PendingIntent其实是个空盒子,需要Intent去填充它,代码如下:
Intent intent = new Intent();
intent.setAction("com.jsdsm.appwidgetprovider");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
上两行代码再熟悉不过了,至于第三行等号左边是固定不变得,等号右边则有三种情况,分别是PendingIntent.getActivity,PendingIntent.getBroadcast和PendingIntent.getService,分别是利用PendingIntent内包裹的Intent启动一个Activity,发送一个广播和启动一个服务。
完整代码会是这样:
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
//创建一个Intent对象
Intent intent = new Intent();
intent.setAction(broadCastString);
//设置pendingIntent的作用
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidgetlayout);
//绑定事件
remoteViews.setOnClickPendingIntent(R.id.btnSend, pendingIntent);
//更新Appwidget
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
@Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(broadCastString))
{
//只能通过远程对象来设置appwidget中的控件状态
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidgetlayout);
//通过远程对象将按钮的文字设置为”hihi”
remoteViews.setTextViewText(R.id.btnSend, "hihi");
//获得appwidget管理实例,用于管理appwidget以便进行更新操作
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
//相当于获得所有本程序创建的appwidget
ComponentName componentName = new ComponentName(context,AppWidget.class);
//更新appwidget
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
super.onReceive(context, intent);
}
然后就不得不讲一讲AppWidget类内方法的执行顺序了:
首先是用户第一次在桌面添加AppWidget的时候会调用onUpdate,将一切准备工作做完,执行appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);更新;
然后用户点击按钮,由于remoteViews.setOnClickPendingIntent(R.id.btnSend, pendingIntent);的缘故,点击按钮就会将pendingIntent打开,而pendingIntent内包裹一个Intent,这个Intent的功能是发送一个广播;
然后由AndroidManifest.xml中的意图过滤器判断Action是否符合,符合,进入到onReceive方法内,判断是否符合,符合,则更新主程序,并更新AppWidget。