Email me: jollen # jollen.org

more: Jollen 的 Embedded Linux 教育訓練

« June 2009 | (回到Blog入口) | August 2009 »

July 2009 歸檔

July 10, 2009

Jollen 的 Android 教學,#19: 什麼是App Widget?

App Widget是Cupcake(Android 1.5)所提供的一個功能,這是一個很實用而且能有很大創意想像空間的功能。什麼是App Widget呢?請看底下的操作示範。

在Android桌面長壓約3秒,出現一個選單,如圖1。

app-widget
圖1:新增項目至桌面

2. 選擇「Widget」,加入”HelloWidget”

app-widget
圖2:加入自行設計的Widget

桌面上出現了一個「Widget」

app-widget
圖3:在Android桌面上出現我們自已設計的App Widget

app-widget
圖4:加入了音樂撥放器App Widget至桌面

這就是App Widget的應用,可以將一個小塊程式(program piece)嵌入到桌面上。App Widget也是一種UI組件,先前所介紹的TextView、WebView等也泛稱為Widget,二者在應用上的差異該怎麼思考呢?以下是幾點看法:

1. App Widget是有生命的UI組件,他會自動更新本身的內容
2. Widget是沒有生命的UI組件,它不會自我更新,只能等待使用者的操作
3. 應用上,App Widget能提供不斷更新的內容,很適合用來設計天氣、時鐘、新聞等主動式應用程式
4. Widget應用上只用來製作UI,而UI因為只能等待使用者來操作,所以過去我們所撰寫的Android應用程式都是屬於被動式應用程式

讓App Widget能「主動」更新自身內容的方法是透過一個「時間觸發裝置」,Android框架會根據我們設定的時間間隔,不斷地callback我們的App Widget。後續將再說明App Widget的做法,並解釋這個部份。

Jollen 的 Android 教學,#20: 如何設計一個小型的App Widget?

Android的ApiDemo範例庫提供了一個很不錯的App Widget範例;不過,對初學者來說,這個範例可能稍嫌繁瑣。在這裡另外提供一個HelloAppWidget範例如下:

/* 範例:HelloAppWidget.java */
package com.moko.hellowidget;
  
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;
      	
    	text = "www.jollen.org";
      	
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
        views.setTextViewText(R.id.appwidget_text, text);
  
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

一個很簡單的App Widget就是只需要這麼幾行程式碼,HelloAppWidget範例就是先前的App Widget操作示範使用的範例,HelloAppWidget會在桌面嵌進一個TextView組件,並顯示”www.jollen.org”字串。

由此範例可以了解,App Widget使用到Android提供的AppWidgetProvider類別,建議可以先行快速瀏覽Android Reference文件,以了解此類別的大略用法。

App Widget的設計流程

設計一個App Widget的流程,並不是由寫程式開始,所以上述的範例程式,並不是首要的重點。實作一個App Widget的過程,用到了過去教學裡的所有觀念,因此對以下的流程述描有不了解的地方,可以再回頭覆習過去的教學。

App Widget設計流程:

1. 規劃App Widget的大小以及更新時間,在res/xml/裡新增一份XML文件,命名為appwidget_provider.xml
2. 規劃App Widget的UI,修改res/layout/main.xml
3. 撰寫App Widget主程式,如上例
4. 編輯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

有了設計流程後,接下來一一說明每個步驟的實作,以及技術重點。

July 11, 2009

Jollen 的 Android 教學,#21: appwidget_provider.xml-描述App Widget屬性的資源檔

以下分別說明HelloAppWidget的實作,以及技術重點。

appwidget_provider.xml-描述App Widget屬性的資源檔

這個檔案主要描述App Widget的幾個屬性:

  • 長度(width)
  • 高度(height)
  • 更新頻率
  • UI layout檔

以下是appwidget_provider.xml的完整內容:

<?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="3000"
    android:initialLayout="@layout/main"
    >
</appwidget-provider>

說明如下:

