http://www.wscxy.com/shosh/article.asp?id=125
Author: Shosh
Site: http://www.wscxy.com
对于MMI工程师在BREW上的开发需求,往往是以Applet为单位的。Brew上的Applet就是一个应用程序,和Application的 概念类似。
大多数Applet都需要UI界面(Daemon程序的需求往往很少),他们往往需要完成一定的功能,所以有逻辑的部分,如计算器程序,就是要处理 用户输入的数据并给出计算结果;另外,我们需要提供显示界面和用户进行交互,越是动感越是绚丽的界面效果越能赢得用户的喜爱。话说回来,MMI层次上开发 的应用,逻辑上大多比较简单,不需要复杂的算法。开发者不必了解太多的底层知识和硬件知识,只要有扎实的程序语言开发功底(BREW上多数用C,也有用 C++的),基本上都能写出一般的应用来。另外,逻辑部分的代码变动性不强,而界面效果在不同的工程间则可以相差很大,还是拿计算器程序来举例,我们在不 同的手机上看到的计算器程序,有的界面效果看上去很一般,而有的则很漂亮,但是他们完成的功能是差不多的。而对于一个公司来讲,他们往往希望同一套代码能 够适应不同工程项目上的需求,这样可以在很大程度上减少工作量。其实这是客制化的一部分,最理想的客制化是不需要修改代码,只要替换图片资源、字符串资源 和其他一些资源如音频资源即可达到目的。但是如果差异较大,光靠资源的替换是很难满足需求的,还是需要修改到代码的。我们会希望尽量少地改动到逻辑代码, 最好是只需要修改到一些数据,而不用去修改实质性的代码,而功能逻辑上的一致性给了我们一定的可能。说了这么多,其实是说:我们在设计的时候尽量考虑通用 性,以提高代码的复用率以及风格变换的简易性。
目前,在Brew上开发界面,主要使用BUIW。在BUIW中,主要有Form, RootForm, Container, Widget, Decorator, Model等概念。下面简单介绍一下这些基本概念:
Model:其实Model是一个用来存放数据的结构。它提供专门的接口让用户设置Model的值或得到Model的值。另外Model还负责维护 Listener List,并提供接口允许对象监听它(将Listener加入到Listener List中)。当用户通过Model提供的设置数据的接口改变Model的值的时候,Model就会逐个给监听它的Listener对象发送消息(实际上 是直接调用Listener注册的回调函数),监听的对象再做出相应的动作,如使用新设置进来的数据更新画面等。其实这里应用了设计模式中的观察者模式 (Abserver Pattern)。Model分很多种,比较常用的有Value Model, Interface Model, Vector Model等,不同种类的Model存放的数据类型不同。一般情况下,一个Widget会关联上一种Model,并监听Model数据的改 动。
Widget:Widget是BUIW中一个最小的UI元素,可以理解成控件,比如按钮(Button Widget)、图片(Image Widget)、静态文本框(Static Widget)、可编辑文本框(Text Widget)、滚动条(Scroolbar Widget)等。它们会根据需要关联Model,如Static Widget,它需要记录显示的字符串内容,所以会关联一个Value Model,而对于Image Widget,则关联Interface Model。一般Model存放的是一些允许用户设置或修改的数据,比如一个Static Widget,有的时候显示“你好”,有的时候显示“这是一段文本”,这些不确定的可随时修改的数据就存放在它关联的Model中。这样当这些数据更改的 时候,Model会通知Widget你需要更新你的长相,和你的数据保持同步了。当然监听者对于数据的改动也可以做出不同的反应。
Container:一个个Widget创建好之后,它们该怎么摆放是一个问题。因为Widget只知道自己长什么样子,却不知道自己该站在什么地 方。Container就是Widget的家,它是用来组织管理Widget的,一个个Widget会依次插入到Container合适的位置去(什么是 合适的位置?就是在这个Container中,除了相对于Container的相对位置以外,还有Z轴方向上的叠放次序,也就是说在垂直于屏幕的方向 上,Widget之间还是有先后顺序的,位于上层的Widget有可能挡住位于其下的Widget,这就是Z-Order的概念)。Widget本身是有 大小的,Container本身也是有大小的,所谓大小就是矩形区域的意思。所以位于Container中的Widget与它的Parent Container有一定的位置关系,分别是完全位于Container中、与Container一部分相交和完全位于Container外部。位于 Container外部的那部分是肯定不会被显示出来的,而位于Container内部的那部分如果在其上没有被其它Widget遮挡住,就可以显示出 来。Container同样根据定位方式可以分成以下几种:
XYContainer,这是一种最简单的Container,通过Widget的左上角位置和Container的左上角位置的偏移量确定Widget 的相对位置。这里水平方向为X方向,向右为正,竖直方向为Y方向,向下为正(一般程序中坐标系都是这么定的)。
PropContainer:这里的Prop是Proportion的缩写,也就是比例的意思。所以PropContainer实际上就是通过给它包含的 Widget在空间上按照比例分配大小来实现定位的。PropContainer需要指定是在X方向上的按比例分配还是Y方向上的按比例分配,不能同时在 X方向和Y方向按比例分配。PropContainer的好处是在不同分辨率的情况下,它都能够布局得很好。
ConstrainContainer:这是通过约束来实现定位的Container,Widget可以通过相对于Parent Container的上下左右边界的距离或者相对于Sibling Widgets边界的距离进行定位,使用起来比XYContainer和PropContainer都来得复杂,在特殊场合会比较有用,平时一般用得比较 少。
CardContainer:CardContainer很特殊,也很简单,因为它只考虑Z方向的叠放次序,而忽略X方向和Y方向的位置距离。它就像一叠 堆放整齐的纸牌,我们只能看到最上面的一张,而位于下面的是无法看到的。前面提到的3种Container,其实我们都是在X方向和Y方向对它们进行定 位,CardContainer根本不需要,所以它最简单。CardContainer的典型应用是和TabWidget的搭配。
所有的Container在Z方向的叠放次序都是在插入的时候指定的。另外,我们可以把一个Container看成是一个比较复杂的Widget,因为我 们可以通过ISHELL_QueryInterface(, AEEIID_WIDGET,)获取到与它相应的IWidget*,然后当做是一个简单的Widget调用WIDGET提供的方法。正因为如此,我们可以 把Container作为Child Widget插入到另一个Container中,来实现比较复杂的界面组织。也正因为如此,我们可以把Container作为WID_FORM插入到 Form中,作为Form的Widget。若不是如此,界面结构将是多么单调,所以对于Container来说,它本身可以作为Widget来使用是极具 意义的。
Decorator:Decorator和Container类似,也可以往里面装入Widget,只是Decorator只能往里面装入一个 Widget。它也不是用来给Widget布局的,而只是为Widget提供修饰服务的。Decorator同样可以被看成是一个Widget,通过 ISHELL_QueryInterface(, AEEIID_WIDGET,)获取到与它相应的IWidget*。MMI在开发Applet设计Form的时候,一般不会用到Decorator。它主 要在设计某些Widget的时候被用到,如Border Widget, TabWidget, ScrollbarWidget等。所以这里也不讨论其具体用法。
FORM:比较传统的理解可以这样认为:每个界面差不多都是一个FORM,正常情况下它包括顶上标题Title、底部的软键SoftKeys和中间 的那个ID为WID_FORM的Widget(前面提到过了),这个WID_FORM的Widget是Form中最重要的一部分,设计和创建Form的时 候,主要在设计和创建这个Widget(前面说过多数情况下它是Container,里面可以包含多个Widget和子Container,这种包含关系 是树形结构)。但是其实也可以这么理解,其实Form只是保存着一部分用来显示的信息,包括Title, WID_FORM的Widget和Softkeys等相关的数据;另外一部分重要的功能就是处理事件(IFORM 是直接从 IHANDLER继承下来的,本身没有增加任何方法),像IFORM_GetWidget(), IFORM_GetRootForm之类的接口实际上都不是IFORM虚函数表中的实现,都只是普通的inline函数。Form的很多功能包括显示都是 依赖RootForm的。
ROOTFORM:比较传统的理解可以这样认为:RootForm继承自Form,基本上每个Applet(只后台运行的Daemon程序除外)都 会创建一个属于自己的RootForm,负责维护应用中所有创建出来的Form。事实上,我们看到的Applet画面就是RootForm绘制的,也可以 理解成我们看到的是RootForm,因为RootForm才真正创建了........(待续).......