Android App Widget設計

App Widget也是一種UI組件,它可以讓一小塊程序(program piece)嵌入到桌面上。與普通的Widget(TextView、WebView等)相比,有以下幾個不同:

App Widget是有生命的UI組件,它會自動更新自身內容;
Widget不能自動更新自身內容,隻能被動的等待用戶的調用;
應用上,由於App Widget自動更新的特性,所以它比較適合用來設計一些天氣,新聞,日歷等功能。

App Widget的設計流程:

規劃App Widget的大小及更新時間,在/res/xml目錄新增一份XML文件;
規劃App Widget的UI,修改res/layout/main.xml;
編寫App Widget的主程序;
編輯AndroidManifest.xml,設定App Widget可接受App Widget的更新事件:android.appwidget.action.APPWIDGET_UPDATE
及設計一個App Widget至少需要以下4個文件:

res/xml/appwidget_provider.xml
res/layout/main.xml
src//HelloAppWidgetProvider.java
AndroidManifest.xml
下面是一個具體實現App Widget的例子:

新建一個Android工程HelloAppWidget,在/res目錄新建一個文件夾xml,在/res/xml目錄下新建一個appwidget_provider.xml文件:
[html] <?xml version="1.0" encoding="utf-8"?> 
    
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" 
    android:minWidth="85dp" 
    android:minHeight="30dp" 
    android:updatePeriodMillis="1000" 
    android:initialLayout="@layout/main" 
    > 
</appwidget-provider> 
<?xml version="1.0" encoding="utf-8"?>
  
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="85dp"
    android:minHeight="30dp"
    android:updatePeriodMillis="1000"
    android:initialLayout="@layout/main"
    >
</appwidget-provider>說明如下:

<appwidget-provider>標簽定義App Widget的屬性
android:minWidth  寬度
android:minHeight    長度
android:updatePeriodMillis定義App Widget的更新頻率,Android框架每隔一段時間,會callback AppWidgetProvider類的onUpdate()事件;以前android的版本設定為1毫秒為單位,現在版本為瞭省電,更新時間為30~60分鐘,所以現在設定30分鐘以內的更新意義不大,系統默認為30~60分鐘更新
android:initialLayout屬性指定此App Widget的UI layout定義,”@”符號在Android的XML定義檔案,代表「目錄」之意,因此”@layout/main”表示「layout目錄下的main.xml檔案」

為瞭界面美觀,google提供瞭App Widget的外觀設計原則,以下是官方提供的設計參考:


編輯main.xml文件:

<?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" 
    android:orientation="vertical" > 
 
    <TextView 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="@string/hello" /> 
    <TextView   
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:id="@+id/appwidget_text" 
    android:textColor="#ff0000" 
    /> 
     
 
</LinearLayout> 
<?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"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <TextView 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/appwidget_text"
    android:textColor="#ff0000"
    />
   

</LinearLayout>我們的App Widget使用LinearLayout來安排佈局,而UI為一個TextView物件。在這裡,將此TextView的id定義為”appwidget_text”。

在Android工程中添加一個新類HelloAppWidgetProvider extends AppWidgetProvider:
[java] package com.android; 
 
import java.util.Date; 
 
import android.appwidget.AppWidgetManager; 
import android.appwidget.AppWidgetProvider; 
import android.content.Context; 
import android.widget.RemoteViews; 
 
public class HelloAppWidgetProvider extends AppWidgetProvider { 
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 
        final int N = appWidgetIds.length; 
        for (int i=0; i<N; i++) { 
            int appWidgetId = appWidgetIds[i]; 
            updateAppWidget(context, appWidgetManager, appWidgetId); 
        } 
    } 
       
    public void onDeleted(Context context, int[] appWidgetIds) { 
    } 
   
    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, 
            int appWidgetId) { 
        CharSequence text; 
        java.text.DateFormat df = new java.text.SimpleDateFormat("hh:mm:ss"); 
        text = "http://blog.csdn.net/imyang2007" + "    Time:" + df.format(new Date()); 
         
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main); 
        views.setTextViewText(R.id.appwidget_text, text); 
        appWidgetManager.updateAppWidget(appWidgetId, views); 
    } 
 
 

  HelloAppWidgetProvider.java 代碼說明