1. <appwidget-provider>標籤定義App Widget的屬性
2. android:minWidth定義寬度
3. android:minHeight屬性定義長度
4. android:updatePeriodMillis定義App Widget的更新頻率,Android框架每隔這段時間,會callback AppWidgetProvider類別的onUpdate()事件;此屬性的時間單位為1/1000秒,以上述的定義來說,等於3秒鐘的時間(3000/1000=3)
5. android:initialLayout屬性指定此App Widget的UI layout定義檔,”@”符號在Android的XML定義檔案,代表「目錄」之意,因此”@layout/main”表示「layout目錄下的main.xml檔案」

以上共四項屬性,是App Widget最基本的屬性,必須良好定義。其中android:updatePeriodMillis屬性可省略,代表不更新App Widget,即Android框架將不callback appWidgetProvider類別的onUpdate()事件。

onUpdate()事件負責更新App Widget的顯示內容。

設計App Widget的第一件工作,就是定義它的大小,以及更新頻率。由於手機的螢幕比較小,再加上桌面的空間有限,因此就要很小心定義App Widget的長度以及寬度。

在Android的Dev Guide文件裡,有一個App Widget設計原則的章節,描述了App Widget的標準大小;當然,這只是建議,我們可以任意定義App Widget的大小,因此不依照Google提供的設計原則也不會有什麼問題。但是,若是能遵循設計原則的指示,桌面的空間安排會較有效率,桌面的整體呈現也會比較美觀。

App Widget的設計需要考量螢幕的方向,若是直向顯示(portrait),則App Widget的大小建議如下:

portrait_sizes.png
(圖片來源:Android Dev Guide;點擊看全圖)

橫向顯示(landscape)的設計建議如下:

landscape_sizes.png
(圖片來源:Android Dev Guide;點擊看全圖)

在後面的教學裡,我們會再詳細說明App Widget的美工設計原則。

接下來要接著進行的工作,即是在”@layout/”裡建立main.xml檔案,以描述App Widget的UI。

Jollen 的 Android 教學,#22: main.xml-描述App Widget的UI

main.xml-描述App Widget的UI

這個檔案在前面的教學裡介紹過了,它的主要用途是描述UI。我們想要設計一個能嵌進桌面,並顯示文字的App Widget,因此必須使用Android的TextView類別。

以下是main.xml的完整內容:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:id="@+id/appwidget_text"
    android:textColor="#ff000000"
    />
</LinearLayout>

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

Jollen 的 Android 教學,#23: HelloAppWidgetProvider.java-實作App Widget供應者

HelloAppWidgetProvider.java-實作App Widget供應者

程式碼已經在前面的教學裡展示過了,當時只提到一個很基本的重點:使用AppWidgetProvider類別。在這裡,我們先說明設計的部份,才能了解程式如何實作。程式碼的說明稍後再做補充。

AppWidgetProvider.png
圖1:設計App Widget

從圖1的設計裡可以知道(配合查詢Android Reference文件),當程式繼承了AppWidgetProvidr類別後,也繼承了二個主要的method:

  • onUpdate()
  • onDelete()

App Widget使用AppWidgetProvider類別,即App Widget的「供應者」,供應什麼東西給誰呢?可以想像成是,我們的應用程式,供應App Widget給Android桌面。

到目前為止,我們知道只需要AppWidgetProvider即可實成一個很陽春的App Widget。而完整的App Widget應該包含三個單元(unit):

1. Provider:此處說明的「供應者」
2. Configure:App Widget的設定單元,用途是提供一個「介面」供使用者輸入資料
3. Receiver:繼承自BroadcastReceiver的單元,即廣播接收器,用來接收Android框架所送出的事件(event)

在後續的教學裡,我們會繼續說明configure與receiver單元的觀念與實作。

Jollen 的 Android 教學,#24: AndroidManifest.xml-加入App Widget的程式資訊

AndroidManifest.xml-加入App Widget的程式資訊

AndroidManifest.xml檔案的用途在前面的教學裡介紹過了。以下是其完整內容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.moko.hellowidget"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" 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>
    </application>
    <uses-sdk android:minSdkVersion="3" />
