Java事件处理

Published: by Creative Commons Licence (Last updated: )

Java事件处理

关于JAVA中事件分发和监听机制实现的代码实例
深入理解JAVA事件机制

  • 事件源
  • 事件
  • 事件监听器
  • 事件分发器:事件分发器主要处理事件的分发和事件监听器的管理,注册和删除事件监听器等。

AWT事件处理

《Java语言程序设计-基础篇(原书第8版)》

事件1.png

事件:
必须继承:EventObject类。可以使用事件的getSource()方法获得事件的源对象。ConponentEvent类还提供了一个getComponent()方法,用来返回触发事件的组件;这两个方法返回同一个事件源对象的引用。

ActionEvent:
getActionCommand(): String 获取这个动作相关的命令字符串;对于按钮来说就是它的显示文本。
getModifiers(): int 获取这个动作事件过程中按住的修改键。
getWhen(): long 获得该事件发生的时间。

事件2.png

Java使用一种基于委托的模型来处理事件:源对象触发一个事件,对此事件感兴趣的对象会处理它。

委派式事件处理方式:事件源将事件的处理工作委托给特定的对象(事件监听器);当事件源发生指定的事件时,就通所委托的事件监听器,由事件监听器来处理这个事件。

将对此感兴趣的对象称为监听器。一个对象要成为源对象上的事件监听器,需要具备两个条件:

  1. 监听器对象的类必须是相应事件监听器接口的实例,以确保监听器有处理这个事件的正确方法。
  2. 监听器对象必须由源对象注册。
代码示例:
private Button ok = new Button("确定");
//注册事件监听器  ActionListener
ok.addActionListener(new OkListener());

//定义事件监听器类
class OkListener implements ActionListener()
{
    //下面定义的方法就是事件处理器,用于响应特定的事件   ActionEvent
    public void actionPerformed(ActionEvent e)
    {
        System.out.println("用户单击了OK按钮");
    }
}

事件3.png

ActionListener等事件监听器接口只包含一个抽象方法,这种接口也就是前面介绍的函数式接口,因此可用Lambda表达式来创建监听器对象。

实现监听器接口的几种方式

《Java面向对象编程-第一版》

  • 用内部类实现监听接口。优点是可以直接访问外部类的成员变量和方法
  • 用容器类实现监听接口。容器内的组件将容器实例本身注册为监听器 button.addActionListener(this);
  • 定义专门的顶层类实现监听接口。优点是可以使处理事件的代码与创建GUI界面的代码分离,缺点是在监听器类中无法直接访问组件。在监听类的事件处理方法中不能直接访问事件源,而必须通过事件类的getSource()方法来获得事件源。
  • 采用事件适配器。如果实现一个监听器接口,必须实现接口中的所有方法,否则这个类必须声明为抽象类。而在实际应用中,往往不需要实现接口中的所有方法。为了编程方便,AWT为部分方法较多的接口提供了适配器类,这些类经管实现了监听接口的所有方法,但实际上方法体为空。可以定义一个继承适配器的类来作为监听器,在这个类中,只需要根据实际的需要来实现个别事件处理方法。
  • 一个组件注册多个监听器。当一个组件注册了多个监听器时,这些监听器到底按何种次序响应事件是不确定的,与它们被注册到组件中的次序没有关系。(比如一个button注册了5个ActionListener)

监听器接口适配器

并不是所有的监听接口都有适配器。

内部类

inner Class:是定义在另一个类范围内的类。

内部类有如下特征:

  • 一个内部类被编译成一个名为OuterClassName$InnerClassName.class
  • 内部类可以引用定义在它嵌套的外部类中的数据和方法
  • 使用可见性修饰符定义内部类时,遵从和应用与在类成员上一样的可见性规则
  • 可以将内部类定义为static。一个static内部类可以使用外部类的名字访问。一个static类是不能访问外部类的非静态成员的
  • 内部类的对象经常在外部类中创建。也可从另一个类中创建一个内部类的对象。
    • 如果该内部类是非静态的,就必须先创建一个外部类的实例,然后使用下面的语法创建一个内部类的对象: OuterClass.InnerClass innerObject = outerObject.new InnerClass();
    • 如果内部类是静态的,那么使用下面的语法为他创建一个对象: OuterClass.InnerClass innerObject = new OuterClass.InnerClass();

对于内部类另可参考:《Java面向对象编程-第一版》 第12章:内部类

匿名类监听器

可以使用匿名内部类简化内部类监听器。

