Java 事件处理模型 北京理工大学计算机学院 金旭亮
事件驱动 的软件系统 面向对象的软件系统许多是 事件驱动 的, 通常定义了一系列的事件, 并且事先规定好这些事件发生时应该如何响应 由于事件的发生通常是离散的, 所以 事件驱动 的软件系统并没有一个固定的执行流程 Java 的 GUI 应用程序也是 事件驱动 的 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 2
Java GUI 事件处理程序示例 手写的 Java 事件处理程序 :TheSimplestEvent.java 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 3
TheSimplestEvent.java 用户鼠标点击 JVM 调用 actionperformed(actionevent e) { } Java GUI 应用程序事件的编程模型被称之为 事件委托模型 (event delegation model) 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 4
事件处理的相关概念 事件对象 (Event object): 事件发生时所携带的信息 事件源 (Event Source): 能够产生事件的 GUI 组件对象, 如按钮 文本框等 事件监听器 (Event Listener): 当事件发生时, 事件监听器对象会得到通知, 它所事先准备好的事件处理方法被调用 这些事件处理方法的参数中, 将引用到相应的事件对象 三者之间的相互协作关系称为 事件委托模型 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 5
Java 事件委托模型的概貌 事件源 (Event Source) 注册 事件监听器 (Event Listener) 事件监听器接口 转发 封装相关信息 事件对象 (Event Object) EeventObject 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 6
事件信息 不同的事件伴随着不同的信息, 这些事件信息都保存在一个派生自 AWTEvent 的对象中 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 7
事件监听器接口 不同的事件携带不同的信息对应不同的事件监听器接口 事件监听器对象必须实现相应的事件监听器接口 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 8
Java 事件委托处理模型的使用方法 1. 写出一个实现事件监听器接口 (XXXListener) 接口的类 2. 创建此事件监听器类的一个对象, 如 myhandler 3. 对事件源组件 ( 比如按钮 ) 调用 addxxxlistener(myhandler) 方法 2014/11/17 9 金旭亮 Java 编程系列 (2014 版 )
示例项目 :TextFieldTest 回车! 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 10
事件监听器接口 ActionListener 从事件监听器基类接口 EventListener 派生而来 : public interface ActionListener extends EventListener ActionListener 接口只有一个方法 : public void actionperformed(actionevent e) 此方法封装事件响应代码 事件监听器基类接口 EventListener 没有任何成员, 这种接口被称为 标记接口 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 11
事件适配器
背景 有些事件接口定义了相当多的方法 : public interface WindowListener { void windowopened(windowevent e); void windowclosing(windowevent e); void windowclosed(windowevent e); void windowiconified(windowevent e); void windowdeiconified(windowevent e); void windowactivated(windowevent e); void windowdeactivated(windowevent e); } 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 13
背景 这会导致事件监听器类的代码很 无奈 : class Terminator implements WindowListener { public void windowclosing(windowevent e) { if (user agrees) System.exit(0); } public void windowopened(windowevent e) {} public void windowclosed(windowevent e) {} public void windowiconified(windowevent e) {} public void windowdeiconified(windowevent e) {} public void windowactivated(windowevent e) {} public void windowdeactivated(windowevent e) {} } 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 14
引入事件适配器 事件适配器 : 实现某一特定事件接口的所有方法, 只不过方法为空 定义适配器是为了简化事件处理的代码量 这时, 只需继承一个事件类, 覆盖其中感兴趣的事件代码就行了 JDK 提供了 WindowAdapter 这一类实现 WindowListener 接口, 从而大大简化了代码 2014/11/17 15 金旭亮 Java 编程系列 (2014 版 )
事件适配器类的实例 // 给窗口增加关闭功能 WindowListener w=new WindowAdapter() { public void windowclosing(windowevent e) { if (user agrees) System.exit(0); } }; frame.addwindowlistener(w); 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 16
更进一步简化 frame.addwindowlistener( new WindowAdapter() { public void windowclosing(windowevent e) { if (user agrees) System.exit(0); } }); 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 17
JDK 中的部分适配器 Event-adapter class in java.awt.event ComponentAdapter ContainerAdapter FocusAdapter KeyAdapter MouseAdapter MouseMotionAdapter WindowAdapter Implements interface ComponentListener ContainerListener FocusListener KeyListener MouseListener MouseMotionListener WindowListener 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 18
处理鼠标事件 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 19
相关接口 与鼠标相关的顶层接口有两个 :MouseListener 和 MouseMotionListener 其子接口 MouseInputListener 组合 了这两个接口的所有方法, 另有一个 MouseWheelListener 接口与鼠标中部滚轮滚动相关 MouseListener 接口 : public void mousepressed( MouseEvent event ) public void mouseclicked( MouseEvent event ) public void mousereleased( MouseEvent event ) public void mouseentered( MouseEvent event ) public void mouseexited( MouseEvent event ) MouseMotionListener 接口 : public void mousedragged( MouseEvent event ) public void mousemoved( MouseEvent event ) 方法参数 MouseEvent 封装了鼠标事件的相关信息 ( 比如 x 和 y 的坐标 ), 其基类 InputEvent 中定义了一些方法与常量, 可以用于确定鼠标的哪个按键被按下 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 20
捕获鼠标示例 参看示例项目 :MouseTracker 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 21
检测鼠标按键 主要通过 InputEvent 方法来完成这一工作 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 22
检测鼠标按键 参看示例项目 :MouseAdapterTest 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 23
使用鼠标绘图 参看示例项目 :Painter 要点 : 在 mousedragged 事件中记录下鼠标位置重写 JPanel 的 paintcomponent 方法, 在其中为记录下的每个鼠标坐标绘制一个小圆点 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 24
处理键盘事件
与按键相关的类型 最基本的是 KeyListener 接口 它定义了 keypressed, keyreleased 和 KeyTyped 方法 按键信息由 KeyEvent 对象负责承载 按键分为以下三类 : 1. Action Key: 如 F1 PageUP 之类 2. Modifier Key: 如 Ctrl Shift 3. 普通键 : 如 a A 1 [ 之类 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 26
按键示例 参看示例项目 : KeyDemo 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 27
活用事件
多重监听器 一般情况下, 事件源可以产生多种不同类型的事件, 因而可以注册 ( 触发 ) 多种不同类型的监听器 一个事件源组件上可以注册多个监听器, 针对同一个事件源的同一种事件也可以注册多个监听器 一个监听器可以被注册到多个不同的事件源上 TheSimplestExample2.java 2014/11/17 29 金旭亮 Java 编程系列 (2014 版 )
如何区分开是哪个按钮被点击? Demo: WhichIsClicked.java 默认情况下,JButton 的 ActionCommand 属性等于其 Text 属性值, 可以通过查看其基类 AbstractButton 的 getactioncommand 方法知道这点 : public String getactioncommand() { String ac = getmodel().getactioncommand(); if(ac == null) { ac = gettext(); } return ac; } 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 30
小结 : 事件处理方法的编写方式 我们可以有四种方式编写事件处理方法, 各有优缺点 : 1. 顶级类 : 创建一个独立的类, 实现特定的事件监听接口, 很少用 2. 让事件源组件自身实现特定的事件监听接口, 不推荐使用 3. 使用内部类, 推荐 ( 多个组件需要复用 ) 4. 使用匿名类, 推荐 ( 仅特定组件需要使用 ) 示例 : 1. Simple1.java--- 内部类 2. Simple2.java--- 内部匿名类 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 31
内部类实现事件监听器的示例 ButtonTest.java 使用内部类 ( 或匿名类 ) 的最大好处是事件处理程序可以很方便地访问外部类的私有成员 2014/11/17 32 金旭亮 Java 编程系列 (2014 版 )
实例分析 MulticastTest.java: 同时关闭多个窗体 要点 : 1. 新窗体的构造函数接收一个 JButton 对象 2. Close all 按钮监控多个对象 还有其他的实现方法吗? 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 33
处理文本框的 TextChange 事件 实例 :TextTest.java 2014/11/17 34 金旭亮 Java 编程系列 (2014 版 )
剖析时钟程序 JTextField 以 MVC 模式实现 当文本框的文字改变时, 相当于 Document 发生了改变, 将会调用 insertupdate,removeupdate 和 changedupdate 三个方法 这三个方法是 DocumentListener 接口的成员 需要理解 MVC 模式的含义 2014/11/17 35 金旭亮 Java 编程系列 (2014 版 )
Actions
引入 Action 的原因 在实际开发中, 经常需要让多个 UI 组件 ( 比如工具栏上的按钮或菜单上的命令 ) 实现同一个功能, 而且它们的状态需要同步, 比如某功能不可用时, 工具栏上的按钮或菜单上的命令必须同时灰掉 为了解决这个问题,Java 引入了 Action 接口, 此类派生自 ActionListener, 它扩充定义了以下的方法 : void actionperformed(actionevent event) void setenabled(boolean b) boolean isenabled() void putvalue(string key, Object value) Object getvalue(string key) void addpropertychangelistener(propertychangelistener listener) void removepropertychangelistener(propertychangelistener listener) 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 37
AbstractAction 由于 Action 接口定义了太多的方法, 为了方便开发,JDK 提供了一个 AbstractAction 类实现了这一接口, 我们可以直接继承自 AbstractAction, 重写需要的方法就行了 参看示例项目 :ActionTest 示例要点 : 1 给按钮加上了图标 2 支持快捷键 (Ctrl+B 等 ) 3 支持 ToolTips 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 38
处理菜单事件 实例 :ImageViewer.java 2014/11/17 39 金旭亮 Java 编程系列 (2014 版 )
对话框的设计与使用
模式与非模式对话框 模式对话框显示时, 必须关闭它之后才可以访问主窗体 JOptionPane 是最常用的显示模式对话框的组件, 拥有相当多的功能 : 参看示例项目 :OptionDialogTest 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 41
JOptionPane 的灵活性 JOptionPane 可以显示一个 Component, 或者是任何一个对象 ( 通常会调用它的 tostring 方法以得到一个字串 ) JOptionPane 显示之后有一个返回值, 应用程序可以通过它来知道用户的操作结果 : int selection = JOptionPane.showConfirmDialog( parent, "Message", "Title", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (selection == JOptionPane.OK_OPTION)... 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 42
开发可交换信息的对话框 参看 DataExchangeTest.java 注意信息是如何传递的 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 43
综合实例 参看示例 :ColorChooserTest.java 三种对话窗口与主窗口相互交换信息的方式 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 44
多文档界面 参看示例项目 :MultipleDocumentTest 2014/11/17 金旭亮 Java 编程系列 (2014 版 ) 45