</manifest>

說明如下:

1. 在<application>裡加入<receiver>標籤,指定android:name屬性為主要的provider類別,即”HelloAppWidgetProvider”,請注意,「.」表示後面的字串為一個「類別名稱」,不要忽略了這個重要的小數點

2. 在<receiver>裡加入<meta-data>標籤,指定android:resource屬性為App Widget的資源檔名稱,以我們的範例來說,就是「@xml/appwidget_provider」,即「xml目錄下的appwidget_provider.xml檔案」

3. 在<receiver>裡加入<intent-filter>標籤,讓我們的App Widget可以接收APPWIDGET_UPDATE事件(event)

一個很簡單的App Widget完成了。接下來,就是針對HelloAppWidgetProvider.java程式碼做細部說明。

July 12, 2009

Jollen 的 Android 教學,#25: HelloAppWidgetProvider.java 程式碼說明

HelloAppWidgetProvider.java 程式碼說明

HelloAppWidgetProvider.png
圖1: HelloAppWidgetProvider的設計

圖1是目前我們的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?

什麼是RemoteView呢?先看一下框架的設計,如圖2。

簡單來說,「RemoteViews」就是表示UI的類別。res/layout/main.xml描述了應用程式的UI,UI裡當然包含許多組件(Widget),而在先前的教學裡講到了一個觀念「Android應用程式的UI就是一個View tree」,view tree就是「View Hierarchy」。

總結來說,RemotViews是一個用來表示View Hierarchy的類別。透過RemoteViews可以找到UI裡的每一個組件。

RemoteViews.png
圖2: RemoteView的設計(點擊看全圖)

程式說明: HelloAppWidgetProvider.java

onUpdate()的程式實作:

    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);
        }
    }

說明如下:

1. onUpdate()負責更新「已經安裝」在桌面上的App Widget內容,因此我們實作一個updateAppWidget()來進行真正更新的工作

2. onUpdate()的第二個參數為AppWidgetManager,這是一個「管理AppWidgetProvider」的類別,我們必須透過框架callback本方法時回傳給我們的AppWidgetProvider物件,來更新桌面上的App Widget

3. onUpdate()的第三個參數appWidgetIds陣列,存放需要更新的App Widget ID;框架會將需要更新的App Widget之ID回傳給onUpdate(),程式必須負責「更新每一個需要更新的App Widget。」

更新App Widget的方式是透過AppWidgetManager來完成,程式實作:

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
    	CharSequence text;
    	      
    	text = "www.jollen.org";
    	        
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
        views.setTextViewText(R.id.appwidget_text, text);
        
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

說明如下:

1. 透過UI layout取得自已的「View Hierarchy」(UI),以前面介紹的RemoteViews物件表示

2. 如圖2,呼叫RemoteView的setTextViewText()方法,修改UI裡的「R.id.appwidget_text」組件,變更文字內容

3. 呼叫AppWidgetProvider的updateAppWidget()方法,更新我們所指定的App Widget,將其UI更新為RemoteView的UI

4. updateAppWidget()的第二個參數為RemoteView,即說明1.取得的UI,說明2.修改了此UI裡的”R.id.appwidget_text”組件,最後透過App Widget Manager更新App Widget的UI

July 16, 2009

Jollen 的 Android 教學,#26: 強大的Intent機制

什麼是Intent(意圖)?

強大的事件處理「Intent」(意圖)是Android很強大的一種機制。

在 Android 應用程式框架中,有一個非常聰明的事件處理機制,稱之為「Intent」。Intent(意圖)的作用與事件(event)很像,但與傳統的事件處理仍然有些差異。傳統的事件處理,講求的是「處理者(handler)的觸發」,當一事件發生時,便callback讓事件的處理者,或是直接將該事件轉送(forward)給應用程式,由應用程式決定處理方式。

在「Intent」這樣的事件處理觀念裡,Android 試圖將事件解釋為「應用程式的意圖」或是「使用者的意圖」,並試著去解釋該意圖的目的,若 Android 系統本身能理解應用程式的意圖,便會「自行」去處理該意圖所應執行的工作。