匿名类是具有如下特征的内部类:

  • 匿名内部类必须总是扩展父类或实现接口,但是它不能有显式的extends或implements子句
  • 匿名内部类必须实现父类或接口中所有的抽象方法
  • 匿名内部类总是使用它的父类的无参数构造方法来创建实例。

Lambda表达式

ActionListener等事件监听器接口只包含一个抽象方法,这种接口也就是前面介绍的函数式接口,因此可用Lambda表达式来创建监听器对象。

JavaBeans和bean事件

《Java语言程序设计-进阶篇(原书第8版)》 第32章:JavaBeans和bean事件

本章中将学习如何创建自定义事件以及开发自己的可触发事件的源组件。每个Java用户界面类都是一个JavaBeans组件。

JavaBeans

JavaBeans是一种软件组件体系结构,在eclipse等开发工具中,它能够在设计中可视化地操作结构良好的对象,从而极大的扩展了Java语言的能力。这些结构良好的对象被称作为JavaBeans,简称beans

符合JavaBeans组件模型的类满足下面的要求:

  1. bean必须是公共类
  2. 必须有一个公共的默认构造器
  3. bean必须实现接口java.io.Serializable
  4. bean的属性通常带有构造恰当的公共访问器方法(get方法)和修改器方法(set方法),这样开发工具可以可视化的看到并更新这个属性。
  5. bean的事件可能带有构造恰当的、公共的注册方法和注销方法,使用这些方法可以添加或删除监听器。如果bean是事件源,它必须提供注册监听器的注册方法。

前三个条件必须满足,它们是JavaBeans组件的最低要求

每个图形用户界面(GUI)类都是一个JavaBeans组件,因为:

  1. 它是一个公共类
  2. 它有一个公共的无参构造方法
  3. 它是对java.awt.Component的扩展,而且实现了java.io.Serializable

bean属性

bean属性通常是bean的数据域。因为存在私有数据域;访问器方法和修改器方法用来保证用户能够读写该属性。

属性描述bean的状态,数据域用于存储属性。然而bean属性未必是一个数据域。 例如:

//创建一个MessageLength属性,用来表示message(数据域)中字符的个数
public int getMessageLength(){
    return message.length();
}

一个属性可以有多个get和set方法,但是其中必定有一个get或set方法的签名与该属性的命名方式一致。

属性可以是只读或只写的,即只有get或set方法。

属性和数据域

Java事件模型回顾

事件类和事件监听器接口

每一个事件类与一个事件监听器接口关联,这种接口定义一个或多个称为处理器(handler)的方法。 事件类与它的监听器接口是相互依存的,所以经常称其为事件组(event set)或事件对(event pair)。

源组件

产生事件的组件称为事件源(Event source)。 源组件包含用于检测引发事件外部或内部动作的代码。当检测到动作时,源组件调用监听器所定义的事件处理器向监听器触发事件。源组件也包含注册和注销监听器的方法。

监听器组件

一个监听器组件可以实现任意多个监听器接口,以便监听多种类型的事件。一个源组件可以注册多个监听器,也可将其本身注册为监听器。

创建自定义源组件

源组件必须有适当的注册与注销方法,用来添加和删除监听器。事件可以单点传送(事件只能通知一个监听器对象),也可以多点传送(事件通知监听器列表中的每个对象)。

1. 注册方法: 添加一个单点传送 监听器的命名方式:

public void add<Event>Listener(<Event>Listener l)
    throws TooManyListenersException;

添加一个多点传送 监听器的命名方式:

public void add<Event>Listener(<Event>Listener l)

2.注销方法: 删除一个监听器的命名方式:

public void remove<Event>Listener(<Event>Listener l)

源组件包含特定的代码,可以创建事件对象,以及通过传递这个事件对象去调用监听器的处理器。可以调用标准Java事件来创建事件对象,必要时也可以定义自己的事件类。

源组件需负责注册注销监听器、创建事件、调用在监听器接口中定义的方法(事件处理器)通知监听器

如果通过扩展可产生事件的组件来创建新的源组件,那么新组件就继承了产生相同类型事件的能力。

创建自定义事件组

事件组/事件对: 事件类 + 对应的监听器

自定义事件类必须扩展java.util.EventObject类或者它的一个子类。此外,它还应该提供创建事件的构造方法以及描述事件的数据成员和方法。

由于创建一个事件时必须指定其源对象,因此,事件类没有默认构造方法。

自定义事件监听器接口必须扩展java.util.EventListener接口或者java.util.EventListener接口的一个子接口,并且定义事件处理器的签名。按照惯例,与名为XEvent的事件类所对应的监听器接口应该命名为XListener。

完整代码:见 EventTest

回调

委托模式

观察者模式