java之十二 事 件 处 理

在我们开始讨论事件处理之前,必须明确一点: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类型的事件。在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类的事件由一个滚动条产生。调整事件有五种类型。在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事件通常在一个组件的大小、位置或者是可视性发生了改变时产生。组件的事件类型有四种。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事件是在容器中被加入或删除一个组件时产生的。容器有两种事件类型。在ContainerEvent类中定义了用于标识它们的整型常量:COMPONENT_ADDED和COMPONENT_REMOVED。它们表示了在容器中加入和删除一个组件。

ContainerEvent是ComponentEvent类的子类,它有如下所示构造函数:

ContainerEvent(Component src, int type, Component comp)

在这里,src是产生事件的容器的引用。Type指定了事件的类型。Comp指定了从容器中被加入或删除的组件。

你可以通过调用getContainer()方法来获得产生这个事件的容器的一个引用,它的形式如下所示:

Container getContainer( )

通过调用getChild( )方法可以返回在容器中被加入或删除的组件。它的通常形式如下所示:

Component getChild( )

FocusEvent 类

一个 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类

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类

一个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类

一个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。

MouseEvent 类

鼠标事件有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( )

The TextEvent Class

这个类的实例描述了文本事件。当字符被用户或程序输入到文本框或文本域时,它们产生了文本事件。TextEvent类定义了整数常量:TEXT_VALUE_CHANGED。

这个类的一个构造函数如下所示:

TextEvent(Object src, int type)

在这里,src是一个产生事件的对象的引用。Type指定了事件的类型。

TextEvent类不包括在产生事件的文本组件中现有的字符。相反,你的程序必须用其他的与文本组件相关的方法来获得这些信息。这不同于那些其他的在本节中被讨论的事件对象。由于这个原因,这里没有TextEvent类的方法可以讨论。可以想到,一个文本事件通知作为监听器的信号将可以从一个特定的文本组件获得信息。

WindowEvent类

窗口事件有七种类型。在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)

AdjustmentListener 接口

在这个接口中定义了adjustmentValueChanged()方法,当一个调整事件发生时,它将被调用。其一般形式如下所示:

void adjustmentValueChanged(AdjustmentEvent ae)

ComponentListener 接口

在这个接口中定义了四个方法,当一个组件被改变大小、移动、显示或隐藏时,它们将被调用。其一般形式如下所示:

void componentResized(ComponentEvent ce)

void componentMoved(ComponentEvent ce)

void componentShown(ComponentEvent ce)

void componentHidden(ComponentEvent ce)

注意:AWT处理改变大小和移动事件。componentResized( )和componentMoved( )方法只用来提供通知。

ContainerListener 接口

在这个接口中定义了两个方法, 当一个组件被加入到一个容器中时, componentAdded( ) 方法将被调用。当一个组件从一个容器中删除时,componentRemoved()方法将被调用。这两个方法的一般形式如下所示:

void componentAdded(ContainerEvent ce)

void componentRemoved(ContainerEvent ce)

FocusListener 接口

在这个接口中定义了两个方法,当一个组件获得键盘焦点时,focusGained( )方法将被调用。当一个组件失去键盘焦点时,focusLost()方法将被调用。这两个方法的一般形式如下所示:

void focusGained(FocusEvent fe)

void focusLost(FocusEvent fe)

ItemListener 接口

在这个接口中定义了itemStateChanged()方法,当一个项的状态发生变化时,它将被调用。这个方法的原型如下所示:

void itemStateChanged(ItemEvent ie)

KeyListener 接口

在这个接口中定义了三个方法。当一个键被按下和释放时,相应地keyPressed( )方法和keyReleased( )方法将被调用。当一个字符已经被输入时,keyTyped( )方法将被调用。

例如,如果一个用户按下和释放A键,通常有三个事件顺序产生:键被按下,键入和释放。如果一个用户按下和释放HOME键时,通常有两个事件顺序产生:键被按下和释放。

这些方法的一般形式如下所示:

void keyPressed(KeyEvent ke)

void keyReleased(KeyEvent ke)

void keyTyped(KeyEvent ke)

MouseListener 接口

在这个接口中定义了五个方法,当鼠标在同一点被按下和释放时,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)

MouseMotionListener 接口

在这个接口中定义了两个方法,当鼠标被拖动时,mouseDragged( )方法将被调用多次。

当鼠标被移动时,mouseMoved()方法将被调用多次。这些方法的一般形式如下所示:

void mouseDragged(MouseEvent me)

void mouseMoved(MouseEvent me)

TextListener 接口

在这个接口中定义了textChanged()方法,当文本区或文本域发生变化时,它将被调用。

这个方法的一般形式如下所示:

void textChanged(TextEvent te)

WindowListener 接口

在这个接口中定义了七个方法。当一个窗口被激活或禁止时,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);

}

}

这个例子的输出如下所示:

在键盘和鼠标事件处理的例子中展示的程序过程,可以被用于任何类型的事件处理,包括那些由控件产生的事件。在最后一章,你将看到许多处理其他类型事件的例子,但是它们采用的基本结构同前面所描述的例子程序是一样的。

Adapter类

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             内部类

练习项目:

利用本章知识,实现一个画图工具,比如画直线,椭圆,画点等;并且可以利用颜色去画带色彩的线条和图形;;

时间: 2024-10-06 11:18:39

java之十二 事 件 处 理的相关文章

Java 第十二章 继承 笔记

Java 第十二章  继承 笔记 一.使用继承:     1)方便修改代码     2)减少代码量 二.super 继承object 类:super 访问父类的无参构造:super 指的是object 的无参构造.     例:子类调用父类:super.属性 / super.方法    注意:子类不能继承父类私有属性:得用set.get方法来调用:    super只能写在代码块的第一句:super只能调用非私有的方法:    super只能出现在子类的方法和构造方法中. 三.不能被继承的父类成

Java基础十二--多态是成员的特点

Java基础十二--多态是成员的特点 一.特点 1,成员变量. 编译和运行都参考等号的左边. 覆盖只发生在函数上,和变量没关系. Fu f = new Zi();System.out.println(f.num);//是父类,答案是3 2,成员函数(非静态). 编译看左边,运行看右边. 因为成员函数存在覆盖特性. Fu f = new Zi();//f.show();输出的是子类里面的show方法 3,静态函数. 编译和运行都看左边. 静态函数不具备多态性,多态性是对象的多态性,然后静态函数不涉

201671010117 2016-2017-2 《Java程序设计》Java第十二周学习心得

Java第十二周学习心得        在第十二周的理论课堂上,老师对1-4章的内容进行了测验,程序填空题和写程序题的分不是很高,程序题的重要部分都有遗漏,对于计算图书的总数那部分不会写,扣分较多,但是现在已经掌握了,接下来要在电脑上多多练习,周五老师对新内容第十章进行了讲解,这周到现在还没有遇到什么问题.

“全栈2019”Java第二十二章:控制流程语句中的决策语句if-else

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第二十二章:控制流程语句中的决策语句if-else 下一章 "全栈2019"Java第二十三章:流程控制语句中决策语句switch上篇 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf

“全栈2019”Java第九十二章:外部类与内部类成员覆盖详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第九十二章:外部类与内部类成员覆盖详解 下一章 "全栈2019"Java第九十三章:内部类应用场景(迭代器设计模式) 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"

Java第十二周作业

1.本周学习总结 1.1以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 1.1.1 请解释Thread类和Runnable接口实现多线程的区别 Thread类继承于Runnable接口,是Runnable接口的子类,使用Runnable接口实现多线程可以避免单继承的限制 使用Runnable接口实现多线程可以更加方便地实现数据共享的概念. 1.1.2 休眠 Thread.currentThread用法与this相似,用于取得当前执行的线程对象 Thread.sleep()主要是休眠,感觉

[Java面试十二]数据库概念相关

1. 什么是存储过程?它有什么优点? 答:存储过程是一组予编译的SQL语句,它的优点有: 允许模块化程序设计,就是说只需要创建一次过程,以后在程序中就可以调用该过程任意次. 允许更快执行,如果某操作需要执行大量SQL语句或重复执行,存储过程比SQL语句执行的要快. 减少网络流量,例如一个需要数百行的SQL代码的操作有一条执行语句完成,不需要在网络中发送数百行代码. 更好的安全机制,对于没有权限执行存储过程的用户,也可授权他们执行存储过程. 2. oracle的存储过程和函数有什么区别? Orac

Java笔记十二.常用API-Hashtable类及其与HashMap、HashSet的区别

常用API-Hashtable类及其与HashMap.HashSet的区别 一.Hashtable<K,V>类 1.概述 Hashtable是一种高级数据结构,实现了一个Key-Value映射的哈希表,用以快速检索数据.Hashtable不仅可以像Vector一样动态存储一系列的对象,而且对存储的每一个值对象(值)都安排与另一个键对象(关键字)相关联,非null对象都可以被使用作为键对象或者值对象.为了能够成功从hashtable中存储和读取对象元素,实现键对象的类必须实现hashCode方法

Java系列学习(十二)-开始Eclipse

1.用Eclipse来写一个HelloWorld (1)选择工作空间 工作空间其实就是我们写的源代码所在的目录 (2)创建一个Java项目 [File-New-Java Project] (3)创建包 [展开项目,在源包src下建立一个包com.kim] (4)创建类 [在com.kim包下建立一个类HelloWorld] (5)编写代码 (6)编译 当你点击保存的时候,自动回保存,在一下目录下,自动生成class文件 (7)运行 运行的三种方式 方式一,点击三角: 方式二,快捷键: 方式三,右