Android的做法是,讓每個意圖(Intent)都帶有一個動作(action),並根據不同的動作去行動。

關於前述教學提到的Intent

在前面的教學裡,我們用到二次Intent如下:

1. 自行定義一個Intent、設定Service可接收此Intent,並透過「送出Intent給框架」的方式,請框架啟動該Service

2. 使用Android內部定義的動作「ACTION_VIEW」,來「檢視」(view)一個「URL」資料,當框架看到內部定義的ACTION_VIEW動作時,便「自行」處理該Intent;處理的方式是啟動WebView並連上網站

以前述的教學為例,使用內建的動作“ACTION_VIEW”就可以很容易做出一個「啟動瀏覽器(WebView類別)上網」的應用程式。

透過這二個例子我們知道,Intent的動作可以是自行定義與框架內部定義二種。Android框架的Intent有很多方便實用的「內建動作」,以下我們說明Android內建Intent的美麗之處。

Android內建的Intent Action

Android的框架確實是讓每個Intent都包含了一個動作,就稱為action。

為了讓大家更容易了解Intent的基本觀念,我們採用「體驗」的方式來說明如何使用內建的Action。現在,我們列舉以下三個情境,並分別實作其範例:

HelloIntentDialer: 啟動撥號器(dialer)並撥號
HelloIntentMusic: 使用者按下「Select Music」後,可以由音樂清單裡選擇音樂並撥放
HelloIntentWallpaper: 啟動Android內建的「背景圖選擇器」,讓使用者更換背景

第二個範例”HelloIntentMusic”其實是ApiDemo裡的範例,而且是很容易能了解Intent內涵的好程式。

除了action外,Intent還可以包含另外一項資訊「data」。

Intent的action指定這個Intent的「動作」是什麼,框架會依指定的動作進行處理;有些action可以附帶一筆資料,這個資料是以Uri的格式撰寫,在HelloIntentDialer的範例會再做說明。

內建的Intent有哪些呢?請參考Android Reference Guide中的Intent類別說明。上述三個範例分別使用以下三個action:

1. ACTION_CALL: 撥號
2. ACTION_GET_CONTENT: 啟動內容選取器
3. ACTION_SET_WALLPAPER: 設定Wallpaper

在進行範例講解前,可以先行閱讀Intent類別的說明。ACTION_CALL是一個內建的action,我們只要產生一個Intent物件,並定義其「action」為ACTION_CALL即可通知框架「打電話」。

Android內建的action是相當實用的應用開發機制,同時也是Android OS最具代表性的機制之一。Android內建的Intent action分為二種:

1. Activity Action: 啟動Activity的action
2. Broadcast Action: 透過廣撥器處理的action

第一種action是activity action,用途是通知框架啟動Activity,這裡提出的三個範例,都是使用activity action。Broadcast action將在Broadcast的教學裡再做說明。

* Update: 2009/08/07

July 17, 2009

Android的Launcher研究:客製化桌面UI

前言

能取得Android OS原始碼,並修改裡頭的內容,有時候也頗有樂趣。最近和幾位朋友聊到「Android框架的改造」,以及如何吸引對Android框架技術有興趣的同好一起交流的議題;我個人認為,一開始如果能丟出一個比較有樂趣的議題,或許可以有拋磚引玉的效果。

上週在北京進行Android培訓課程時,與eoeAndroid社群也進行了想法的交流,由於大家都體認到Android底層技術的重要性及其價值,而且eoeAndroid社群裡也有許多技術好手,所以就和eoeAndroid的創辦人靳岩兄有了一個共同主持研究Android底層技術「同好小組」的想法,希望能透過社群的方式,集合大家的智慧,一起把底層技術研究清楚。

因為要讓大家能有焦點,所以「發題」很重要,這個工作就由落在我身上了。由於第一次希望題目能簡單,並且有趣一點,至少要能達到發球的效果,吸引大家開始關心Android底層技術,所以原則是:希望能用最簡單的方式、讓大家體驗修改底層的樂趣。

