Email me: jollen # jollen.org

more: Jollen 的 Embedded Linux 教育訓練

« December 2008 | (回到Blog入口) | February 2009 »

January 2009 歸檔

January 4, 2009

Jollen 的 Android 教學,#4: 使用 XML 安排 UI

繼上一篇文章介紹了 View 的觀念後,接下來就要了解一下如何「安排」Android 應用程式的 layout。

Android 應用程式的 layout(UI 佈局)除了直接撰寫程式碼的方式外,也能使用 XML 檔案來做描述(XML-based Layout)。在 Android Development Kit 的「Package Explorer」視窗,點選「res -> layout」選擇 main.xml,可以看到 “Hello Moko” 應用程式的 XML layout 檔案,如圖1。

xml_layout_1.png
圖1:"Hello Moko" 的 XML layout 檔

以下這段 XML 用來描述「TextView」物件的 layout:

<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />

以上述的例子來看,我們可以設定以下幾個 tag 來定義 TextView 的屬性:

- 'android:layout_width' - View 的寬度
- 'android:layout_height' - View 的高度
- 'android:text' - TextView 所要顯示的文字

到上一篇文章為止所撰寫的「Hello Moko」是以程式碼編寫的方式來安排 UI,要怎麼將 Hello Moko 改以 XML 做為 UI 的安排方式呢?請依照以下步驟進行程式調整。

R.java: 資源索引檔

在 Package Explorer 點選「src -> com.moko.hello -> R.java」,如圖2。

xml_layout_2.png
圖2:"Hello Moko" 的 R.java

R.java 是由 Android Development Kit 所自動產生的資源索引檔(resource index),「R」是一個類別,這是 Android 應用程式資源的索引類別。「R.layout」類別則是 UI 佈局的索引類別,R.layout 類別裡的「main」成員就是 Android 應用程式的「主佈局索引」。

修改 Hello Moko 程式碼

R.java 根據 main.xml 自動產生,「並不是由程式設計師手動編寫」,請勿修改此檔案。接下來,只要把先前的程式(HelloMoko.java)修改成下列寫法即可:

public class HelloMoko extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.main);
	}
}

當 Activity 被建立後,我們呼叫 setContentView() 方法,將主要的 UI(R.layout.main)顯示在視窗上。R.layout.main 索引到一個 TextView 物件,此物件定義於 main.xml。將 R.layout.main 顯示在 Activity 視窗上,請記得修改程式碼,才能使用 XML-based layout。

我們順帶將 TextView 物件的 text 屬性修改如下:

<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Hello TextView"
    />

xml_layout_3.png

程式執行結果如下(使用 Android 模擬器)。

xml_layout_4.png

Jollen 的 Android 教學,#5: 使用 View 的 XML 屬性

上一篇文章介紹了 XML-based layout 後,發現這是一個很方便,而且有用的 UI 安排方式。以圖1為例,我們現在想要做出一個應用程式,讓文字的超鍊結(hyperlink)可以被使用者點選,並自動呼叫瀏覽器連到該網站,這樣的應用程式該如何撰寫呢?請依以下步驟修改程式碼。

xml_layout_attributes_1.png
圖1:如何設計一個可點擊 URL link 的應用程式?

View 的 XML 屬性

每一個 View 都有許多屬性,我們可以利用 XML 來描述每一個 View 的屬性,進而達到控制物件的效果。以 TextView 為例,有一個「android:autoLink」屬性可以控制「是否要自動將網址轉換為可點擊的 URL 文字」。

要怎麼知道每一個 View 都哪些屬性呢?這個時候就要祭出 Android SDK 的 documentation 了。以 TextView 為例,透過以下的說明,可以了解 TextView 有哪些屬性,以及該屬性的用途:

http://code.google.com/intl/zh-TW/android/reference/android/widget/TextView.html#attr_android:autoLink

原來,只需要透過「autoLink」屬性,並將此屬性設定為「web」即可做出我們想要的功能。

修改 main.xml

將 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="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Jollen's Blog - http://www.jollen.org/blog"
    android:autoLink="web"
    />
</LinearLayout>

我們為 TextView 物件新增一個「android:autoLink」的屬性,並將此屬性設定為「web」,以後只要「text」屬性裡出現 URL,TextView 就會自動將 URL「文字」轉換成可點擊的 link。

程式執行時,只要點擊 link,就會自動啟動瀏覽器,並連接該網址,如下圖。

xml_layout_attributes_2.png

January 5, 2009

Jollen 的 Android 教學,#6: WebView 體驗與 findViewByID

