標籤:des android style http io color ar java for
何為Home Screen Widgets
Home screen Widget即稱為小工具或者中文小工具,是顯示在首頁上的views,通過後台進程更新view的資料。
Android由AppWidgetManager來管理系統的widgets。安裝apk後,會根據widget定義在widget列表中顯示該Widget的名稱、表徵圖以及所佔空間,在Android4.0中,以網格方式來顯示,有些OEM廠商會對UI進行重新設計,widget列表的展現形式會有所不同。
我們長按widget列表中的某個widget,將其拖拽到首頁上,實際上是AppWidgetManager在首頁上建立該widget的一個執行個體(instance)。可以有多個執行個體,不同執行個體用widgetID來區分它們。
widget定義兩個重要的java 類,一個是widget configurator activity,在widget執行個體產生時,被AppWidgetManager通過intent喚起進行初始化配置,其中action名為android.appwidget.action.APPWIDGET_CONFIGURE,這個java類是可選的,通常在此進行配置資料的輸入和儲存,由於資料量少,可以很方便地儲存在shared preference中。如果我們建立了兩個widget執行個體,這個配置activity會被調用兩次。
一個Java類,負責管理widget的生命週期,包括當拖拽到首頁時,需要更新時,以及拖入到垃圾桶時的處理。它是AppWidgetProvider的繼承,本質是一個廣播接收器,根據AppWidgetManager發出的廣播資訊,觸發不同的回呼函數,例如widget資料更新時間間隔(在widget定義中給出),widget執行個體的產生和刪除,第一個widget執行個體的產生,最後一個widget執行個體的刪除。
widget在頁面上的產生是由AppWidgetManager根據我們在widget定義中給出的layout檔案產生有關的UI,不是由我們的代碼直接產生,因此我們也無法向在activity中那也直接對widget中的view進行操作,需要通過RemoteViews,提交給AppWidgetManager對某個執行個體進行處理。
小例子
我們將通過小例子,詳細解讀如何建立一個widget。小例子是一個生日提醒通知器。
在產生widget執行個體時,彈出配置activity,在activity中輸入名字和出生日期,按“設定”按鈕將資料儲存在preference中,並關閉activity。
widget最上的view顯示widget_id:名字,中間左邊顯示距下一次生日的天數,點擊右邊的view可開啟某個網頁,下面顯示出生日期。
定義廣播接收器
前面提到Widget有兩個重要的java類,一個是可選的用於配置的activity,一個用於管理wdiget生命週期的廣播接收器,接收器接收AppWidgetManager的廣播訊息,來觸發各類回呼函數。我們需在AndroidManifest.xml中定義這兩個java類,下面給出廣播接收器的類定義。
<manifest …… >
<application …… android:label="TestWidget" ……> <!-- android:label就是widget列表中的widget名稱 -->
… …
<receiver android:name=".BirthDayWidgetProvider">
<meta-data android:name="android.appwidget.provider" android:resource="@xml/birthday_widget_provider"/>
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
</receiver>
</application>
</manifest>
這個類是繼承android.appwidget.AppWidgetProvider,通過meta-data設定有關的參數。
這裡補充一下meta-data的知識,最為常見的是採用鍵值對的方式,即<meta-data android:name="xxx" android:value="yyy" />,在組件中可以獲得該數值,例如在activity中:
ActivityInfo actInfo = mContext.getPackageManager().getActivityInfo( getComponentName(), PackageManager.GET_META_DATA);
String msg = actInfo.metaData.getString("activity_name");
而在service,則為ServiceInfo,在application中為ApplicationInfo,在receiver中為ActivityInfo,但用getReceiverInfo, 如果需要ComponentName參數,可以用 new ComponentName(context, MyComponent.class)來擷取。
我們看看android.appwidget.AppWidgetManager.java的有關代碼:
/**
* Sent when it is time to update your AppWidget.
*
* This may be sent in response to a new instance for this AppWidget provider having
* been instantiated, the requested {@link AppWidgetProviderInfo#updatePeriodMillis update interval} having lapsed, or the system booting.
* ……
*
* @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
*/
public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
/**
* Field for the manifest meta-data tag.
*
* @see AppWidgetProviderInfo
*/
public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
在meta-data中,android.appwidget.provider為Android指定的關鍵字,用於在對應的resource中定義App Widget Provider資訊的檔案,該檔案位於xml/下,本例子為res/xml/birthday_widget_provider.xml,定義widget的參數。
android.appwdiget.action.APPWIDGET_UPDATE是接收器監聽AppWidgetManger的廣播訊息之一,其他還監聽ACTION_APPWIDGET_ENABLED等等訊息,但在receiver中必須指明是ACTION_APPWIDGET_UPDATE,系統才可識別出接收器實際是widget,才能在widget列表中添加該widget。其他需要監聽的訊息無需在此列出。
App widget provider的定義
在manifest.xml中,通過meta-data給出appwidget provider定義所在檔案為xml/birthday_width_provider.xml,內容如下。
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="150dp"
android:minHeight="120dp"
android:updatePeriodMillis="43200000"
android:initialLayout="@layout/birday_widget"
android:configure="cn.wei.flowingflying.testwidget.ConfigBirthDayWidgetActivity"
android:previewImage="@drawable/gift"
android:resizeMode="horizontal|vertical">
</appwidget-provider>
在widget列表中顯示widget的大小為3×2。在xml中,我們定義長150dp,寬120dp,實際上widget在Home Screen佔據的空間是按網格計算的,每個網格為74dp×74dp,系統會分配所需容納的網格。一般手機網格為4×4,平板為8×7。在《Pro Android》一書中給出建議為74的N倍減去2dp(適配邊框),而在Android開發人員網站推薦定義的min長寬為70*N-30,下面是給出的一個例子:
本例間隔時間為12小時(43200000ms)。Android強烈建議1天最多隻有幾次,不要太多。從Android2.0開始,最小值為30分鐘。如果我們設定為0,表示不會自動update,我們可以通過AlarmManager類來自行控制何時update。作為實驗小例子,可以改為1小時,但不能設定太短,例如1分鐘,在模擬器的實驗中,如果時間間隔太短是不會進行觸發的。
從SDK3.1開始,使用者長按widget,可以resize widget,包括horizontal,vertical和none。要能夠resize,要求layout參數可伸縮,注意,如果size改變是沒有callback提醒的。 具體如何resize不太明確。
previewImage是在widget list的表徵圖,如果沒有這項,實用manifest檔案中定義的main icon。widget列表中的顯示也稱為preview。
配置Activity的定義
在appwidget-provider中通過android:configure定義配置的java類ConfigBirthDayWidgetActivity,這是個普通的activity,需要在AndroidManifest.xml中進行說明,並支援響應APPWIDGET_CONFIGURE action,AndroidManifest.xml的程式碼片段如下:
<activity android:name=".ConfigBirthDayWidgetActivity" android:label="配置生日小工具">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
至此,我們完成了widget的定義,即使我們沒有具體編寫任何的java類代碼,我們仍可以將其打包,並在裝置上安裝。安裝後在widget列表中看到我們的小例子TestWidget。
Widget的外觀定義
在appwidget-provider中通過android:initialLayout定義widget的外觀。相應的layout/birday_widget.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="150dp"
android:layout_height="120dp"
android:orientation="vertical"
android:background="@drawable/box1">
<TextView android:id="@+id/bd_name"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="Anonymous"
android:background="@drawable/box1"
android:gravity="center"/>
<LinearLayout android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="60dp">
<TextView android:id="@+id/bd_days"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="0"
android:gravity="center"
android:textSize="30sp"
android:layout_weight="50"/>
<TextView android:id="@+id/bd_buy"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="20sp"
android:text="Buy"
android:layout_weight="50"
android:background="#FF6633"
android:gravity="center"/>
</LinearLayout>
<TextView android:id="@+id/bd_date"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="2000/1/1"
android:background="@drawable/box1"
android:gravity="center"/>"
</LinearLayout>
和activity不同,不能直接在代碼中擷取view的對象並進行控制,需要通過AppWidgetManager用RemoteViews來進行間接控制。因此在widget外觀定義中,view需要能支援remote view,包括FrameLayout、LinearLayout、RelativeLayout、AnalogClock、Button、Chronometer、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView、AdapterViewFlipper。
Android給出widget外觀設計的guideline,見http://developer.android.com/guide/practices/ui_guidelines/widget_design.html。
通過android:background="@drawable/box1",可以定義外圍輪廓。res/drawable/box1.xml的內容如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke android:width="4dp" android:color="#888888"/> <!--定義邊框 -->
<padding android:left="2dp" android:top="2dp" android:right="2dp" android:bottom="2dp"/>
<corners android:radius="4dp" />
</shape>
Android學習筆記:Home Screen Widgets(1):關於Widget