浏览人次:【11196】
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平台、主机环境与JAVA的关系》
摘自Java Native Interface: Programmer's Guide and Specification
|
JNI
JNI是Java Native Interface的缩写,从Java1.1开始,JNI标准成为Java平台的一部分,它允许Java程式与其他语言(如C、C++、组合语言)所撰写出来的程式码能够相互使用。
《图八 JNI架构图》 |
JAVA的优点
透过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)。
《图十一 范例:Hello World.h》 |
实作函式主体程式码(*.c)
根据*.h的C函式原型(prototype),实作函式主体程式码。
《图十二 范例:Hello World.c》 |
编译成动态函式库档案(*.dll/*.so)
当产生函式库档案后(*.dll/*.so),需要JNI实做的函式将会在执行时,透过system.loadlibrary() 被载入Java环境下运作。
结语
Android UI架构设计的理念,是希望不要被内建UI元件所局限,透过延伸View与ViewGroup的结构,建立出自己所属独特创新的使用者介面,可以非常弹性的制作想要呈现的UI特效与互动,虽然建立一个完整的客制化UI组件是很复杂的,但是Android提供了良好且先进的架构,从小部分修改内建View元件,到重新制作一份完整客制化UI,让我们在发展及扩充创新有趣的客制化UI模组有无限的可能,而唯一的限制是你的想像力。
---本文作者陈立承任职于资策会网路多媒体研究所---
|