如果要繼續體驗 View 的樂趣,那麼「WebView」這個 View 無疑是最佳人選。android.webkit.WebView 是使用「WebKit」技術的 View,主要的用途是「顯示網頁」。使用 WebView,我們可以在 Android 應用程式裡顯示自已的 HTML 文件,或是線上的網頁。

接下來請依照以下步驟,建立我們的第二個 Android 應用程式「Hello Web」。

建立新專案: HelloWeb

建立一個新的 Android 專案,如圖1。

hello_web_1.png
圖1: 建立 Hello Web 專案

並且撰寫 HelloWeb.java 程式如下:

package com.moko.web;

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;

import com.moko.web.R;

public class HelloWeb extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        final String mimetype = "text/html";
        final String encoding = "utf-8";
        
        WebView wv;
        
        wv = (WebView) findViewById(R.id.wv);
        wv.loadData("<img src=\"http://www.google.com.tw/intl/en_com/images/logo_plain.png\" />", mimetype, encoding);
    }
}

上述程式碼採用 XML layout 方式來安排 UI,因此接下來的工作就是編輯 XML layout 檔案。

規劃 UI: main.xml

編輯 main.xml 來規劃「Hello Web」的 UI 如下:

<?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="wrap_content"
    >
<WebView 
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content" 
	android:id="@+id/wv" 
	/>
</LinearLayout>

在這裡我們定義了「WebView」標籤,並且指定「WebView」的 ID 為「wv」。透過指定「ID」屬性給 View 的方式,便能「讓 Android 應用程式在執行時期(run-time)找到指定的 View 物件」。

使用 View 的 ID 屬性: findViewByID

怎麼在執行時期,找到「XML layout」安排好的 View 呢?看到 HelloWeb.java 的程式片斷如下:

        final String mimetype = "text/html";
        final String encoding = "utf-8";
        
        WebView wv;
        
        wv = (WebView) findViewById(R.id.wv);
        wv.loadData("<img src=\"http://www.google.com.tw/intl/en_com/images/logo_plain.png\" />", mimetype, encoding);

呼叫 findViewByID() 方法,即可在執行時期「動態取得 View 物件」。當我們在 main.xml 裡加入 WebView 標籤,「存檔」後,R.java 資源索引檔也會跟著更新,我們可透過「R.id.wv」來索引到 WebView 物件。以下是 R.java 的內容:

package com.moko.web;

public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int icon=0x7f020000;
    }
    public static final class id {
        public static final int wv=0x7f050000;
    }
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int hello=0x7f040000;
    }
}

取得 WebView 物件後,呼叫 WebView 的 loadData() 方法,將 HTML 內容載入到 WebView 物件裡,並顯示在 Activity 上。loadData() 的參數如下:

  • 第一個參數:HTML 內容
  • 第二個參數:MimeType 類型,指定為 text/html,即 HTML 類型文件
  • 第三個參數:文字編碼方法,指定為 utf-8(Unicode)

Hello Web 範例程式所載入的 HTML 文件是一個 <IMG> 標籤,因此我們在視窗上所看到的內容就是一張圖檔,如下圖(使用真正的 Google Phone 做測試)。

hello_web_2.png
圖: Hello Web 執行結果。

January 8, 2009

Jollen 的 Android 教學,#7: 如何讓文字並排顯示 - TableLayout

android.widget.TableLayout 是一個「排版」的類別,假設現在我們想要做出如圖1的文字排版效果,那麼使用 TableLayout 就是標準的做法。傳統寫程式排版的做法不是非常的方便,所以我們將採用 XML layout 方式來實作。

TableLayout_1.png

圖1: 文字並排顯示

建立新專案: HelloLayout

建立新的專案「HelloLayout」,並撰寫程式碼如下:

package com.moko.layout;

import com.moko.layout.R;

import android.app.Activity;
import android.os.Bundle;

public class HelloLayout extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

由於我們採用 XML layout 的做法,所以主程式不需要做什麼修改。本範例的重點應該是在 main.xml 檔案的編寫。

設計 TableLayout: 編寫 main.xml

新的 Android 專案創建時,預設是使用 LinearLayout(線性排版)來安排 UI。現在,我們將使用 TableLayout 來取代 LinearLayout,以「表格」方式來安排 UI。TableLayout 讓我們可以將畫面切割成一張表格,如果我們可以設計一個二欄式(2 columns)的表格,就可以做出如圖1的顯示效果了。

以下是 main.xml 的內容:

<?xml version="1.0" encoding="utf-8"?>

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

	<TableRow>
		<TextView  
		    android:layout_width="fill_parent" 
		    android:layout_height="wrap_content" 
		    android:text="www.androidin.com"
		    android:padding="3dip"
		    android:autoLink="web" />
		<TextView  
		    android:layout_width="fill_parent" 
		    android:layout_height="wrap_content" 
		    android:text="www.google.com"
		    android:autoLink="web" />
	</TableRow>

</TableLayout>

android.widget.TableRow 是配合 TableRow 使用的一個類別,當我們在 TableLayout 裡安排一個 TableRow 時,在 TableRow「裡頭的所有 View」就會被安排在同一列(row)裡。以本範例來說,在第一列(row)裡有二個 TextView,所以這二行文字顯示時,就會呈現如圖1的效果。

為了避免文字擠在一起,因此我們在 TextView 裡加上了 padding 的屬性,如下:

android:padding="3dip"

第一個 TextView 的 padding 為 3dip,表示與旁邊的 View 必須空 3 個「間格」,這樣二個 TextView 才不會擠在一起。

January 12, 2009

Jollen 的 Android 教學,#8: 沒有 UI 的 Service

到目前為止,我們都著重在 Activity 以及 UI 的介紹,在 Android 應用程式裡,有一種沒有 UI 的類別(android.app.Service),稱之為 Service。簡單來說,Service 是一個 background process(背景程序),透過背景程序,我們可以實作一些不需要 UI 的功能,例如:在背景撥放音樂。

以下是利用 Eclipse 環境自動產生的類別 'MokoService':

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MokoService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

}

MokoService 類別繼承自 android.app.Service,幾個有關 Service 的重要觀念如下:

1. Service 物件以 separated process 的方式執行,這表示 Service 與 UI(Activity)並不在同一個 process 裡執行,而是個自在不同的 process 執行。

2. Android 應用程式是在 Activity 裡啟動與停止 Service。

3. 覆載(override)onStart() 方法(method)在 Service 被啟動時,執行我們想要的背景功能。

4. 覆載 onDestroy() 方法在 Service 被停止時,停止執行中的背景功能。

以下是一個加入 onStart 與 onDestroy 的 MokoService 實作:

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MokoService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public void onStart(Intent intent, int startId) {
		
	}
	
	@Override
	public void onDestroy() {
		
	}
}
目前,我們了解 Activity 與 Service 的觀念了,接下來,要怎麼在 Activity 裡啟動 Service 呢?

Jollen 的 Android 教學,#9: 啟動 Service - startService()

上一個課程裡,我們實作了一個 Service 的類別稱為 MokoService,現在我們想要在 Activity 裡載入並啟動 MokoService 類別,讓它可以在背景執行,請依以下步驟完成這個任務。。

修改 AndroidManifest.xml

在 Package Explorer 視窗裡找到目前 Android 專案的資訊描述檔,檔名是 AndroidManifest.xml。這是一個用來描述 Android 應用程式「整體資訊」的檔案,每個 Android 應用程式專案都會有一個。在這裡修改 Androidmanifest.xml 的目的是為了「在我們的 Android 應用程式裡加入一個 Service 類別」,這樣才有辦法啟動 Service。修改後的內容如下,紅色的部份是新增的描述:。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.moko.hello"
      android:versionCode="1"
      android:versionName="1.0.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
        <activity android:name=".HelloMoko"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".MokoService">
            <intent-filter>
                <action android:name="com.moko.hello.START_MUSIC" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
    </application>
</manifest>

這是什麼意思呢?我們留待後續再做說明。接著只需要再加上一行程式碼,就能啟動 MokoService 類別了。

啟動 Service - startService()

回到 HelloM 類別,加入一行程式碼:

public class HelloMoko extends Activity {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState); 
       
       setContentView(R.layout.main);
       
       startService(new Intent ("com.moko.hello.START_MUSIC"));
   }
}

Activity 類別裡有一個 method 叫做 startService:

startService(Intent service)

呼叫 startService() 即可啟動一個 Service 類別,只是,startService() 的參數是一個「Intent」的型別,並不是所要啟動的類別名稱。「Intent」是一個很像「Event」的類別,後續我們再做比較精確的說明,在這裡,我們不如把 Intent 當成是 Event(事件)。

當程式送出 com.moko.hello.START_MUSIC 事件給 Android 時,Android 便去尋找能處理此事件的類別,然後啟動它。在這裡,能處理 com.moko.hello.START_MUSIC 事件的類別就是 MokoService,這個關係就是透過 AndroidManifest.xml 的設定實現的。

January 19, 2009

Jollen 的 Android 教學,#10: 如何檢查 Service 是否已啟動?使用 Android 除錯器