上圖是目前的HelloAppWidget范例設計,說明如下:
 

  • onUpdate(): 收到ACTION_APPWIDGET_UPDATE廣播時,框架會callback此method
    onDelete(): 收到ACTION_APPWIDGET_DELETE廣播時,框架會callback此method
    AppWidgetManager: 管理App Widget的類
    在AndroidManifest.xml裡我們要讓HelloAppWidgetProider類可以接收ACTION_APPWIDGET_UPDATE廣播事件;ACTION_APPWIDGET_UPDATE是最主要的App Widget事件,當AppWidgetProvider被要求為App Widget提供”RemoteView”時,就會收到這個事件。

    RemoteViews 就是表示UI的類。res/layout/main.xml描述瞭應用程序的UI,UI裡當然包含許多組件(Widget),Android應用程序的UI就是一個View tree,view tree就是View Hierarchy。總結來說,RemotViews是一個用來表示View Hierarchy的類。通過RemoteViews可以找到UI裡的每一個組件。

    onUpdate()負責更新已經安裝在桌面上的App Widget內容,因此我們實現一個updateAppWidget()來進行真正更新的工作。onUpdate()的第二個參數為AppWidgetManager,這是一個管理AppWidgetProvider的類,我們必須通過框架callback本方法時回傳給我們的AppWidgetProvider,來更新桌面上的App Widget。onUpdate()第三個參數appWidgetIds陣列,存放需要更新的App Widget ID;框架會將需要更新的App Widget的ID回傳給onUpdate(),程序必須負責更新每一個需要更新的App Widget。

    updateAppWidget()說明:

    通過UI layout取得自已的「View Hierarchy」(UI),以RemoteViews表示;
    調用RemoteView的setTextViewText()方法,修改UI裡的「R.id.appwidget_text」組件,更改文字內容;
    調用AppWidgetProvider的updateAppWidget()方法,更新指定的App Widget,將其UI更新為RemoteView的UI;
    updateAppWidget()的第二個參數為RemoteView,即說明1.取得的UI,說明2.修改瞭此UI裡的”R.id.appwidget_text”組件,最後通過App Widget Manager更新App Widget的UI。
    App Widget的最後一步,編輯AndroidManifest.xml文件:
    [html] <?xml version="1.0" encoding="utf-8"?> 
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
        package="com.android" 
        android:versionCode="1" 
        android:versionName="1.0" > 
     
        <uses-sdk android:minSdkVersion="10" /> 
     
        <application 
            android:icon="@drawable/ic_launcher" 
            android:label="@string/app_name" > 
            <receiver android:name=".HelloAppWidgetProvider" > 
                <meta-data 
                    android:name="android.appwidget.provider" 
                    android:resource="@xml/appwidget_provider" /> 
     
                <intent-filter> 
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> 
                </intent-filter> 
            </receiver> 
     
            <activity 
                android:name=".HelloAppWidgetActivity" 
                android:label="@string/app_name" > 
                <intent-filter> 
                    <action android:name="android.intent.action.MAIN" /> 
     
                    <category android:name="android.intent.category.LAUNCHER" /> 
                </intent-filter> 
            </activity> 
        </application> 
     
    </manifest> 
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android"
        android:versionCode="1"
        android:versionName="1.0" >

        <uses-sdk android:minSdkVersion="10" />

        <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" >
            <receiver android:name=".HelloAppWidgetProvider" >
                <meta-data
                    android:name="android.appwidget.provider"
                    android:resource="@xml/appwidget_provider" />

                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                </intent-filter>
            </receiver>

            <activity
                android:name=".HelloAppWidgetActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />

                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>

    </manifest>
    說明如下:

    在<application>裡加入<receiver>標簽,指定android:name屬性為主要的provider類,即”HelloAppWidgetProvider”,註意「.」表示後面的字符串為一個「類別名」,不要忽略瞭這個重要的小數點
    在<receiver>裡加入<meta-data>標簽,指定android:resource屬性為App Widget的資源名稱,就是「@xml/appwidget_provider」,即「xml目錄的appwidget_provider.xml文檔」
    在<receiver>裡加入<intent-filter>標簽,讓App Widget可以接收APPWIDGET_UPDATE事件(event)
    至此,一個簡單的App Widget設計完成,運行效果為:
     


    最後,發現一個問題,程序並沒有實現我們的預想的每秒更新一次App Widget,而是在30~60分鐘更新一次,這是1.6之後的android版本為瞭使設備不那麼耗電,把App Widget的更新時間增加到瞭30分鐘以上。

     

    摘自 Young的專欄

發佈留言