題目說明: Launcher

第一次的題目是「Launcher」的修改。

Launcher就是Android的應用程式啟動器,Launcher的功能還包含:桌面的切換、應用程式快捷(shortcut)功能、背景圖(Wallpaper)功能等等。因此,修改Launcher可以改變一些很深層的UI功能。

在Android的桌面最下方,有一個圖示,按下後可以拉出應用程式圖示清單,這是Launcher提供的功能。這一次,因為我們覺得這個Launcher的圖示太製式化了,越看越不好看,所以想要修改一下,換張圖,要怎麼做到呢?

範例展示

例如,圖1是原始的圖示;圖2是修改後的圖示。

launcher-1.png
圖1: 原始圖示

launcher-2.png
圖2: 幫Launcher妝扮一下

實作說明 1/4: 取得Android原始碼與EeePC移植

這個功能並不難做,事實上,完全不用寫程式。只要把圖檔重做就可以了。只不過前提是,要知道:

1. 如何取得Android OS原始程式碼
2. 如何編譯Android OS

最簡單的做法是:

1. 下載Android原始碼後、取得EeePC的移植(product)
2. 編譯「TARGET_PRODUCT」為eee_701
3. 由於Launcher都是用Java語法寫成的,所以不會有架構(ARM/x86/...)的問題,編譯後可以取得Launcher.apk;APK套件是不分處理器平臺的

先學會如何由Android原始碼編譯出eee_701的image,才有辦法繼續進行。

實作說明 2/4: 修改圖檔

在Android原始碼的 packages/apps/ 目錄裡,存放了Android內建的應用程式原始碼,Launcher是Android的一個應用程式,所以從這裡找到它的原始碼,並進行修改工程。

切換到以下目錄:

<android source>/packages/apps/Launcher/

接著要修改src/目錄下的內容,還是res/目錄下的內容呢?圖檔屬於Android的「resource」,因此當然是到res/目錄下找到我們要的圖檔。

切換到以下目錄:

<android source>/packages/apps/Launcher/res/

又看到了一大堆目錄,圖檔的部份存放於:

  • drawable-land/ - landscope 模式的圖檔
  • drawable-port/ - portrait 模式的圖檔

我們先改一下portrait模式的圖檔。找到drawable-port/tray_handle_normal.png檔案如下:

tray_handle_normal-1.png

就是它了,換掉,把圖檔換成這個:

tray_handle_normal-2.png

換好後重編Android即可。一行程式都不用改。

實作說明 3/4: 安裝Launcher.apk

重編Android原始碼,接著可以在out/target/product/<product name>/system/app/找到Launcher.apk套件。把Launcher.apk安裝到AVD(Android 模擬器)裡做測試,方法如下:

1. 先啟動一個AVD
2. 執行adb將Launcher.apk手動安裝到AVD裡,指令如下:

$ adb install -r <your-path>/Launcher.apk

成功後可看到以下畫面:

338 KB/s (837376 bytes in 2.417s)
        pkg: /data/local/tmp/Launcher.apk
Success

實作說明 4/4: 重開機

已經完成了,直接重開即可。「重開」是把AVD重新啟動,不是把電腦重新開機 ;-)

應用與討論

歡迎大家上傳你的作品、或是貼圖與大家分享,方式是透過eoeAndroid社群的討論區:

http://www.eoeandroid.com/forumdisplay.php?fid=54

如果有更詳細的Launcher研究心得,或是希望能針對Launcher進行討論,歡迎至eoeAndroid的討論區發文。

關於 July 2009

此頁面包含了在July 2009發表於Jollen's Blog的所有日記,它們從老到新列出。

前一個存檔 June 2009

後一個存檔 August 2009

更多信息可在 主索引 頁和 歸檔 頁看到。

Top | 授權條款 | Jollen's Forum: Blog 評論、討論與搜尋
Copyright(c) 2006 www.jollen.org