瀏覽人次:【11018】
Android User Interface
在Android的應用程式架構中,提供了一套使用者介面(User Interface;UI)架構給予開發者建立Android平台上各種UI,對每一個Android應用程式內的使用者介面來看,皆是由不同的View與ViewGroup物件所構成,在Android使用者介面架構內提供了多種不同的View與ViewGroup類別,如圖一所示,這些Android UI架構內所定義的基礎類別,分別提供給開發者針對使用者介面設計、開發及管理使用。
《圖一 View與ViewGroup階層架構圖》
|
View是Android UI架構中最基本的UI元件,在Android應用程式內的UI元件皆為Views所構成,如文字欄位、按鈕、及各種Widget等,即為View的衍生類別;ViewGroup是一種特別的View,擁有裝載容納其他Views物件的特性,是View的容器也是各種佈局(Layout)的原生類別。
以應用程式的使用者介面角度考量,View物件掌控螢幕上特定的一塊矩形區域,記錄著這塊區域的佈局參數及內容。Android會透過View物件控管所屬區域內的佈局配置、區域量測、繪製方式、焦點改變、畫面捲動、及按鍵與觸控手勢等互動;若UI內的View物件與使用者有互動,則Android會針對產生互動的View回傳對應的事件訊息。
View與ViewGroup
Android平台所提供的使用者介面架構,是以樹狀結構(Tree–Structured)來表示一個應用程式的使用者介面,根據應用程式的需求,將預先定義的使用者介面,使用符合需要的View與ViewGroup來去建構,如圖二使用者介面樹狀圖所示,這是個簡單的Android 應用程式UI Tree例子,這棵UI Tree(使用者介面樹)會因為使用者介面使用需求複雜度的不同,而定義成複雜或簡單的樹狀結構,在此UI Tree(使用者介面樹)架構下,樹的節點並不一定要使用Android使用者介面架構所提供的基本View(widget元件)及ViewGroup(Layout元件),它也允許使用開發者所延伸擴充或自定義的View(widget元件)及ViewGroup(Layout元件)。對這棵UI Tree(使用者介面樹)來說,每一個View物件是屬於葉子(Leaves)部份,而ViewGroup物件則是屬於這棵樹的樹枝(Branches)部分。
《圖二 使用者介面樹狀圖—Android應用程式UI架構》 |
為了將描述整個使用者介面的View階層樹狀圖繪製在螢幕上,需要將其樹根節點(Root node)傳遞給Android系統,而Android系統則會透過參考View樹根節點(Root node),使用其表達的使用者介面樹狀結構,並根據得到所需之UI配置相關資訊,去量測使用者介面的範圍並繪製於螢幕相對位置上。
而當Android在UI繪製時,將會由樹根節點(Root node)開始向下追蹤,並要求依序每一個View子節點(child nodes)繪製它們所屬區域,每一個ViewGroup子節點(child nodes)則會負責通知所屬於此ViewGroup內的每一View子節點,每個View子節點將會獨立繪製更新各自所屬範圍。在繪製階段,View子節點(child nodes)會向所屬的父節點(parent node)要求索取在父節點內的一個繪製區域尺寸與所屬位置,但是父節點(parent node)才是最後決定每個子節點(child nodes)要畫多大、畫在哪。
User interface layout
在Android內,是利用Layout元件來定義畫面上的使用者介面佈局配置,是由ViewGroup所延伸出來的(也可視為一ViewGroup物件),它定義了整體UI的設計結構及UI各元件如何顯現給使用者。以View階層樹狀圖為基礎,每個View子節點皆會使用其ViewGroup父節點佈局配置參數(Layout parameters)控制,而佈局配置參數(Layout parameters)會定義子節點所分配到的大小與位置等資訊。如圖三之Android使用者介面佈局配置參數所示,每個一View子節點都會使用相對應的佈局配置參數,告訴它們的父節點想要如何被配置。
《圖三 Android使用者介面佈局配置參數》
|
而Android會依序解析使用者介面佈局配置(UI layout)內的各項UI元件,是使用View階層樹狀圖由上而下的順序依序處理,而這個也是Android更新繪製各項使用者介面元件的順序,若有多個Views重疊繪製於同一個位置,而最後繪製的View將會覆蓋掉前一次繪製結果。
擴充Android UI方式
延伸View結構
Android內部提供了數個完整實做的常用UI元件,如:按鈕(Buttons)、複選框(Checkboxes)、文字輸入欄(Text-entry fields)等View元件,搭配不同的佈局管哩,如:LinearLayout、FrameLayout、或RelativeLayout等ViewGroup元件,我們可以利用這些內建的View元件,快速地建立出所需的使用者介面,並配合各種不同的ViewGroup來做所需的佈局管理。
然而當需要建構複雜的操作介面時,例如製作一個音量旋鈕、3D使用者介面、或新增使用者介面行為事件等等,這些Android平台所提供基礎固定的功能UI元件就開始不敷使用,但我們並不會因此而有所受限;若我們需要針對某些使用者介面的功能去客製化或建立自己所屬的特殊可動元件,例如:可以利用延伸或合併View物件來產生一個新的UI元件。
合併既有使用者介面元件
當我們不想建立一個全新客製化的View元件,而是重覆使用既有View元件,將數個Views既有特性加以延伸或合併成為一個新的View單一元件,例如去完成像是複合欄位的文字編輯窗格,或是製作一個組合框架元件(ComboBox;一個組合彈出清單,可以自由進入文字欄位)等等。
自訂使用者介面元件
若Android內的View與ViewGroup沒有特別需要保留的功能,也可以利用View物件類別覆寫及繼承,重新建立一個新的使用者介面元件,並針對每一個所需要的功能,如:按鍵或觸控螢幕的事件管理、繪製及呈現方式等部分做客製化。
不論是合併還是完整客製化自己的使用者介面元件,其製作流程的概念都是相通,建立客製化UI元件流程概述如下:
根據所要功能延伸現有View或其子類別成為一新的View類別。
從父類別覆寫所需的功能,如繪製方式、量測方法、與按鍵處理等。
使用改寫完成新的View元件,新的View元件可以用來取代被擴充的View元件。
一個完整客製化的使用者介面元件,擁有很大的彈性,可以建立任何你想要顯現的圖形UI元件,也許是一個歌詞顯示視窗,游標會隨著歌曲移動,或是一個3D的圖形介面,伴隨呈現著多樣化的3D互動。如圖四之客製化UI元件所示,新延伸出來的客製化元件,仍被Android視為一個View元件來管理。
《圖四 客製化UI元件》 |
使用外部繪圖引擎建立UI
不論使用合併還是延伸製作新View元件,想要的組件都是Android沒有提供的,所幸只要遵循Android UI架構的規範,可以輕鬆快速的製作出想要的UI元件外觀與行為,甚至可以使用外部3D或向量繪圖引擎,如JSR184、297等3D繪圖引擎或是SVG向量繪圖引擎等,來協助製作與管理不同客製化UI元件。
對於外部3D或向量繪圖引擎的使用,依循Android擴充使用者介面架構的規則,開發一個新的View類別,而這個View元件的更新、繪製及客製化等相關行為,皆透過外部既存的3D或向量繪圖引擎負責處理,如此,可以更快速方便製作一個新的UI元件,並透過外部引擎所提供的功能,來協助管理此UI行為與相關互動。
以圖五之擴充Android UI架構圖來做說明,左邊為一Android應用程式的使用者介面外觀示意圖,如同前面所提到的,一個使用者介面會有個相對應的View階層樹狀圖,用來描述Android內部View與ViewGroup元件,如圖五右上所示,紅框的ViewGroup元件為整個使用者介面的樹根節點(Root node),通常代表著應用程式全螢幕的範圍,此樹根節點(Root node)下有兩節點,分別代表著此應用程式下方圖形按鈕UI與3D使用者介面,其中,被黃框ViewGroup所管理的是三個ImageButton Views,而綠框內的3D UI場景即為利用3D繪圖引擎所擴充的新UI元件。
《圖五 擴充Android UI架構圖 - 使用外部繪圖引擎製作Android UI,左:Android應用程式UI示意圖,右上:此應用程式所建構使用的使用者介面階層樹狀圖,右下:新View的場景示意樹狀圖,此UI為利用3D引擎所製作的客製化之View元件。》
|
在圖五中,左邊所呈現的3D場景UI為利用3D引擎所製作的客製化之View元件,而這個View裡面包含了外部3D引擎(如:JSR 184、JSR297等)所管理的3D場景,如右下:新View的場景示意樹狀圖所示,這個新建立的UI元件,是遵循Android擴充使用者介面架構的規則所製作的,主要的關鍵在於Andorid的UI架構使用。
Android系統會對每個View分配一塊螢幕上的空間,而View元件則會負責更新繪製被分配到的範圍,我們可以利用這樣的機制,將View當作一個被繪製的工作區域,透過既存的外部3D繪圖引擎來更新此塊內容,如此,我們可以利用一些內容製作工具,如:Maya、3Ds Max…等,來協助製作與設計UI元件內容,或是重複利用已建構好的這些3D UI物件,而這些外部建立的3D UI場景物件,再由3D繪圖引擎內的場景管理系統協助管理各項3D物件(如同圖五右下場景示意圖)。
透過這種方式,可以快速來建立Android 新形態的UI內容,並經由外部工具與繪圖引擎的協助,製作出與Android內建UI元件不同的特效動畫與行為互動。而在Android內,要使用外部既存的各種不同的繪圖引擎,必須先將繪圖引擎從新編譯成Android支援的函式庫,若是使用非JAVA語言所開發的繪圖引擎,如;C語言等,可以透過Android應用程式架構中,所提供的JNI(Java Native Interface),來與底層原生C語言(Native C)做資料溝通。
《圖六 Android 3D UI範例》 |
JNI - Java Native Interface使用介紹
在Android 平台上,所有應用程式皆是由JAVA開發,經編譯完成後透過Android內的Dalvik虛擬機器(Virtual Machine:VM)來執行,而Dalvik虛擬機器在執行JAVA應用程式時,若需要與C組件做溝通,Dalvik便會嘗試載入C組件,讓JAVA呼叫C組件內的函式,而讓JAVA與Native C可以互相做溝通的,便是透過JNI(Java Native Interface),在Android SDK內並未對JNI有相關說明,我們於下面做個簡單的介紹。
Java平台、主機環境與JNI
Java平台,包含Java虛擬機器(Java Virtual Machine;VM)和Java應用程式介面(Java API)。Java應用程式是以Java語言所撰寫而成,經過編譯過程產生標準二進位檔案(*.class)後,即可被執行於所有裝有Java虛擬機器的硬體上,使達到跨平台的優點。
主機環境(Host environment),是指主機作業系統、鏈結函式庫與CPU指令集的統稱。一般而言,主機環境內的原生應用程式(Native application)是使用原生程式語言(Native programming language)撰寫,如:C/C++,經過鏈結原生函式庫(Native library),編譯為主機認可的特殊二進位檔案後始可使用。原生應用程式(Native application)與原生函式庫(Native library)必須與主機執行環境相依,因此在不同主機環境下編譯的應用程式,無法共通使用。
Java平台透過Java執行環境(Java Runtime Environment;JRE),架構在主機環境之上;JNI架構則實做於Java虛擬機器中,提供Java平台與原生應用程式溝通介面。
《圖七 Java平台、主機環境與JNI的關係》
摘自Java Native Interface: Programmer's Guide and Specification
|
JNI
JNI是Java Native Interface的縮寫,從Java1.1開始,JNI標準成為Java平台的一部分,它允許Java程式與其他語言(如C、C++、組合語言)所撰寫出來的程式碼能夠相互使用。
《圖八 JNI架構圖》 |
JNI的優點
透過JNI架構:
開發者得以使用Java平台開發應用程式,使應用程式具跨平台的特性。
開發者得以重複使用以往所開發的原生應用程式函式,尤其原生程式語言(Native programming language),如:C/C++,發展已久,函式庫非常完整,Java應用程式能夠使用C/C++既有的函式庫原始碼,毋須重新實做Java函式。
JNI使用時的注意事項
雖然Java平台具跨平特性,然而,原生應用程式(Native application)未必具此性,如:C/C++;因而在將使用JNI開發的應用程式放入不同的平台之前,需重新編譯相關的原生應用程式(Native application),使其編譯後的檔案能與平台相容。
Java是一種型別安全(type-save)的程式語言,而原生程式語言(Native programming language),如:C/C++,不是;因此在程式碼的撰寫上,需要特別注意程式語言間的特性,以免造成當機問題。
JNI的實做流程範例(以Java和C語言為例介紹)
《圖九 JNI實做流程圖》 摘自Java Native Interface: Programmer's Guide and Specification |
編寫Java類別程式碼(*.java)
目的在宣告需要JNI實做的函式。
《圖十 範例:HelloWorld.java》 [1]需要JNI實現的方法應使用native關鍵字 |
編譯Java類別程式碼(*.class)
使用javac將*.java編譯成*.class檔案。
產生C標頭檔(*.h)
使用javah –jni 產生C標頭檔(*.h);此標頭檔內包含需要JNI實現的方法的C函式原型(prototype)。
《圖十一 範例:HelloWorld.h》 |
實做函式主體程式碼(*.c)
根據*.h的C函式原型(prototype),實做函式主體程式碼。
《圖十二 範例:HelloWorld.c》 |
編譯成動態函式庫檔案(*.dll/*.so)
當產生函式庫檔案後(*.dll/*.so),需要JNI實做的函式將會在執行時,透過system.loadlibrary() 被載入Java環境下運作。
結語
Android UI架構設計的理念,是希望不要被內建UI元件所侷限,透過延伸View與ViewGroup的結構,建立出自己所屬獨特創新的使用者介面,可以非常彈性的製作想要呈現的UI特效與互動,雖然建立一個完整的客製化UI組件是很複雜的,但是Android提供了良好且先進的架構,從小部分修改內建View元件,到重新製作一份完整客製化UI,讓我們在發展及擴充創新有趣的客製化UI模組有無限的可能,而唯一的限制是你的想像力。
---本文作者陳立承任職於資策會網路多媒體研究所---
|