當最基本的控件,如Clock和Picture Frame Home Screen ,隨第一款Android手機的發(fā)布后,Android用戶(hù)就開(kāi)始嘗試編寫(xiě)各種應用Widget(小工具)了,隨著(zhù)Widget API的公開(kāi),為開(kāi)發(fā)人員帶來(lái)了全新有趣的開(kāi)發(fā)模式,除了傳統的電話(huà)應用外,還可以做其它方面的應用開(kāi)發(fā)。開(kāi)發(fā)人員可以使用Widget API(包含在A(yíng)ndroid 1.5中,最新版本已經(jīng)到Android 2.0了)創(chuàng )建簡(jiǎn)單的控件,然后在新的Widget中顯示和使用這些控件。
本文向你介紹如何從零開(kāi)始創(chuàng )建一個(gè)主屏幕應用Widget,通過(guò)使用AlarmManager接口,以用戶(hù)設定的時(shí)間間隔更新圖片。你將看到如何創(chuàng )建一個(gè)Widget,以及如何隨機地從一組圖片中選擇一張圖片顯示,根據用戶(hù)設定的時(shí)間間隔周期性改變顯示的圖片。
創(chuàng )建一個(gè)簡(jiǎn)單的Widget包括以下幾個(gè)步驟:
1、創(chuàng )建一個(gè)RemoteView,由它為Widget提供用戶(hù)界面;
2、將RemoteView綁定一個(gè)Activity(行為)實(shí)現AppWidgetProvider接口;
3、在A(yíng)ndroid manifest配置文件中提供Widget的關(guān)鍵配置信息。
項目準備
一個(gè)Widget就是一個(gè)處理特定行為的BroadcastReceiver,AppWidgetProvider接口為開(kāi)發(fā)人員提供了一個(gè)框架來(lái)簡(jiǎn)化處理這些行為,它包括以下方法:
1、onEnabled():創(chuàng )建第一個(gè)Widget時(shí)調用,如果可以,應在這里進(jìn)行全局初始化。
2、onDisabled():它和onEnabled()相反,創(chuàng )建最后一個(gè)Widget時(shí)才調用它,如果可以,應在這里進(jìn)行全局清理。
3、onUpdate():當Widget需要更新它的View時(shí)調用,用戶(hù)第一次創(chuàng )建Widget時(shí)也需要調用它。
4、onDeleted():當Widget的一個(gè)特定實(shí)例被刪除時(shí)調用,清理特定實(shí)例應放在這里進(jìn)行。
5、onReceive():此方法默認情況下處理BroadcastReceiver行為,并調用上面的方法(警告:根據相關(guān)文檔記載,需要開(kāi)發(fā)人員自己處理某些特殊情況,更多信息請看下面的說(shuō)明)。
作者注:點(diǎn)擊此鏈接(http://groups.google.com/group/android-developers/browse_thread/thread/365d1ed3aac30916/e405ca19df2170e2?pli=1)檢查有關(guān)AppWidget框架的缺陷信息,討論內容包括解決此問(wèn)題的代碼(也可從本文下載示例代碼,http://www.developer.com/img/2009/08/3833306_appwidget_code.zip),如果它們并不存在,可以將Widget標識符傳遞給onUpdate()方法。
為了讓Android識別Widget,需要在manifest文件中加入一個(gè)標準的標記,下面的代碼片段顯示了一個(gè)示例:
1 <receiver android:name="ImagesWidgetProvider">你可能已經(jīng)注意到,和常見(jiàn)的定義不一樣,小節引用了一個(gè)XML文件資源,這個(gè)文件為Widget定義了額外的數據,與AppWidgetProviderInfo類(lèi)一致,這里定義的信息是不變的,因此這個(gè)例子不包括updatePeriodMillis的值,因為這個(gè)程序允許用戶(hù)修改與更新時(shí)間,如果你在這里分配updatePeriodMillis,它就不能這樣做。下面是imageswidget_info.xml文件的完整代碼:
1 <?xml version="1.0" encoding="utf-8"?>標記定義了Widget的大小,默認布局和創(chuàng )建Widget實(shí)例時(shí)的啟動(dòng)行為配置,為了讓W(xué)idget在主屏幕上更好地顯示,Widget必須保持一定的大小,主屏幕分為特定大小的單元格,Google提供的基本原則是用你想占用的單元格數量乘以74,再減去2。在這個(gè)例子中,Widget應該是一個(gè)正方形,長(cháng)和寬都各占兩個(gè)單元格,因此大小就是74*2-2=146.
實(shí)現onUpdate()
不顯示內容的Widget是沒(méi)有用的,幸好這個(gè)Widget的RemoteView對象很容易實(shí)現,它使用一組存儲在應用程序的drawable資源目錄下的圖片,程序使用R.drawable.[imagename]引用這些圖片資源,代碼需要創(chuàng )建一個(gè)數組容納圖片的名字,這樣從這些圖片名字中隨機選擇一個(gè)就可以了。下面的代碼片段顯示了onUpdate()的實(shí)現,它隨機顯示一張圖片:
1 @Override注意onUpdate()方法使用Widget實(shí)例列表作為最后的參數,每個(gè)實(shí)例必須分開(kāi)處理,由于A(yíng)pp Widget框架的現有缺陷,有些實(shí)例可能不可見(jiàn)或不能啟用,但在這個(gè)例子中可以忽略這些問(wèn)題。請記住,你自己在實(shí)現時(shí)可能想跟蹤哪個(gè)Widget是真正激活的。
你可以下載本文提供的代碼(http://www.developer.com/img/2009/08/3833306_appwidget_code.zip),查看其中的R.layout.widget XML布局定義,它基本上只是一個(gè)ImageView,RemoteViews只能使用一組有限的View對象,包括Button,ImageButton,ImageView,TextView,AnalogClock,Chronometer和ProgressBar,并且只能在FrameLayout,LinearLayout或RelativeLayout內使用。請保持RemoteView簡(jiǎn)單,因為訪(fǎng)問(wèn)是通過(guò)setImageViewResource() 和 setTextViewText()方法控制的,它們的目的是在另一個(gè)進(jìn)程內畫(huà)一個(gè)View,因此你的應用程序比正常布局要少些控制。
自此,Widget最基本的部分完成了,但為了讓用戶(hù)配置圖片更新之間的時(shí)間間隔,你必須要實(shí)現配置

實(shí)現Widget的Activity配置
和manifest 文件中定義的ImagesWidgetConfiguration類(lèi)似,任何Activity配置都有兩個(gè)特殊情況:
1、只要一啟動(dòng),它的目的就是返回結果,因此必須要調用setResult()方法返回一個(gè)適當的結果(結果要么是RESULT_CANCELED或RESULT_OK)。
2、在設置結果時(shí),Widget標識值必須放在額外引用的AppWidgeManager.EXTRA_APPWIDGET_ID中。
下面的代碼片段顯示了如何處理這兩個(gè)例外,如果用戶(hù)中途退出Activity,調用setResult()將默認值設為RESULT_CANCELED。
1 Bundle extras = launchIntent.getExtras();除了這兩個(gè)限制外,你可能還要實(shí)現你喜歡的Activity配置,圖1顯示了這個(gè)例子的簡(jiǎn)單Activity配置,如果你RESULT_CANCELED,Widget不會(huì )顯示給用戶(hù),如果你返回RESULT_OK,用戶(hù)才看得見(jiàn)Widget,你可以使用任意存儲機制保存Widget實(shí)例的配置數據,這個(gè)例子使用的是SharedPreferences接口,存儲Widget標識更新之間的時(shí)間,你可以下載本文配套的代碼查看完整的實(shí)現(http://www.developer.com/img/2009/08/3833306_appwidget_code.zip)。

圖 1 配置界面:這個(gè)簡(jiǎn)單的配置界面讓用戶(hù)設置圖片刷新間隔時(shí)間
實(shí)現更新調度
正如前面提到的,AppWidgetProviderInfo類(lèi)中的配置值是不變的,因為updateTimeMillis值就在這個(gè)類(lèi)中,任何有AppWidgetProviderInfo的Widget實(shí)例,只要設置了這個(gè)值,Widget就會(huì )根據其頻率更新,沒(méi)有辦法修改它,因為這個(gè)應用程序應該讓用戶(hù)配置Widget中圖片刷新的頻率,因此你必須自動(dòng)動(dòng)手實(shí)現。
AlarmManager類(lèi)是最常用的更新機制,因為它支持重復通知,這些通知是將被觸發(fā)的簡(jiǎn)單的PendingIntent對象。
你可能會(huì )想你可以創(chuàng )建一個(gè)具有AppWidgetManager.ACTION_APPWIDGET_UPDATE的Intent對象,然后給特定的Widget標識符設置額外的值,接著(zhù)你就可以通過(guò)調用AlarmManager的setRepeating()方法,采用調度機制反復地更新,遺憾的是,這種做法行不通,Android系統反復使用Intents匹配行為和方案值,那些“額外的”值是不會(huì )拿去對比的。實(shí)際上,解決方案非常簡(jiǎn)單:首先為你的Widget定義一個(gè)方案,然后用它定義唯一的Intent實(shí)例。下面的代碼片段顯示如何實(shí)現這個(gè)目標:
1 Intent widgetUpdate = new Intent(); 在ImagesWidgetConfiguration Activity中你會(huì )發(fā)現前面的代碼,manifest文件顯示
在A(yíng)ppWidgetProvider的onReceive()方法內提供了一個(gè)簡(jiǎn)單的解決方案,首先,檢查更新行為,如果它是一個(gè)更新,確保它不包含scheme值,如果它不是更新,那么調度AlarmManager 的PendingIntent不會(huì )觸發(fā)這個(gè)行為,在這種情況下,檢查每個(gè)Widget標識符是否有一個(gè)配置選項值,如果沒(méi)有,那么在配置Widget之前,你知道這個(gè)更新行為被接收到。但如果選項值有效,那么你知道可以調度PendingIntent,在onReceive()方法中你可以看到完整的邏輯實(shí)現。
這個(gè)方案允許多個(gè)Widget同時(shí)顯示,如圖2所示。更新也可以以不同的頻率進(jìn)行,每個(gè)顯示的圖片是從圖片集中隨機選擇的。

圖 2 多個(gè)Widget實(shí)例:這里顯示了兩個(gè)不同的同時(shí)運行的實(shí)例
至此,你已經(jīng)看到了如何在A(yíng)ndroid平臺上創(chuàng )建一個(gè)基本的Widget,每個(gè)Widget實(shí)例按獨立的方式調度,由用戶(hù)自行配置,但App Widge框架不直接支持,因此需要自動(dòng)動(dòng)手編碼實(shí)現。以后我將在此Widget基礎上講解如何從互聯(lián)網(wǎng)上下載圖片,以及如何直接在屏幕上控制圖片,敬請期待吧!
聯(lián)系客服