Activity 是一個有 UI 的類別,Service 則是一個沒有 UI 的類別。要知道 Activity 是否啟動,只要看看手機是否出現畫面即可;要知道 Service 是否有啟動,最容易的方式就是透過「除錯」的方式。以下我們實際以一個完整專案方式來對 Android 應用程式做除錯。

建立 MokoService 類別

點擊 Eclipse 的 File -> New -> Class 項目,利用 Eclipse 的自動新增功能,在先前的 HelloMoko 專案裡建立 MokoService 類別,如圖1。欄位「Superclass」應填入 android.app.Service。

create_class_eclipse.png
圖1: 建立 MokoService 類別

修改 MokoService 實作

在新增的 MokoService 類別裡,加入 onStart() 與 onDestory() 實作,如圖2。onStart() 的實作如下:

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
	}

因為 onStart() 是一個負載(override)實作,因此要呼叫 superclass 的 onStart() 方法。接著,將滑鼠移到 MokoService 類別裡的第 17 行(super.onStart),然後點擊 Run -> Toggle Breakpoint 在程式碼第 17 行的地方建立一個中斷點。

debug_service_1.png
圖2: onStart() 與 onDestory() 實作與設定中斷點

除了 MokoService 類別外,我們還要修改 AndroidManifest.xml 並在 Activity 裡啟動 MokoService 類別,請參考 [教學, #9] 的說明。

啟動除錯器

點擊 Run -> Debug Configurations 執行專案,並啟動除錯器。當 Android 應用程式成功安裝到 target device 並執行時, 會出現一個詢問對話框,選 Yes 即可,Eclipse 會將環境切換至除錯模式,如圖3。

debug_service_2.png
圖3: 是否要切換到除錯模式?

接著可以在除錯模式下看到 Android 應用程式停在先前所設定的中斷點(breakpoint),這表示 MokoService 類別已被 Android 系統載入並執行了,如圖4。

debug_service_3.png
圖4: 程式在中斷點暫停

Jollen 的 Android 教學,#11: AndroidManifest.xml 的用途是什麼?

AndroidManifest.xml 是一個用來描述 Android 應用程式「整體資訊」的設定檔。簡單來說,這是一個「自我介紹」檔,我們可以向 Android 系統「介紹」我們的 Android 應用程式,以便讓 Android 系統完整地了解我們的應用程式資訊。

在 [教學, #9] 中,我們提及:「在這裡修改 AndroidManifest.xml 的目的是為了『在我們的 Android 應用程式裡加入一個 Service 類別』,這樣才有辦法啟動 Service...」這個工作的目的是為了向 Android 系統做二項自我介紹。說明如下。

1. 應用程式「實作了一個 MokoService 類別」

    <application android:icon="@drawable/icon" android:label="@string/app_name">
    ...
        <service android:name=".MokoService">
        ...
        </service>
    ...
    </application>

在 application 標籤裡加入 ‘service’ 標籤,告訴 Android 系統我們的應用程式有一個叫做「MokoService」的類別。「android:name」屬性用來指定 Service 的類別名稱,別忘了在 AndroidManifest.xml 裡,類別名稱都是以「.」(小數點)開始。

2. MokoService 類別可處理「com.moko.hello.START_MUSIC」意圖

       <service android:name=".MokoService">
        	<intent-filter>
        		<action android:name="com.moko.hello.START_MUSIC" />
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </service>

在 service 標籤裡加入 ‘intent-filter’ 標籤,告訴 Android 系統我們的應用程式可「濾出」哪一個「Intent」。在前面的教學裡,我們把 Intent 暫時解釋為 Event(事件);因此,這裡的「自我介紹」用意是為了告訴 Android 系統,我們可接受的事件名稱為何。

我們只要在 intent-filter 標籤裡加入 ‘action’ 標籤,並指定 action 標籤的 android:name 屬性即可。Intent 的命名規則為「xxx.yyy.NAME」的路徑命名法。

當 Android 收到由 Activity 發出的 Intent 後,便去找尋可處理 com.moko.hello.START_MUSIC 的類別,然後載入並啟動此類別。

最後,在 ’intent-filter’ 裡加入 ‘category’ 標籤,用來定義 com.moko.hello.START_MUSIC 的分類,在這裡指定為預設類別 「android.intent.category.DEFAULT」,這是一個 Android 定義的常數。完整的 Service 類別「自我介紹」標籤與屬性,可參考 Android SDK 的說明。

關於 January 2009

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

前一個存檔 December 2008

後一個存檔 February 2009

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

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