基于嵌入式系统Java图形用户界面开发包AWT的设计实现

时间:2022-09-22 05:18:44

基于嵌入式系统Java图形用户界面开发包AWT的设计实现

摘要:该文针对介绍了J2ME平台的基本概念和体系结构,着重讨论并设计实现了一套适用于嵌入式的图形界面应用开发AWT包。

关键词:J2ME;嵌入式;AWT

中图法分类号:TP311文献标识码:A文章编号:1009-3044(2010)22-6311-03

The Design of JAVA GUI Program: AWT on Embedded System

BAN Hai-qing

(College of Computer Science and Information, Guizhou University, Guiyang 550025, China)

Abstract: This paper will explain how to build AWT in embedded systems on the base of analyzing the inside theory of Java, Meanwhile, this paper still cover the configuration of AWT class libraries according to J2ME specifications.

Key words: Java 2Micro Edition; embedded system; AWT

1 Java平台简介

Java平台由Java虚拟机(Java Virtual Machine)和Java 应用编程接口(API)构成。API为Java应用提供了一个独立于操作系统的标准接口,可分为基本部分和扩展部分。在硬件或操作系统平台上安装一个Java平台之后,Java应用程序就可运行。现在Java平台已经嵌入了几乎所有的操作系统。这样Java程序可以只编译一次,就可以在各种系统中运行。

2 J2ME体系结构

J2ME 平台是由配置(Configuration)和简表(Profile)构成的。

配置:主要配置有两种,连接的设备配置(CDC)和限制连接的设备配置(CLDC)。CDC(全称Connected Devices Configuration)其目标设备特征为:2M 以上内存,具有网络连接能力,通常为无线网络,32位或者64位的处理器运行需求,至少需要512K用于运行Java虚拟机,至少需要256K用于运行时内存分配,CDC是CLDC的一个超集,包含CLDC中的所有类 ―― 所以CLDC之上的简表也可以构建在CDC之上。

CLDC(全称Connected Limited Devices Configuration)其目标设备特征为:512KB 以下内存,有限能源供应(通常使用电池),有限或非持续网络连接,简单的用户界面,16位或者32位的处理器。运行需求:至少128K的固定内存用于存储虚拟机和CLDC的类库,至少32K的动态内存供虚拟机运行时使用(堆栈等)

简表:CLDC之上常用的是:MIDP(全称Mobile Information Devices Profile)版本MIDP2.0。MIDP致力于解决像用户界面、持久存储、联网和应用程序生命周期这样的问题CDC之上常用的有:FP(全称Foundation Profile)、PBP(全称Personal Basis Profile)、PP(全称Personal Profile)。

可选包:CLDC之上的可选包:Bluetooth、Mobile Media、Wireless Messaging API、Web Services API等。CDC之上的可选包(一般是基于PBP):RMI、JDBC、JMF、JSSE(Java安全套接字扩展)。

J2ME体系结构图如图1。

3 图型接口AWT包的设计实现

这套AWT包是模仿PC平台上运行的J2SE中的AWT包来基于CLDC/MIDP设计并且实现的,实现的接口可以以源码的形式呈现给二次开发人员使用,也可以编译后以库的形式。AWT的实现包括对组件、窗口系统的布局管理,以及各种事件的处理。

3.1 AWT的基础架构基础工具类

颜色:它主要是对RGB颜色的封装,并不包括Alpha位。类中提供了常用颜色的静态字段。

字体:字体相关的主要有记录字体状态的Font类和与平台相关的度量字体长度的FontMetrics类。Font类的的构造函数包含3个参数,这里说下它们在MIDP的Font体系上的映射:

name:这个是字体的名字,自定义。

style:与MIDP的Font的style对应

size:小于10,对应MIDP的SIZE_SMALL;大于12,对应MIDP的SIZE_LARGE;中间的值对应SIZE_MEDIUM。

画笔:画笔类是整个AWT框架中比较核心的一个类,特别是它与MIDP体系中的Graphics类有很大的不同。MIDP体系中的Graphics对于一个Canvas只有一个实例。AWT中的Graphics不止一个实例,每个组件都有自己独立的Graphics实例,甚至组件在实现自己的paint()时也可以创建内部的Graphics实例。而每个实例中都包含了一些偏移信息,这些信息能算出它相对坐标原点的绝对坐标及clip区域。所以在用MIDP的Graphics实现AWT的Graphics的功能时,每次绘制都要算出绝对坐标及clip区域。

3.2 组件模型

