所谓事件就是指发送给GUI系统的消息,该消息通知GUI系统某种事情已经发生,要求作出响应。事件根据来源可分为以下几种:

1.计算机输入输出设备产生的中断事件,如鼠标和键盘同GUI系统的交互操作。这种事件是最原生的“底层”事件,一般都需要组件做深入处理,籍此触发更高抽象层次具有语义的逻辑事件。为GUI系统扩展自定义组件往往需要编写处理这些事件,派发高级逻辑事件。Swing中这些事件对应的有MouseEvent、KeyEvent等。


2.GUI系统触发的逻辑事件。这种事件是1中所提的原始事件经过组件的处理后发出的高级事件,比如Swing的JButton产生的ActionEvent。另外通知界面重绘的Paint事件也是高级逻辑事件,这种事件在接收到EXPOSE事件或系统显式调用repaint方法后触发的事件。


3.应用程序触发的事件。应用程序主动触发新事件可以有两种方式来进行,第一种是事件通过添加到系统事件队列进行派发。Swing中通过postEvent、repaint及invokeLater等方法,向系统事件队列添加事件。这种触发机制实质上是调度,触发事件的线程和事件派发线程可以不是同一个线程。事件被添加到系统事件队列后触发过程结束,而之后事件的处理要在事件派发线程上等待执行。第二种是通过调用组件的派发方法(Swing中是fireEventXXXX)触发。使用这种方法,事件对象不会被放到系统事件队列中去,而是直接传递给事件处理方法处理。它的触发机制实质上是函数调用。这种事件触发方式要求事件处理线程必须同时是事件派发线程。


GUI系统的事件模型根据事件处理线程和事件派发线程的关系可分为两种,一种是单线程模型,另一种是多线程模型。单线程模型是指事件的处理线程和派发线程是同一线程。事件从事件队列中取出之后,立即在当前线程中处理,处理完后才取下一个事件继续循环。多线程模型是指事件派发线程只负责从事件队列中获取事件。获取到新的事件后,它会启动新的事件处理线程,并将事件交由此线程处理,之后派发线程并不等待事件处理线程完成,而是立即获取下个事件进行派发。下面的图将这两种模型放在同一张示意图中进行了对比。

 


事件处理过程往往要对应用程序的数据进行改变,而这些数据往往也是组件的模型数据。多线程模型由于能产生多个处理线程同时处理,因此容易产生同步问题。这种同步问题主要体现在两个方面,一是程序数据的同步出现问题,另一个是组件的数据模型产生同步问题。对于GUI系统来说必须考虑组件的外观同其数据模型一致。多线程模型GUI系统需要采用复杂的同步机制来保持数据模型的同步,这种同步技能对于系统的开发者和应用开发者来说都是很高的要求。因此现代的GUI系统已经很少有这种模式了(我曾经在哪儿见过这种系统)。


单线程模型系统的事件处理顺序是固定的,因此能避免许多数据同步问题。但这种模型同样存在其问题,也就是所谓线程独占。后面还会详细论述。在单线程模型中,为了避免长时间事件处理占据线程,应用程序往往在事件处理方法中启动其他线程来完成事件的处理,而将事件派发线程留给耗时短的事件处理。为保证界面的一致性,使用单线程模型GUI系统的应用需要将对组件的操作放在事件派发线程上完成。


Swing和SWT所采用的事件模型都是单线程模型,它们的组件操作都是非线程安全的。AWT组件的操作则是线程安全的,它内部采用许多复杂的同步机制来完成这个操作。Swing和SWT的不同是SWT在运行时检查组件操作是否在派发线程上,如果不是则抛出异常警告,而Swing则需要程序员自己控制同步问题。

Swing使用Java平台JavaBeans的机制,使得其事件触发和事件处理的架构更加OO化。这在以前的文章中曾经详细介绍过Swing的事件处理器模型(Swing框架之Component:续文三)。SWT与Swing的事件处理器模型类似,但SWT的事件处理接口是不区分事件类型,需要应用程序使用switch语句分拣事件。

