在我们开始讨论事件处理之前,必须明确一点:Java原始的1.0版和现在开始于1.1版的版本之间在小应用程序处理事件的方式上有了根本的变化。1.0版的事件处理方法仍然被支持,但是不推荐在新的程序中应用。同时,许多支持老的1.0事件处理模型的方法已经不被推荐使用。新的方法应该被所有新的程序中应用,其中也包括那些为Java2编写的程序,因而也被这本书中所提供的程序所使用。
|
现在处理事件的方法是基于授权事件模型(delegation event model)的,这种模型定义了标准一致的机制去产生和处理事件。它的概念十分简单:一个源(source)产生一个事件(event)并把它送到一个或多个的监听器(listeners)那里。在这种方案中,监听器简单地等待,直到它收到一个事件。一旦事件被接受,监听器将处理这些事件,然后返回。这种设计的优点是那些处理事件的应用程序可以明确地和那些用来产生那些事件的用户接口程序分开。一个用户接口元素可以授权一段特定的代码处理一个事件。
在授权事件模型中,监听器为了接受一个事件通知必须注册。这样有一个重要的好处:通知只被发送给那些想接受的它们的监听器那里。这是一种比Java 1.0版设计的方法更有效的处理事件的方法。以前,一个事件按照封装的层次被传递直到它被一个组件处理。这需要组件接受那些它们不处理的事件,所以这样浪费了宝贵的时间。而授权事件模型去掉了这个开销。
注意:Java也允许你处理事件而不采用授权事件模型。这可以通过扩展一个awt组件来实现。这种技术在第22章的结尾被讨论。然而,授权事件模型是首选的方案。
接下来给出了事件的定义并且描述了事件源与监听器的任务。
|
在授权事件模型中,一个事件是一个描述了事件源的状态改变的对象。它可以作为一个人与图形用户接口相互作用的结果被产生。一些产生事件的活动可以是通过按一个按钮,用键盘输入一个字符,选择列表框中的一项,点击一下鼠标。许多别的用户操作也能作为例子列出。
事件可能不是由于用户接口的交互而直接发生的。例如,一个事件可能由于在定时器到期,一个计数器超过了一个值,一个软件或硬件错误发生,或者一个操作被完成而产生。你还可以自由地定义一些适用于你的应用程序的事件。
|
一个事件源是一个产生事件的对象。当这个对象内部的状态以某种方式改变时,事件就会产生。事件源可能产生不止一种事件。
一个事件源必须注册监听器以便监听器可以接受关于一个特定事件的通知。每一种事件有它自己的注册方法。这里是通用的形式:
public void addTypeListener(TypeListener el)
在这里,type是事件的名称,而el是一个事件监听器的引用。例如,注册一个键盘事件监听器的方法被叫做addKeyListener(),注册一个鼠标活动监听器的方法被叫做addMouseMotionListener( ),当一个事件发生时,所有被注册的监听器都被通知并收到一个事件对象的拷贝。这就是大家知道的多播(multicasting)。在所有的情况下,事件通知只被送给那些注册接受它们的监听器。
一些事件源可能只允许注册一个监听器。这种方法的通用形式如下所示:
public void addTypeListener(TypeListener el)
throwsjava.util.TooManyListenersException
在这里,type是事件的名称而el是一个事件监听器的引用。当这样一个事件发生时,被注册的监听器被通知。这就是大家知道的单播事件。
一个事件源必须也提供一个允许监听器注销一个特定事件的方法。这个方法的通用形式如下所示:
public void removeTypeListener(TypeListener el)
这里,type是事件的名字而el是一个事件监听器的引用。例如,为了注销一个键盘监听器,你将调用removeKeyListener()函数。
这些增加或删除监听器的方法被产生事件的事件源提供。例如,component类提供了那些增加或删除键盘和鼠标事件监听器的方法。
|
一个事件监听器是一个在事件发生时被通知的对象。它有两个要求。首先,为了可以接受到特殊类型事件的通知它必须在事件源中已经被注册。第二,它必须实现接受和处理通知的方法。
用于接受和处理事件的方法在java.awt.event中被定义为一系列的接口。例如,MouseMotionListener接口定义了两个在鼠标被拖动时接受通知的方法。 如果实现这个接口,任何对象都可以接受并处理这些事件的一部分。许多别的监听器接口以后将在别的章中被讨论。
|
Java事件处理机制的核心是这些代表事件的类。因而,我们从浏览事件类开始学习事件处理。正如你将看到的,它们提供一个一致而又易用的封装事件的方法。
在java.util中被封装的EventObject类是Java事件类层次结构的根节点。它是所有事件类的父类。它的一个构造函数如下所示:
EventObject(Object src)
这里,src是一个可以产生事件的对象。
EventObject类包括两个方法:getSource( )和toString( )。GetSource( )方法返回的是事件源。它通常的形式如下所示:
Object getSource( )
正如所期望的一样,返回的是等价于事件的一个字符串。
在java.awt包中被定义的AWTEvent类是EventObject类的子类。同时作为所有基于awt的事件的父类(不论直接还是间接),它在授权事件模型中被使用。它的getID()方法可以被用来决定事件的类型。这个方法的形式如下所示:
int getID( )
关于AWTEvent类的细节,在第22章将进一步讨论。需要明确的是在本节中我们讨论的所有其他类都是AWTEvent子类。
小结:
· EventObject 是所有时间类的父类
· AWTEvent是所有在授权事件模型中处理的AWT事件类的父类
java.awt.event这个包定义了一些能被各种用户接口单元产生的事件类型。 在表20-1中列举了这些事件类中最重要的一些并对它们的产生条件进行了简要的描述。每一类中最常用的构造函数及其他方法将在下一节中讲述。
|
在一个按钮被按下,列表框中的一项被选择,或者是一个菜单项被选择时都会产生一个ActionEvent类型的事件。在ActionEvent类中定义了四个用来表示功能修改的整型常量:ALT_MASK,CTRL_MASK,META_MASK和SHIFT_MASK。除此之外,还有一个整型常量ACTION_PERFORMED用来标识事件。
ActionEvent类有两个构造函数:
ActionEvent(Object src, int type, String cmd)
ActionEvent(Object src, int type, String cmd, intmodifiers)
在这里, src是一个事件源对象的引用。事件的类型由type指定, cmd是它的命令字符串,modifiers这个参数显示了在事件发生时,ALT, CTRL, META, 或 SHIFT中的哪一个修改键被按下。
你可以通过调用ActionEvent对象的getActionCommand( )方法来获得命令的名字,下面是这个方法。
String getActionCommand( )
例如,当一个按钮被按下时,一个ActionEvent类事件被产生,它的命令名和按钮上的标签相同。
int getModifiers( )
这个方法返回了一个值,它表示了在事件产生时ALT, CTRL, META, 或 SHIFT这些修改键哪一个被按下。
|
一个AdjustmentEvent类的事件由一个滚动条产生。调整事件有五种类型。在AdjustmentEvent类中定义了用于标识它们的整型常量。这些常量和意义在下面列出:
BLOCK_DECREMENT 用户点击滚动条内部减少这个值
BLOCK_INCREMENT 用户点击滚动条内部增加这个值
TRACK 滑块被拖动
UNIT_DECREMENT 滚动条端的按钮被点击减少它的值
UNIT_INCREMENT 滚动条端的按钮被点击增加它的值
除此之外,还有一个整数常量ADJUSTMENT_VALUE_CHANGED,它用来表示改变已经发生。
AdjustmentEvent类有两个构造函数:
AdjustmentEvent(Adjustable src, int id, int type, intdata)
在这里, src是一个产生事件的对象的引用。 id等于ADJUSTMENT_VALUE_CHANGED这个常量。事件的类型由type指定, data是与它相关的数据。
getAdjustable( )方法返回了产生事件的对象。它的形式如下所示:
Adjustable getAdjustable( )
通过getAdjustmentType()方法,可以获得调整事件的类型。它返回被AdjustmentEvent定义的常量之一。下面是通常的形式:
int getAdjustmentType( )
调整数量可以通过getValue()方法获得,它的原形如下所示:
int getValue( )
例如,当一个滚动条被调整时,这个方法返回了代表变化的值。
|
一个ComponentEvent事件通常在一个组件的大小、位置或者是可视性发生了改变时产生。组件的事件类型有四种。ComponentEvent这个类定义了用于标识它们的整型常量。这些常量和它们的意义如下所示:
COMPONENT_HIDDEN 组件被隐藏
COMPONENT_MOVED 组件被移动
COMPONENT_RESIZED 组件被改变大小
COMPONENT_SHOWN 组件被显示
ComponentEvent类有这样一个构造函数:
ComponentEvent(Component src, int type)
在这里,src是产生事件的对象的引用。Type指定了事件的类型。
ComponentEvent 类是 ContainerEvent, FocusEvent,KeyEvent, MouseEvent 和WindowEvent这几个类的父类。
getComponent( )方法返回了产生事件的组件。它的形式如下所示:
Component getComponent( )
|
一个ContainerEvent事件是在容器中被加入或删除一个组件时产生的。容器有两种事件类型。在ContainerEvent类中定义了用于标识它们的整型常量:COMPONENT_ADDED和COMPONENT_REMOVED。它们表示了在容器中加入和删除一个组件。
ContainerEvent是ComponentEvent类的子类,它有如下所示构造函数:
ContainerEvent(Component src, int type, Component comp)
在这里,src是产生事件的容器的引用。Type指定了事件的类型。Comp指定了从容器中被加入或删除的组件。
你可以通过调用getContainer()方法来获得产生这个事件的容器的一个引用,它的形式如下所示:
Container getContainer( )
通过调用getChild( )方法可以返回在容器中被加入或删除的组件。它的通常形式如下所示:
Component getChild( )
|
一个 FocusEvent是在一个组件获得或失去输入焦点时产生。这些事件用FOCUS_GAINED 和 FOCUS_LOST这两个整型变量来表示。
FocusEvent类是ComponentEvent类的子类,它有两个构造函数:
FocusEvent(Component src, int type)
FocusEvent(Component src, int type, booleantemporaryFlag)
在这里,src是产生事件的组件的引用。Type指定了事件的类型。如果焦点事件是暂时的,那么参数temporaryFlag被设为true。否则,它是false(一个暂时焦点事件被作为另一个用户接口操作的结果产生。例如,假如焦点在一个文本框中,如果用户移动鼠标去调整滚动条,这个焦点就会被暂时失去。)
通过调用isTemporary()方法可以知道焦点的改变是否是暂时的。它的调用形式如下所示:
boolean isTemporary( )
如果这个改变是暂时的,那么这个方法返回true,否则返回false。
|
InputEvent抽象类是ComponentEvent类的子类,同时是一个组件输入事件的父类。它的子类包括: KeyEvent类和MouseEvent类。 在InputEvent类中定义了如下所示的八个整型常量,它们被用来获得任何和这个事件有关的修改符的信息。
ALT_MASK BUTTON2_MASK META_MASK
ALT_GRAPH_MASK BUTTON3_MASK SHIFT_MASK
BUTTON1_MASK CTRL_MASK
isAltDown( ),isAltGraphDown( ),isControlDown( ),isMetaDown( )和isShiftDown( )等方法用来测试是否在事件发生时相应的修改符被按下。这些方法如下所示:
boolean isAltDown( )
boolean isAltGraphDown( )
boolean isControlDown( )
boolean isMetaDown( )
boolean isShiftDown( )
通过调用方法可以返回一个值,包含这个事件所有修改符的标志。如下所示:
int getModifiers( )
|
一个ItemEvent事件是当一个复选框或者列表框被点击,或者是一个可选择的菜单项被选择或取消选定时产生(复选框和列表框在本书的后面将作论述)。这个项事件有两种类型,它们可以用如下所示的整型常量标识。
DESELECTED 用户取消选定的一项
SELECTED 用户选择一项
除此之外,ItemEvent类还定义了一个整型常量ITEM_STATE_CHANGED,用它来表示一个状态的改变。
ItemEvent类有这样一个构造函数:
ItemEvent(ItemSelectable src, int type, Objectentry, int state)
在这里, src是一个产生事件组件的引用。例如, 它可能是一个列表或可选择元素。 Type指定了事件的类型。产生该项事件的特殊项在entry中被传递。该项当前的状态由state表示。
GetItem()方法能被用来获得一个产生事件的项的引用。如下所示:
Object getItem( )
getItemSelectable()方法能被用来获得一个产生事件的ItemSelectable对象的引用。如下所示:
ItemSelectablegetItemSelectable( )
列表框和可选框就是实现了ItemSelectable接口的用户接口元素的例子。 getStateChange( )方法返回了事件对应的状态(如选择或取消)。如下所示:
int getStateChange( )
|
一个KeyEvent事件是当键盘输入发生时产生。键盘事件有三种,它们分别用整型常量:KEY_PRESSED,KEY_RELEASED和KEY_TYPED来表示。前两个事件在任何键被按下或释放时发生。而最后一个事件只在产生一个字符时发生。请记住,不是所有被按下的键都产生字符。例如,按下SHIFT键就不能产生一个字符。
还有许多别的整型常量在KeyEvent类中被定义。例如,从VK_0到VK_9和从VK_A到VK_Z定义了与这些数字和字符等价的ASCII码。这里还有一些其他的:
VK_ENTER VK_ESCAPE VK_CANCEL VK_UP
VK_DOWN VK_LEFT VK_RIGHT VK_PAGE_DOWN
VK_PAGE_UP VK_SHIFT VK_ALT VK_CONTROL
VK常量指定了虚拟键值(virtual key codes)并且与任何control,shift或alt修改键不相关。
KeyEvent类是InputEvent类的子类,它有这样两个构造函数:
KeyEvent(Component src, int type, long when, intmodifiers, int code)
KeyEvent(Component src, int type, long when, intmodifiers, int code, char ch)
在这里,src是一个产生事件的组件的引用。Type指定了事件的类型。当这个键被按下时,系统时间在when里被传递。参数Modifiers决定了在键盘事件发生时那一个修改符被按下。像VK_UP和VK_A这样的虚拟键值在code中传递。如果与这些虚拟键值相对应的字符存在,则在ch中被传递,否则ch中是CHAR_UNDEFINED。对于KEY_TYPED事件,code将是VK_UNDEFINED。
KeyEvent类定义了一些方法,但是其中用的最多的是用来返回一个被输入的字符的方法和用来返回键值的方法getKeyCode()。它们的通常形式如下所示:
char getKeyChar( )
int getKeyCode( )
如果没有合法的字符可以返回,getKeyChar( )方法将返回CHAR_UNDEFINED。同样,在一个KEY_TYPED事件发生时,getKeyCode()方法返回的是VK_UNDEFINED。
|
鼠标事件有7种类型。在MouseEvent类中定义了如下所示的整型常量来表示它们:
MOUSE_CLICKED 用户点击鼠标
MOUSE_DRAGGED 用户拖动鼠标
MOUSE_ENTERED 鼠标进入一个组件内
MOUSE_EXITED 鼠标离开一个组件
MOUSE_MOVED 鼠标移动
MOUSE_PRESSED 鼠标被按下
MOUSE_RELEASED 鼠标被释放
MouseEvent类是InputEvent类的子类,它有如下所示的构造函数:
MouseEvent(Component src, int type, long when, intmodifiers, int x,int y, int clicks, boolean triggersPopup)
在这里,src是一个产生事件的组件的引用。Type指定了事件的类型。鼠标事件发生时的系统事件在when中被传递。 参数modifiers决定了在鼠标事件发生时哪一个修改键被按下。
鼠标的坐标在x,y中传递。点击的次数在clicks中传递。triggersPopup标志决定了是否由这个事件引发在平台上弹出一个弹出式菜单。
在这个类中用的最多的方法是getX()和getY( )。它们返回了在事件发生时,对应的鼠标所在坐标点的X和Y。形式如下所示:
int getX( )
int getY( )
相应的,你也可以用getPoint( )方法去获得鼠标的坐标。形式如下所示:
Point getPoint( )
它返回了一个Point对象,在这个对象中以整数成员变量的形式包含了x和y坐标。
translatePoint()方法可以改变事件发生的位置。它的形式如下所示:
void translatePoint(int x,int y)
在这里,参数x和y被加到了该事件的坐标中。
getClickCount( )方法可以获得这个事件中鼠标的点击次数。如下所示:
int getClickCount() isPopupTrigger( )方法可以测试是否这个事件将引起一个弹出式菜单在平台中弹出。如下所示:
boolean isPopupTrigger( )
|
这个类的实例描述了文本事件。当字符被用户或程序输入到文本框或文本域时,它们产生了文本事件。TextEvent类定义了整数常量:TEXT_VALUE_CHANGED。
这个类的一个构造函数如下所示:
TextEvent(Object src, int type)
在这里,src是一个产生事件的对象的引用。Type指定了事件的类型。
TextEvent类不包括在产生事件的文本组件中现有的字符。相反,你的程序必须用其他的与文本组件相关的方法来获得这些信息。这不同于那些其他的在本节中被讨论的事件对象。由于这个原因,这里没有TextEvent类的方法可以讨论。可以想到,一个文本事件通知作为监听器的信号将可以从一个特定的文本组件获得信息。
|
窗口事件有七种类型。在WindowEvent类中定义了用来表示它们的整数常量。这些常量和它们的意义如下所示:
WINDOW_ACTIVATED 窗口被激活
WINDOW_CLOSED 窗口已经被关闭
WINDOW_CLOSING 用户要求窗口被关闭
WINDOW_DEACTIVATED 窗口被禁止
WINDOW_DEICONIFIED 窗口被恢复
WINDOW_ICONIFIED 窗口被最小化
WINDOW_OPENED 窗口被打开
WindowEvent类是ComponentEvent类的子类。它的构造函数如下所示:
WindowEvent(Window src, int type)
在这里,src是一个产生事件的对象的引用。Type指定了事件的类型。
在这个类中用的最多的方法是getWindow()。它返回的是产生事件的Window对象。其一般形式如下所示:
Window getWindow( )
|
在表12-2中列举了一些可以产生我们在前面所描述的事件的用户接口组件。除了这些图形用户接口元素之外,其他组件,如一个小应用程序,也可以产生事件。例如,你可以在一个小应用程序中获得键盘和鼠标事件(你可能也建立了你自己的组件,它们也可以产生事件)。在本章中我们将只处理鼠标和键盘事件,但是接下来的两章将处理在表12-2中所列的事件源所产生的事件。
正如我们前面所解释的,在授权事件模型中有两部分:事件源和监听器。事件源是通过实现一些在java.awt.event包中被定义的接口而生成的。当一个事件产生的时候,事件源调用被监听器定义的相应的方法并提供一个事件对象作为参数。在表12-3中列出了通常用到的监听器接口,同时还简要的说明了它们所定义的方法。接下来将解释每一个接口包含的一些特殊方法。
在这个接口中定义了actionPerformed()方法,当一个动作事件发生时,它将被调用。一般形式如下所示:
void actionPerformed(ActionEvent ae)
|
在这个接口中定义了adjustmentValueChanged()方法,当一个调整事件发生时,它将被调用。其一般形式如下所示:
void adjustmentValueChanged(AdjustmentEvent ae)
|
在这个接口中定义了四个方法,当一个组件被改变大小、移动、显示或隐藏时,它们将被调用。其一般形式如下所示:
void componentResized(ComponentEvent ce)
void componentMoved(ComponentEvent ce)
void componentShown(ComponentEvent ce)
void componentHidden(ComponentEvent ce)
注意:AWT处理改变大小和移动事件。componentResized( )和componentMoved( )方法只用来提供通知。
|
在这个接口中定义了两个方法, 当一个组件被加入到一个容器中时, componentAdded( ) 方法将被调用。当一个组件从一个容器中删除时,componentRemoved()方法将被调用。这两个方法的一般形式如下所示:
void componentAdded(ContainerEvent ce)
void componentRemoved(ContainerEvent ce)
|
在这个接口中定义了两个方法,当一个组件获得键盘焦点时,focusGained( )方法将被调用。当一个组件失去键盘焦点时,focusLost()方法将被调用。这两个方法的一般形式如下所示:
void focusGained(FocusEvent fe)
void focusLost(FocusEvent fe)
|
在这个接口中定义了itemStateChanged()方法,当一个项的状态发生变化时,它将被调用。这个方法的原型如下所示:
void itemStateChanged(ItemEvent ie)
|
在这个接口中定义了三个方法。当一个键被按下和释放时,相应地keyPressed( )方法和keyReleased( )方法将被调用。当一个字符已经被输入时,keyTyped( )方法将被调用。
例如,如果一个用户按下和释放A键,通常有三个事件顺序产生:键被按下,键入和释放。如果一个用户按下和释放HOME键时,通常有两个事件顺序产生:键被按下和释放。
这些方法的一般形式如下所示:
void keyPressed(KeyEvent ke)
void keyReleased(KeyEvent ke)
void keyTyped(KeyEvent ke)
|
在这个接口中定义了五个方法,当鼠标在同一点被按下和释放时,mouseClicked( )方法将被调用。当鼠标进入一个组件时,mouseEntered()方法将被调用。当鼠标离开组件时,mouseExited( )方法将被调用。当鼠标被按下和释放时,相应的mousePressed( )方法和 mouseReleased( )方法将被调用。
这些方法的一般形式如下所示:
void mouseClicked(MouseEvent me)
void mouseEntered(MouseEvent me)
void mouseExited(MouseEvent me)
void mousePressed(MouseEvent me)
void mouseReleased(MouseEvent me)
|
在这个接口中定义了两个方法,当鼠标被拖动时,mouseDragged( )方法将被调用多次。
当鼠标被移动时,mouseMoved()方法将被调用多次。这些方法的一般形式如下所示:
void mouseDragged(MouseEvent me)
void mouseMoved(MouseEvent me)
|
在这个接口中定义了textChanged()方法,当文本区或文本域发生变化时,它将被调用。
这个方法的一般形式如下所示:
void textChanged(TextEvent te)
|
在这个接口中定义了七个方法。当一个窗口被激活或禁止时,windowActivated( )方法或windowDeactivated( )方法将相应地被调用。如果一个窗口被最小化,windowIconified( )方法将被调用。当一个窗口被恢复时,windowDeIconified()方法将被调用。当一个窗口被打开或关闭时,windowOpened( )方法或windowClosed( )方法将相应地被调用。当一个窗口正在被关闭时,windowClosing()方法将被调用。
这些方法的一般形式如下所示:
void windowActivated(WindowEvent we)
void windowClosed(WindowEvent we)
void windowClosing(WindowEvent we)
void windowDeactivated(WindowEvent we)
void windowDeiconified(WindowEvent we)
void windowIconified(WindowEvent we)
void windowOpened(WindowEvent we)
|
现在你已经学习了授权事件模型的原理,并且对它的各种组件有了总体的认识。下面让我们来具体实践一下。采用了授权事件模型编程的小应用程序确实十分简单。只需要如下所示两步:
1. 在监听器中实现相应的监听器接口,以便接受相应的事件。
2. 实现注册或注销(如果必要)监听器的代码,以便可以得到事件的通知。
请记住,一个事件源可能产生多种类型的事件。每一个事件都必须分别注册。当然,一个对象可以注册接受多种事件,但是它必须实现相应的所有事件监听器的接口。
为了明白授权事件模型实际上是如何工作的,我们将分析一个例子,在这个例子中处理了两个最常用的事件产生器:鼠标和键盘。
|
为何处理鼠标事件,你必须实现MouseListener接口和MouseMotionListener接口。接下来的小应用程序说明了这个过程。它在小应用程序所在窗口的状态栏中显示了鼠标的当前坐标。每当鼠标按钮被按下,在鼠标指针所在的位置将显示“Down”。而当鼠标按钮释放时,将显示“Up”。如果鼠标按钮被点击,“Mouse clicked”将被显示在小应用程序显示区域的左上角。
当鼠标进入或退出小应用程序窗口时,在小应用程序显示区域的左上角将显示一个消息。当你拖动鼠标时,跟随着被拖动的鼠标一个*将被显示。在这里注意两个变量:mouseX和mouseY,它们存放着在鼠标的按下,释放或拖动事件发生时鼠标的位置。接下来,这些坐标将在paint()方法中被使用,以便在这些事件发生的点显示输出。
// Demonstrate the mouse event handlers.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
/*
<appletcode="MouseEvents" width=300 height=100>
</applet>
*/
public class MouseEvents extends Applet
implementsMouseListener, MouseMotionListener {
String msg ="";
int mouseX = 0,mouseY = 0; // coordinates of mouse
public voidinit() {
addMouseListener(this);
addMouseMotionListener(this);
}
// Handle mouseclicked.
public voidmouseClicked(MouseEvent me) {
// savecoordinates
mouseX = 0;
mouseY = 10;
msg ="Mouse clicked.";
repaint();
}
// Handle mouseentered.
public voidmouseEntered(MouseEvent me) {
// savecoordinates
mouseX = 0;
mouseY = 10;
msg ="Mouse entered.";
repaint();
}
// Handle mouseexited.
public voidmouseExited(MouseEvent me) {
// savecoordinates
mouseX = 0;
mouseY = 10;
msg ="Mouse exited.";
repaint();
}
// Handle buttonpressed.
public voidmousePressed(MouseEvent me) {
// savecoordinates
mouseX =me.getX();
mouseY =me.getY();
msg = "Down";
repaint();
}
// Handle buttonreleased.
public voidmouseReleased(MouseEvent me) {
// savecoordinates
mouseX =me.getX();
mouseY =me.getY();
msg ="Up";
repaint();
}
// Handle mousedragged.
public voidmouseDragged(MouseEvent me) {
// savecoordinates
mouseX =me.getX();
mouseY =me.getY();
msg ="*";
showStatus("Dragging mouse at " + mouseX + ", " +mouseY);
repaint();
}
// Handle mousemoved.
public voidmouseMoved(MouseEvent me) {
// show status
showStatus("Moving mouse at " + me.getX() + ", " +me.getY());
}
// Display msg inapplet window at current X,Y location.
public voidpaint(Graphics g) {
g.drawString(msg, mouseX, mouseY);
}
}
这个例子的输出如下所示:
让我们来仔细看看这个例子。MouseEvents类扩展了Applet类,同时实现了MouseListener接口和MouseMotionListener接口。这两个接口包括了接受并处理各种鼠标事件的方法。请注意,在这里小应用程序不但是事件源,同时也是这些事件的监听器。这是因为支持addMouseListener( )方法和addMouseMotionListener()方法的Component类是Applet的超类。所以小应用程序不但是事件源而且还是监听器。
在init()方法中,这个小应用程序注册它自己为鼠标事件的监听器。这些是通过调用addMouseListener( )方法和addMouseMotionListener()方法来实现的,正如我们已经提到的,它们是类的成员方法,它们的原型如下所示:
synchronized void addMouseListener(MouseListener ml)
synchronized voidaddMouseMotionListener(MouseMotionListener mml)
在这里,ml是一个接受鼠标事件的对象的引用,而mml是一个接受鼠标运动事件的对象的引用。在这个程序里,它们是相同的一个对象。
接下来,这个小应用程序实现了在MouseListener接口和MouseMotionListener接口中定义的所有方法,以便对这些鼠标事件进行处理。每一个方法都处理了相应的事件,然后返回。
|
你可以采用与在前一章中鼠标事件范例相同的结构去处理键盘事件。当然,不同的是,你必须实现相应的KeyListener接口。
在分析这个例子之前,让我们回顾一下键盘事件是如何产生的。当一个键被按下时,一个KEY_PRESSED事件被产生。这就使keyPressed( )这个事件处理方法被调用。当这个键被释放时,一个KEY_RELEASED事件产生,相应的事件处理方法keyReleased( )被执行。如果一个字符被按键产生,那么一个KEY_TYPED事件将被产生,并且事件处理方法keyTyped( )将被调用。因此,每次用户按下一个键时,通常有两个或三个事件被产生。如果你关心的只是字符,那么你可以忽略由按键和释放键所产生的信息。然而,如果你的程序需要处理特殊的键,比如方向键,那么你必须通过调用keyPressed(
)这个事件处理方法来处理它们。
另外,在你的程序处理键盘事件之前,你的程序必须要获得输入焦点。通过调用requestFocus( )方法可以获得焦点,这个方法在Component类中被定义。如果你忽略了这一点,你的程序将不会获得任何键盘事件。
下面的程序演示了键盘输入的处理。它将回显按键到小应用程序窗口,并在窗口的状态栏上显示每一个按键被按下或释放的状态。
// Demonstrate the key event handlers.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
/*
<appletcode="SimpleKey" width=300 height=100>
</applet>
*/
public class SimpleKey extends Applet
implementsKeyListener {
String msg ="";
int X = 10, Y =20; // output coordinates
public voidinit() {
addKeyListener(this);
requestFocus();// request input focus
}
public voidkeyPressed(KeyEvent ke) {
showStatus("Key Down");
}
public voidkeyReleased(KeyEvent ke) {
showStatus("Key Up");
}
public voidkeyTyped(KeyEvent ke) {
msg +=ke.getKeyChar();
repaint();
}
// Displaykeystrokes.
public voidpaint(Graphics g) {
g.drawString(msg, X, Y);
}
}
这个例子的输出如下所示:
如果你想处理特殊的键,比如方向键,你需要在keyPressed( )这个事件处理方法中进行处理。它们不能通过 keyTyped()这个事件处理方法来处理。为了表示这些键,你需要使用它们的虚拟键值。作为说明,接下来的这个小应用程序将输出一些特殊键的名字。
// Demonstrate some virtual key codes.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
/*
<appletcode="KeyEvents" width=300 height=100>
</applet>
*/
public class KeyEvents extends Applet
implementsKeyListener {
String msg ="";
int X = 10, Y =20; // output coordinates
public voidinit() {
addKeyListener(this);
requestFocus();// request input focus
}
public voidkeyPressed(KeyEvent ke) {
showStatus("Key Down");
int key =ke.getKeyCode();
switch(key) {
caseKeyEvent.VK_F1:
msg +="<F1>";
break;
caseKeyEvent.VK_F2:
msg +="<F2>";
break;
caseKeyEvent.VK_F3:
msg +="<F3>";
break;
caseKeyEvent.VK_PAGE_DOWN:
msg +="<PgDn>";
break;
caseKeyEvent.VK_PAGE_UP:
msg +="<PgUp>";
break;
caseKeyEvent.VK_LEFT:
msg += "<Left Arrow>";
break;
caseKeyEvent.VK_RIGHT:
msg +="<Right Arrow>";
break;
}
repaint();
}
public voidkeyReleased(KeyEvent ke) {
showStatus("Key Up");
}
public voidkeyTyped(KeyEvent ke) {
msg +=ke.getKeyChar();
repaint();
}
// Displaykeystrokes.
public voidpaint(Graphics g) {
g.drawString(msg, X, Y);
}
}
这个例子的输出如下所示:
在键盘和鼠标事件处理的例子中展示的程序过程,可以被用于任何类型的事件处理,包括那些由控件产生的事件。在最后一章,你将看到许多处理其他类型事件的例子,但是它们采用的基本结构同前面所描述的例子程序是一样的。
|
Java提供了一个适配器类(adapter class),它可以使一些情况下的事件处理变得简单。一个适配器类实现并提供了一个事件监听器接口中所有的方法,但这些方法都是空方法。
当你只想接受和处理特定的事件监听器接口对应的一部分事件时,适配器类将十分有用。你可以定义一个扩展了相应适配器类的新类来作为事件监听器,然后只实现那些你感兴趣的事件处理方法。
例如,MouseMotionAdapter类有两个方法: mouseDragged( )方法和mouseMoved( )方法。
这些空方法的声明被定义在MouseMotionListener接口中。如果你只对鼠标的拖动事件感兴趣,那么你可以简单地继承MouseMotionAdapter类并实现mouseDragged( )方法。MouseMotionAdapter类中实现mouseMoved( )方法的空方法将替你处理鼠标运动事件。
在表12-4中列出了在java.awt.event包中定义的适配器类,并且注明了它们所实现的接口。
接下来的例子将采用一个适配器。当鼠标被点击或拖动时,相应的信息将被显示在小应用程序查看器和浏览器的状态栏上,而其他鼠标事件将被忽略掉。这个程序有三个类。
AdapterDemo类扩展了Applet类。它的init( )方法产生了一个MyMouseAdapter类的实例,并且注册这个对象去接受鼠标事件通知。它也生成了一个类的实例,并且注册这个对象去接受鼠标运动事件通知。它们的构造函数都是以一个小应用程序的引用作为参数的。
MyMouseAdapter类实现了mouseClicked( )方法。其他鼠标事件在继承MouseAdapter类的代码中被忽略。
MyMouseMotionAdapter类实现了mouseDragged( )方法。别的鼠标运动事件在从MouseMotionAdapter类继承到的代码中被忽略。
请注意到我们的这两个事件监听器类都保存了一个小应用程序的引用。这个被作为参数传递给构造函数的信息在下面将被用来调用showStatus( )方法。
// Demonstrate an adapter.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
/*
<appletcode="AdapterDemo" width=300 height=100>
</applet>
*/
public class AdapterDemo extends Applet {
public voidinit() {
addMouseListener(new MyMouseAdapter(this));
addMouseMotionListener(new MyMouseMotionAdapter(this));
}
}
class MyMouseAdapter extends MouseAdapter {
AdapterDemoadapterDemo;
publicMyMouseAdapter(AdapterDemo adapterDemo) {
this.adapterDemo = adapterDemo;
}
// Handle mouseclicked.
public voidmouseClicked(MouseEvent me) {
adapterDemo.showStatus("Mouse clicked");
}
}
class MyMouseMotionAdapter extends MouseMotionAdapter {
AdapterDemoadapterDemo;
publicMyMouseMotionAdapter(AdapterDemo adapterDemo) {
this.adapterDemo = adapterDemo;
}
// Handle mousedragged.
public voidmouseDragged(MouseEvent me) {
adapterDemo.showStatus("Mouse dragged");
}
}
正如你看到的,我们不必去实现MouseMotionListener接口和MouseListener接口定义的全部方法,这就让我们省去了相当多的处理,也预防了由空方法带来的代码混乱。作为一个练习,你最好能用KeyAdapter类来重写一下前面键盘输入中的一个例子。
一个内部类(innerclass)是一个定义在其他类或者甚至是表达式中的类。在这一章我们将说明在使用事件适配器类时,如何通过使用内部类来简化代码。
为了理解使用内部类的好处,让我们看看如下所示列出的这个小应用程序。它没有使用一个内部类。它主要是在鼠标被按下时,在小应用程序查看器或浏览器的状态栏上显示“Mouse Pressed”这个字符串。在这个程序中有两个并列的类。MousePressedDemo类扩展了Applet类,而MyMouseAdapter类扩展了MouseAdapter类。MousePressedDemo类的init( )方法产生了一个MyMouseAdapter类的实例,并且将这个对象作为参数提供给addMouseListener(
)方法。
请注意,一个小应用程序的引用被作为参数提供给了MyMouseAdapter类的构造函数。这个引用被存储在这个实例的成员变量中,以便以后在mousePressed( )方法中使用。当鼠标被按下时,在mousePressed()方法中通过被存储的小应用程序的引用,调用了小应用程序的showStatus( )方法。换句话说,showStatus( )方法被存储在MyMouseAdapter类中的小应用程序的引用调用了。
// This applet does NOT use an inner class.
import java.applet.*;
import java.awt.event.*;
/*
<appletcode="MousePressedDemo" width=200 height=100>
</applet>
*/
public class MousePressedDemo extends Applet {
public voidinit() {
addMouseListener(new MyMouseAdapter(this));
}
}
class MyMouseAdapter extends MouseAdapter {
MousePressedDemomousePressedDemo;
publicMyMouseAdapter(MousePressedDemo mousePressedDemo) {
this.mousePressedDemo = mousePressedDemo;
}
public voidmousePressed(MouseEvent me) {
mousePressedDemo.showStatus("Mouse Pressed.");
}
}
接下来,让我们看看如何通过使用内部类来改进前面的这个程序。在这个程序里,InnerClassDemo类是一个扩展了Applet类的最高类。MyMouseAdapter类是一个扩展了MouseAdapter类的内部类。由于MyMouseAdapter类被定义在InnerClassDemo类的范围之内,所以它可以访问这个类中的所有成员变量和方法。因此,mousePressed( )方法可以直接调用showStatus( )方法。这就不再需要通过存储一个小应用程序的引用来完成这些工作了。 因此,也就不再需要为了调用这个对象,而给MyMouseAdapter()方法传递一个小应用程序的引用。
// Inner class demo.
import java.applet.*;
import java.awt.event.*;
/*
<appletcode="InnerClassDemo" width=200 height=100>
</applet>
*/
public class InnerClassDemo extends Applet {
public voidinit() {
addMouseListener(new MyMouseAdapter());
}
classMyMouseAdapter extends MouseAdapter {
public void mousePressed(MouseEventme) {
showStatus("Mouse Pressed");
}
}
}
|
一个匿名内部类(Anonymousinner class)是一个没有指定名称的类。在这一节中,我们将说明一个匿名内部类如何有利于处理事件程序的编写。让我们看一下下面的这个小应用程序。像以前一样,它还是在鼠标被按下时,在小应用程序查看器或浏览器的状态栏上显示“Mouse Pressed”这个字符串。
// Anonymous inner class demo.
import java.applet.*;
import java.awt.event.*;
/*
<appletcode="AnonymousInnerClassDemo" width=200 height=100>
</applet>
*/
public class AnonymousInnerClassDemo extends Applet {
public voidinit() {
addMouseListener(new MouseAdapter() {
public voidmousePressed(MouseEvent me) {
showStatus("Mouse Pressed");
}
});
}
}
在这个程序中只有一个最高类:AnonymousInnerClassDemo类。在init( )方法中调用了方法addMouseListener( ),它的参数是一个定义并产生一个匿名内部类的表达式。让我们来仔细分析一下这个表达式。
new MouseAdapter( ){ ... }的语法意义是告诉编译器在括号中的代码定义了一个匿名内部类。此外,这个类扩展了MouseAdapter类。这个新类没有名称,但是在这个表达式被执行时,自动实例化。
由于这个匿名内部类被定义在AnonymousInnerClassDemo类的范围之内,它可以访问这个类中的所有成员变量和方法。因此,也就可以直接的调用showStatus( )方法了。
像刚才所说明的那样,不论是有名称的内部类还是匿名内部类,都以一种简单而有效的方式解决了一个让人讨厌的问题。它们也让你的代码更加有效。
|
1. 思考生活中,有没有关于事件触发的例子;比如门开的时候有人进来发生了什么事?能不能多举几个这样的示例?
|
在本章中,我们主要学习了:
u 事件类的结构、应用范围;
u 事件监听器接口的应用;
|
英文 全文 中文
ActionEvent ActionEvent 动作事件
AdjustmentEvent AdjustmentEvent 调整事件
ComponentEvent ComponentEvent 组件事件
ContainerEvent ContainerEvent 容器事件
FocusEvent FocusEvent 焦点事件
InputEvent InputEvent 输入事件
ItemEvent ItemEvent 条目事件
KeyEvent KeyEvent 键盘事件
MouseEvent MouseEvent 鼠标事件
TextEvent TextEvent 文本事件
WindowEvent WindowEvent 窗口事件
ActionListener ActionListener 动作监听器
AdjustmentListener AdjustmentListener 调整事件监听器
ComponentListener ComponentListener 组件监听器
ContainerListener ContainerListener 容器监听器
FocusListener FocusListener 焦点监听器
ItemListener ItemListener 条目监听器
KeyListener KeyListener 键盘监听器
MouseListener MouseListener 鼠标监听器
MouseMotionListener MouseMotionListener 鼠标动作监听器
TextListener TextListener 文本监听器
WindowListener WindowListener 窗口监听器
Adapter Adapter 适配器类
Inner Inner 内部类
|
利用本章知识,实现一个画图工具,比如画直线,椭圆,画点等;并且可以利用颜色去画带色彩的线条和图形;;