组件:Component类是AWT框架下所有组件的基类,这里主要说明下使用过程中要注意的事项。窗口的大小默认是全屏的,其他的组件必须要设置大小,不然其大小就是Dimension(0,0)。组件(非窗口)必须要被添加到一个容器中,并最终属于一个窗口才会被绘制。组件的基本方法中,凡是涉及到改变组件外在表现的,都在方法内部调用了repaint()方法,所以没必要重复调用。组件在执行repaint操作时会进行一些检查:1)组件是否可见,即 isVisible() == true;2)组件的顶级容器是否是一个窗口,该窗口是否是active的,不满足上述条件就不执行任何操作。3)检查组件的背景是否是透明的(即getBackground() == Color.LUCID),若是透明的会调用其父容器的repaint方法,repaint区域就是子组件的repaint区域。所以组件背景也尽量不要是透明,因为它会引发大量的绘制操作。

隐藏组件时(即setVisible(false)),框架会自动调用其父容器的repaint方法,重绘该子组件所占区域。这也说明了两个注意点:1)不需要我们自己调父容器的重绘;2)两个组件重叠,但还不属于同一个父容器的情况一定不要出现。因为这样的话,重叠的组件不会被自动重绘。

容器:Container类是AWT框架的基础容器类,也是一个组件。由于容器中是用数组存放子组件的,且绘制的时候也是按照数组的顺序依次绘制(从0到length-1)。所以假如有组件重叠的情况,需要考虑组件的覆盖情况,并按顺序添加组件。最好还是尽量避免组件的重叠。实现容器类时,覆写的容器paint()中只需添加容器自己的绘制操作。调用容器的repaint()时,框架会先执行容器类的paint(),然后依次从0到length-1执行其子组件的绘制操作(其间会先检查子组件的可见性以及与重绘区域是否有交集)。

容器、组件包含的层次关系如图2所示。

事件模型:如图3所示。

事件源:激发事件;

事件监听者:处理事件;

事件:被封装放入消息对列等待处理(键盘事件、绘图事件、窗口事件、焦点事件、动作事件);

事件队列:提交事件、处理事件;

在事件模型中,事件处理是从一个事件源授权到一个或多个事件监听者,由此得到授权事件模型这个名字。事件模型的原理很简单:构件激发事件,事件监听者监听和执行事件。可以通过调用add***Listener(***Listener)方法向构件注册监听者。把监听者加入到构件中以后,如果构件激发相应类型的事件,那么监听者接口中的适当方法将被调用。这里用到了设计模式中的观察着模式。

事件队列:事件队列是由一个单独的线程处理的,该线程不停地从队列中取出事件,然后按类型处理,没有事件就wait。

键盘事件:键盘事件的处理有两种方式,对于自定义的组件,你想把对一些按键的响应作为组件的默认行为的话,可以覆写Component的processKeyEvent(KeyEvent ke),但在覆写方法的最后还是要加上把键盘事件通知给监听器的代码。另外一种方式就是给组件添加KeyListener,需要注意的是首先要使组件enableEvents(AWTEvent. KEY_EVENT_MASK),其他的事件监听也要如此。然后就是目前的实现只能添加一个监听器,后面的会把前面的覆盖,有需要你们可以自己改。

键盘事件处理的流程大体是:先找到当前active的窗口(只会有一个),然后找到该窗口的焦点组件(也只会有一个),发给他处理,然后再一级级往其父容器传递,一直传到顶级容器。如果你想让这个键盘事件到此为止,不再被父容器处理,就调用KeyEvent的consume()方法(其他事件类似)。键盘事件的处理还要注意的就是对keyPressed()和keyReleased()的选择,对于占用资源较多的操作最好是放在keyReleased()中(因为porting不支持keyRepeated消息,对于按住某个键是通过不停地发keyPressed消息模拟的)。另外在按键响应中存在焦点切换时也应该放在keyReleased()中。例如,目前焦点在A组件上,响应按键KeyEvent_VK_0时要切换到B组件上(常见的切换焦点的操作有:1)requestFocus();2)弹出新窗口)。若你把这个操作放在keyPressed()中处理,就会出现按下按键KeyEvent_VK_0,焦点切换到的组件B,松开按键KeyEvent_VK_0时,B组件接收到了按键KeyEvent_VK_0的keyReleased()事件,这个显示不是你期望的。

焦点事件:焦点切换是个主动抢占的模式,即谁请求(requestFocus())谁就会获得焦点,当然组件要先enableEvents(AWTEvent. FOCUS_EVENT_MASK)。若想监听焦点切换的事件,可以添加FocusListener,焦点事件有FOCUS_GAINED(获得焦点)和FOCUS_LOST(失去焦点)两种类型。焦点切换是同一个窗口内的概念,因为焦点组件属于一个窗口(Window类的一个字段),每个窗口都有自己独立的焦点组件。假如你调用另外一个窗口的组件的requestFocus(),而那个窗口并没有requestActive()的话,在表现上不会有任何变化(虽然会调用那个组件的repaint方法,但由于它的顶级容器不是active,所以不会做任何绘制操作),但那个窗口的焦点组件发生了变化。

绘制事件:绘制事件是在调用组件的repaint方法时,框架内部产生内部处理的。由于是由单线程独立处理的,所以不会出现绘图顺序错乱的问题。

该事件对于应用开发人员是不可见的。

动作事件:动作事件是指ActionEvent,它包括遥控器按键中标准按键以外的按键。ActionEvent也不是走的KeyEvent的事件队列的流程,而是一个独立的流程。一般是直接发给事件源组件,即getSource(),它由实现了ActionCommandSourceLocator这个接口的类确定,然后由事件源组件的processActionEvent(ActionEvent ae)来处理。

另外addActionListener()方法并不是Component公共的方法,假如你要自定义的组件能添加监听器监听动作事件,就必须自己实现该方法。

窗口事件:窗口事件是指WindowEvent,主要用到的事件类型是WINDOW_ACTIVATED、WINDOW_DEACTIVATED,其他的基本用不到。

窗口事件的存在主要是因为,应用可能需要在窗口被激活或取消激活的时候进行某些操作(比如,启动或停止一些timer),但requestActive()并不能保证窗口就能被激活,因为框架里还有模态对话框,当当前窗口是模态对话框时,普通窗口的requestActive()就会失败。所以这些操作不能在requestActive()之前或之后做,而应该在接收到WindowEvent时做。

窗口管理:窗口类是Window,它主要有两个子类Frame和Dialog。Frame主要用作普通窗口,它必须是全屏的。而Dialog可以不是全屏的,且框架中对话框都是模态的(模态指当对话框为当前窗口时,阻断其他窗口的激活请求,除非有另外一个对话框,它的owner是当前的对话框) 。窗口的请求激活是调用方法requestActive(),请求隐藏的方法是dispose()。窗口管理器中记录了当前的Frame或Dialog。

3.3 组件开发

下面内容是根据上面设计实现的AWT包中的接口来进行一些组件的开发工作,目的就是验证所设计的接口的实用性。

开发组件:

开发组件一般就是继承Component类,然后覆写它的paint()方法。

首先要注意的就是对于paint(Graphics g)方法中参数Graphics g的使用,这里的Graphics是AWT框架中的,即Graphics。要注意到:

1)g是经过了偏移和剪贴区设置的。2)尽量不要修改参数g的偏移和clip区域,特别是你想把paint()方法分成几块来实现的时候。

对于组件可以接收的事件,可以在构造函数中就设定好,免得每次使用该组件时都要设置。假如组件有默认响应的按键事件,可以覆写Component的processKeyEvent(KeyEvent ke)方法(如何实现前面讲键盘事件部分有说明),而不推荐内部定义监听器。

另外就是对绘制要用到的字符串的获取,假如获取需要较长时间,且这些字符串不是变化的,那最好是定义字段,在构造函数中初始化好这些字段(即以空间换时间),因为paint()方法调用频率比较高(至少比你以为的要高)。

还有就是对组件在有焦点、没焦点两种状态的不同表现需要在paint()方法中做相应判断实现。

开发容器

在继承容器类中,一种是为了开发容器而继承容器类;另一种是想把几个基础组件的组合成一个复杂组件而需要继承Container类。但这两种情况涉及到的内容都差不多。一般的容器可以使用JForm或EForm。

开发容器最主要的就是注意setSize()方法的覆写。一个容器内部往往是有一些已经明确的组件(对外部可见),比如JForm中的titleBar、statusBar。这些组件的location和size都要受到容器的size的影响。所以覆写Container的setSize()方法,在其中添加受其影响的子组件的location和size的设置。图4表示为简单的AWT的设计实现。

4 结束语

经过上述的设计实现,我们实现了一套可以在嵌入式设备上进行基于JVM的图形用户开发包,实用此开发包可以简化应用开发中的设计,并且在实际开发中能快速高效的进行开发和调试。开发的应用可以快速的移植到不同的嵌入式平台上去。

参考文献:

[1] 李金铭,林晓宇,宁正元.面向对象程序设计:Java[M].北京:清华大学出版社,北京交通大学出版社,2005

[2] Cay S,Horstmann,Gary Cornell.Core Java 2,Volume 1-Fumdamentals基础篇(美)[M].北京:清华大学出版社,2003.

[3] Cay S,Horstmann,Gary Cornell.Java 2核心技术(卷I):基础知识[M].volume I:Fundamentals (美).程峰,黄若波,章恒,译.北京:机械工业出版社,2003.

[4] Cay S,Horstmann,Gary Cornell.最新Java 2核心技术(卷Ⅱ):高级特性[M].(v1.3) 5E (美).王建华,董志敏,杨保明,等,译.北京:机械工业出版社,2003.

上一篇:高速广域网技术探讨 下一篇:基于Visual C++ 2010 的商场抽奖系统