Swing的事件处理模型可以推广到任何JavaBeans,不一定是可视化的Swing组件。在这个模型中,JavaBeans本身既可以是事件源(被观察对象),也可以是事件处理器(观察者),JavaBeans也可以侦听自身的事件并且处理。比如前面文章所提的MyButton在处理鼠标事件时就是自己侦听自己发出的鼠标事件,自己既是事件源,又是事件处理器,形成自反系统。各种各样的JavaBeans通过这种机制联系成一张事件网,各种JavaBeans就是这个网上的节点,而它们之间的事件触发与事件处理关系就是这张网络上的线。当某个节点被外界或自身发出的事件所触发时,行成了事件的传播。这个过程很像网络上节点的振动引起周围周围节点振动的模型。下图示意了这种JavaBeans之间的事件网:



 例如new JscrollPane(new JtextArea())这个系统,它里面包括两个JScrollBar和一个JTextArea,当鼠标拖动事件触发JScrollBar时,JScrollBar处理了这个鼠标拖动事件,并发出滚动条拖动事件,这个事件传播给JTextArea,JTextArea处理这个拖动事件,相应的更新自己显示的内容,如果JTextArea之后又根据更新发出了一个新的事件,这个事件便会继续传播下去。


Swing的事件处理模型是Swing彰显OO特性的又一地方。对象和对象之间传递事件(消息),由此组成了分布式的对象网络。这种结构是自然界中普遍存在的结构,是面向对象分布式网络的基因架构。

最后说说这种单线程模型常见的问题。我们举例说明这个问题,这个例子在Swing框架之Component:续文三中曾经详细描述过。如下图所示,假设有个城市(类比图形界面),和外界沟通的渠道只有一个单向环形高速公路(类比事件队列循环),任何物资运输必须通过车辆(类比各种事件)单向运输到另一个点�����这个城市从外界获取木材和水等(类比鼠标、键盘等原生事件),通过自来水厂和木材厂(类比各种组件)加工成自来水和家具(类比逻辑事件),并将这些产品运输给居民或学校(类比消费逻辑事件的事件处理器)使用。



开始一切都正常,直到有一天,有个质量不好的卡车(类比耗时性事件)出发了。走到半路上,这个卡车突然坏了,于是司机下来修车。但是这车太难修,一修就是几天,结果后面的汽车无法前进,各种物资无法按时运到目的地。市民急了,市长虽然不停的打电话催运输公司,但即使运输公司多添加几辆车也没用。由于进城的唯一条路被那辆车给占着,所以再多的车辆也只能堵在路上。

不了解这种事件处理模型的人往往将时间复杂的任务放在处理函数中完成,这就造成了应用程序响应速度很慢。用户触发这个事件(卡车)后,由于事件(车辆)被阻塞在路上,用户界面(城市)就失去了响应(瘫痪)。早期的Swing开发者往往不了解这种模型,造成编写的Swing程序响应速度很慢,原因就是在此。这种问题同样发生在SWT中。具体这种问题怎么解决,可以阅读以前的文章:


Swing框架之Component:续文二
使用SwingWorker之一
使用SwingWorker之二
使用SwingWorker之三

今天的文章就到此为止,以后详细谈谈GUI系统的绘图部分。

评论:

期待中!!!

发表于 tommyjian 在 2007年10月17日, 07:58 上午 CST #

如果我是J2SE版的NetBeans,那么
里面有没有什么工具创建javaBeans,不然我自己写很多
set/get函数很累啊。
我要自己定义一个类然后写n多set/get函数。
问一下LZ是不是有什么工具可以处理。

发表于 ccy 在 2007年10月17日, 04:23 下午 CST #

ccy,

在NB 6中编辑器选中字段后,在右键菜单中选择:
Refactor->Encapsulate Fields ...

发表于 williamchen 在 2007年10月17日, 04:36 下午 CST #

多谢LZ,可以了。

发表于 ccy 在 2007年10月17日, 05:57 下午 CST #

我想在用jfreechart画成的k线图上用鼠标选中一块区域,然后就此区域的图像保存下来。请问如何捕获鼠标事件?

发表于 andyelvis 在 2007年11月01日, 10:03 上午 CST #

发表一条评论:
  • HTML语法: 禁用

This blog copyright 2009 by williamchen