什么是AppWidget?AppWidget就是我們平常在桌面上見(jiàn)到的那種一個(gè)個(gè)的小窗口,利用這個(gè)小窗口可以給用戶提供一些方便快捷的操作。本篇打算從以下幾個(gè)點(diǎn)來(lái)介紹AppWidget:
1.如何創(chuàng)建一個(gè)簡(jiǎn)單的AppWidget
2.如何使得AppWidget與客戶端程序交互
創(chuàng)建簡(jiǎn)單的AppWidget
在介紹之前給大家看一下程序運(yùn)行的最后結(jié)果和項(xiàng)目結(jié)構(gòu)圖,以便大家有個(gè)整體的印象。
運(yùn)行結(jié)果圖:

項(xiàng)目結(jié)構(gòu)圖:

第一步:
首先在res文件夾下新建一個(gè)名字為xml的文件夾,然后在xml目錄下創(chuàng)建一個(gè)名為appwidget01的xml文件(如上圖所示)。這個(gè)appwidget01中的內(nèi)容如下:
<?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>
這個(gè)xml是用來(lái)描述你所要?jiǎng)?chuàng)建的appWidget的一些描述信息的,比如高度、寬度、刷新間隔、布局文件等等。僅僅這個(gè)描述文件還不夠,我們看到的appWidget可都是有界面元素的呀,比如說(shuō)文本,圖片,按鈕等等,這些東西的定義都需要放到layout文件夾下面。這個(gè)文件就是上面代碼中寫(xiě)到的那個(gè)appwidgetlayout。
第二步:
在layout文件夾下面新建一個(gè)appwidgetlayout.xml文件,在這個(gè)文件中描述了appWidget的控件和布局等等信息,就和我們平常創(chuàng)建的一個(gè)activity的布局文件沒(méi)什么兩樣,因?yàn)橹皇呛?jiǎn)單的演示,所以僅用一個(gè)文本和一個(gè)按鈕。xml的內(nèi)容如下:
<?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>
第三步:
既然appWidget中存在按鈕等等控件,那么就肯定少不了處理這些控件事件的處理代碼啦。這些代碼被放在一個(gè)繼承于AppWidgetProvider的類中,在本例子中我新建了一個(gè)AppWidget的類,該類繼承于AppWidgetProvider,以后所有的AppWidget上面的控件事件都會(huì)在這個(gè)類中處理??匆幌骂惖膬?nèi)容:
public class AppWidget extends AppWidgetProvider
{
private final String broadCastString = "com.qlf.appWidgetUpdate";
/**
* 刪除一個(gè)AppWidget時(shí)調(diào)用
* */
@Override
public void onDeleted(Context context, int[] appWidgetIds)
{
super.onDeleted(context, appWidgetIds);
}
/**
* 最后一個(gè)appWidget被刪除時(shí)調(diào)用
* */
@Override
public void onDisabled(Context context)
{
super.onDisabled(context);
}
/**
* AppWidget的實(shí)例第一次被創(chuàng)建時(shí)調(diào)用
* */
@Override
public void onEnabled(Context context)
{
super.onEnabled(context);
}
/**
* 接受廣播事件
* */
@Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
}
/**
* 到達(dá)指定的更新時(shí)間或者當(dāng)用戶向桌面添加AppWidget時(shí)被調(diào)用
* */
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
}
}
各個(gè)方法的作用大家一看上面的注釋就明白了。我們暫時(shí)不需要實(shí)現(xiàn)里面的方法。
第四步:
在AndroidManifest.xml中定義一些創(chuàng)建AppWidget必要的東西,先看代碼:
<?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/appwidget01" />
</receiver>
</application>
<uses-sdk android:minSdkVersion="8" />
</manifest>
可以看到我們?cè)谂渲梦募锩娑x了一個(gè)receiver,他的名字是上面創(chuàng)建處理控件代碼的那個(gè)類,下面那個(gè)intent-filter中的action是系統(tǒng)自帶的用于更新所有appwidget的廣播動(dòng)作。然后meta-data標(biāo)簽是一個(gè)描述我們創(chuàng)建appwidget的元數(shù)據(jù),那個(gè)android:name="android.appwidget.provider"是固定的,android:resource="@xml/appwidget01"指定創(chuàng)建的appWidget的描述信息的位置。這樣程序就知道到哪里去初始化這些appWidget啦。
經(jīng)過(guò)上面四個(gè)步驟,我想您已經(jīng)能夠成功在桌面上添加小工具了,效果就是我們最前面發(fā)出的樣子。
AppWidget與程序交互
前面我們只是簡(jiǎn)單的介紹了如何創(chuàng)建一個(gè)appWidget,但是目前這個(gè)appWidget還沒(méi)有任何的交互功能。下面我們介紹一下appWidget如何與程序進(jìn)行交互。首先要介紹一個(gè)對(duì)象,這個(gè)對(duì)象在appwidget和程序的交互中很重要,他就是RemoteViews。因?yàn)閍ppwidget運(yùn)行的進(jìn)程和我們創(chuàng)建的應(yīng)用不在一個(gè)進(jìn)程中,所以我們也就不能像平常引用控件那樣來(lái)獲得控件的實(shí)例。這個(gè)時(shí)候RemoteViews出場(chǎng)了,從字面上看他的意思是遠(yuǎn)程的視圖,也就是說(shuō)通過(guò)這個(gè)東西我們能夠獲得不在同一進(jìn)程中的對(duì)象,這也就為我們編寫(xiě)appwidget的處理事件提供了幫助。我們使用一下代碼來(lái)創(chuàng)建一個(gè)RemoteViews :
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidgetlayout);
remoteViews.setOnClickPendingIntent(R.id.btnSend, pendingIntent); //為小工具上的按鈕綁定事件
可以看到上面又出現(xiàn)了一個(gè)陌生的對(duì)象pendingIntent,這個(gè)又是用來(lái)干嘛的呢?我們知道在一般的程序中綁定按鈕的點(diǎn)擊事件是直接在實(shí)現(xiàn)了OnClickListener接口的類中中完成的。不過(guò)因?yàn)閍ppwidget并不在我們應(yīng)用的進(jìn)程中,所以當(dāng)然他也訪問(wèn)不到我們?cè)趹?yīng)用中設(shè)置的onclick代碼啦。而PendingIntent就是被用來(lái)解決這個(gè)問(wèn)題的。PendingIntent可以看成是一個(gè)特殊的Intent,如果我們把Intent看成一封信,那么PendingIntent就是一封被信封包裹起來(lái)的信。這封信在remoteViews.setOnClickPendingIntent()中被“郵寄”到了appwidget, 當(dāng)appwidget中的按鈕單擊時(shí)他知道將這封信打開(kāi),并執(zhí)行里面的內(nèi)容。這樣就避免了直接從appwidget中執(zhí)行本地代碼。我們來(lái)看看PendingIntent是如何定義的:
//創(chuàng)建一個(gè)Intent對(duì)象
Intent intent = new Intent();
intent.setAction(broadCastString);
//這一步相當(dāng)于寫(xiě)信,說(shuō)明這個(gè)信的作用到底是什么,在這里表示將發(fā)送一個(gè)廣播
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
有了上面的介紹,我們?cè)趧?chuàng)建appwidget的交互應(yīng)用時(shí)就簡(jiǎn)單不少了。我們剩下要做的工作就是在appwidget在創(chuàng)建的時(shí)候調(diào)用上面說(shuō)到的方法為appwidget中的控件綁定事件,也就是在AppWidget類下的onUpdate方法中完成這個(gè)過(guò)程。
/**
* 到達(dá)指定的更新時(shí)間或者當(dāng)用戶向桌面添加AppWidget時(shí)被調(diào)用
* */
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
//創(chuàng)建一個(gè)Intent對(duì)象
Intent intent = new Intent();
intent.setAction(broadCastString);
//設(shè)置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);
}
通過(guò)上面的代碼我們就為button按鈕綁定了一個(gè)事件,這個(gè)事件的作用是發(fā)送一個(gè)廣播便于其他應(yīng)用接收、更新信息。這是appwidget發(fā)送廣播,那么appwidget如何接受來(lái)自其他程序發(fā)送的廣播呢?這就是public void onReceive(Context context, Intent intent)的功能啦。這個(gè)方法會(huì)接收來(lái)自其他應(yīng)用發(fā)出的廣播,我們只要在這個(gè)程序中過(guò)濾我們需要的廣播就能響應(yīng)其他應(yīng)用的操作來(lái)更新appwidget的信息了。要注意的是,因?yàn)閍ppwidget運(yùn)行的進(jìn)程和我們創(chuàng)建的應(yīng)用不在一個(gè)進(jìn)程中的限制,所以更新的appwidget的時(shí)候也要通過(guò)遠(yuǎn)程對(duì)象來(lái)操作,具體代碼如下:
/**
* 接受廣播事件
* */
@Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(broadCastString))
{
//只能通過(guò)遠(yuǎn)程對(duì)象來(lái)設(shè)置appwidget中的控件狀態(tài)
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidgetlayout);
//通過(guò)遠(yuǎn)程對(duì)象將按鈕的文字設(shè)置為”hihi”
remoteViews.setTextViewText(R.id.btnSend, "hihi");
//獲得appwidget管理實(shí)例,用于管理appwidget以便進(jìn)行更新操作
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
//相當(dāng)于獲得所有本程序創(chuàng)建的appwidget
ComponentName componentName = new ComponentName(context,AppWidget.class);
//更新appwidget
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
super.onReceive(context, intent);
}
總結(jié)下就是appwidget上的操作都必須借助遠(yuǎn)程對(duì)象來(lái)操作。最后看一下運(yùn)行的圖片吧:
按之前:

按之后:
