【Java】事件

一、介绍

1)概念

在刚刚,我们已经将游戏的界面搭建出来了,图片也都添加进去了,接下来我们要做的事情,就是让图片去移动起来,想要让它移动,就要来学习 事件

事件:可以被组件识别的操作。

说白了就是:当你对组件干了某件事情之后,就会执行对应的代码。

例如在登录页面中,当 登录按钮点击后,就会校验 用户名和密码 这段逻辑,这就是事件。

image-20240420112841130

2)三个核心要素

  • 事件源(表示你要对哪些组件进行操作): 按钮 图片 窗体…

    例如刚刚的登录案例中,被点击的 登录按钮 就是 事件源

  • 事件:对 事件源 进行的操作叫做 事件

    例如:鼠标单机、鼠标划入、键盘输入…

  • 绑定监听:当事件源上发生了某个事件,就会执行某段代码

    例如刚刚的登录案例中,当事件源(登录按钮)上发生了点击事件后,就会执行 校验用户名和密码 的这段代码


3)常见的三种事件监听

  • 键盘监听 KeyListener

    键盘监听:对组件用键盘进行了操作。例如很多软件的快捷键就是这么干的

  • 鼠标监听 MouseListener

    凡是能用鼠标操作的,都是 鼠标监听

  • 动作监听 ActionListener

    动作监听比较特殊,你可以把它理解为 键盘监听鼠标监听 的精简版。

    键盘监听 几乎可以监听键盘中所有的按键。鼠标监听 可以监听 鼠标按下不松...,并且可以将单机分成很多步。

    但是动作监听就没有这么麻烦了,它是一个精简版。

    动作监听在监听鼠标的时候,它只能监听 点击;在监听键盘的时候,它只能监听空格。


4)事件的三种实现方式

  • 定义实现类实现接口
  • 匿名内部类
  • 本类实现接口

二、动作监听

1)代码实现

我提前在 Test3 中写了一段代码,在代码中创建了一个窗体,并给界面设置了宽高、标题、界面置顶、居中、关闭模式、取消默认的居中放置、让整个界面显示出来。

package com.itheima.test;

import javax.swing.*;

public class Test3 {
    public static void main(String[] args) {
        JFrame jFrame = new JFrame();
        //设置界面的宽高
        jFrame.setSize(603, 680);
        //设置界面的标题
        jFrame.setTitle("事件演示");
        //设置界面置顶
        jFrame.setAlwaysOnTop(true);
        //设置界面居中
        jFrame.setLocationRelativeTo(null);
        //设置关闭模式
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
        jFrame.setLayout(null);

        jFrame.setVisible(true);
    }
}

在中间,我们需要创建一个按钮对象,因为我们需要来对按钮添加事件。

按钮的类叫做 JButton(Java Button),给这个 JButton 取个名字为:jtb,在按钮中显示文本:"点我啊"

//创建一个按钮对象
JButton jtb = new JButton("点我啊");

再往下,去给这个按钮设置位置宽高,通过 jtb 去调用 setBounds() 方法,它同样也有四个参数:x、y 代表位置,width、height 代表宽高,它们的单位都是 像素

//设置位置和宽高
jtb.setBounds(0,0,100,50);

再往下,给 jtb 添加一个动作监听,调用方法 jtb.addActionListener()

这段代码我们可以把它拆解一下。

jtb:组件对象,表示你要给哪个组件添加事件。

addActionListener()add 可以理解成 添加,后面的 ActionListener 就是需要监听的事件。

合起来就是:我要给组件添加哪个事件监听。我现在写的是 ActionListener ,表示 动作监听动作监听 包含两部分:① 鼠标左键点击;② 空格。除此之外,其他的都监听不了。

jtb.addActionListener();

事件添加后,是不是要去执行一段代码,那这段代码在哪呢?

这段代码就是由括号中的参数去决定的。

选中方法,ctrl + b 跟进。

image-20240420115022375

可以看见它的形参是 ActionListener 类型的。

选中它,继续跟进。

image-20240420115113671

可以看见 ActionListener 是一个接口,所以我们在调用方法的时候,需要传入 ActionListener 接口的实现类对象。

最简单的方式就是写一个 ActionListener 的实现类

image-20240420115326180

然后用刚刚新建的类去实现 ActionListener接口。实现一个接口,需要重写里面的抽象方法。

image-20240420115459225

package com.itheima.test;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MyActionListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("按钮被点击了");
    }
}

此时在 jtb.addActionListener() 方法中就可以传入 ActionListener 的实现类对象。

这句话整体解释就是:现在我们需要给 jtb 这个按钮去添加动作监听事件,动作监听中包含了:鼠标左键单机、空格。

我们以 鼠标左键单机 为例,当用鼠标左键单机了 jtb 这个按钮之后,就会执行 MyActionListener 这个类中的代码。

因此这里的参数就表示:事件被触发之后要执行的代码。

由于里面的代码逻辑就是打印:"按钮被点击了",因此当我们点击按钮后,这个输出语句就会出现。

jtb.addActionListener(new MyActionListener());

最后,将按钮添加到整个界面中即可。

要注意的时,这个 jtb 也应该被添加到隐藏容器中。

//把按钮添加到界面当中
jFrame.getContentPane().add(jtb);

右键运行来看下效果,可以发现鼠标每点击一次,都会打印出 "按钮被点击了" 这句话,同样按空格也会打印出这句话。

5qoyy-l191m

2)优化1:采用 匿名内部类 简化代码

在实际开发中,每一个按钮的业务逻辑都是不一样的,如果每一个按钮都要去新创建一个类,太麻烦了!

之前我们讲过,当一个接口的实现类只被用到一次时,这个类我们就没有必要再去单独定义一个类了,而是用 匿名内部类 的形式去简化代码。

这样写其实跟刚刚是一样的,我要给 jtb 这个按钮去添加 动作监听,当这个监听被触发了之后,就会执行 actionPerformed() 方法中的代码。

之前讲过,整个这段代码应该叫做 匿名内部类的对象,真正没有名字的类是后的的大括号,而这个大括号实现了前面 ActionListener接口,因此需要在该类中重写接口中所有的抽象方法。前面的 nwe 关键字 new 的并不是这里的接口,而是后面大括号这个没有名字的类。

jtb.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("达咩~不要点我哟~");
    }
});

右键运行,用鼠标左键点它,或者按下空格,跟刚刚的效果都是一模一样的。

z0n22-s3tu5

这个是我们在实际开发中其中的一种写法,除此之外,我们在实际开发当中还会有另外一种写法。


3)优化2:直接使用本类作为实现类

我提前写好了 MyJFrame类 放到 Test包 下。

MyJFrame:我的界面,用这个界面去 extends JFrame

在这个界面中,首先我对这个界面做了一个设置,然后创建了两个按钮。

package com.itheima.test;

public class MyJFrame extends JFrame {
    public MyJFrame(){
        //设置界面的宽高
        this.setSize(603, 680);
        //设置界面的标题
        this.setTitle("拼图单机版 v1.0");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //设置关闭模式
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
        this.setLayout(null);

        //创建一个按钮对象
        JButton jtb1 = new JButton("点我啊");
        //给按钮设置位置和宽高
        jtb1.setBounds(0,0,100,50);


        //创建一个按钮对象
        JButton jtb2 = new JButton("再点我啊");
        //给按钮设置位置和宽高
        jtb2.setBounds(100,0,100,50);


        //让整个界面显示出来
        this.setVisible(true);
    }
}

现在我就要在这个界面中,给这两个按钮添加事件。

在添加点击事件的时候,我们需要在括号中传入 ActionListener 实现类的对象。

jtb1.addActionListener();

现在我们有第三种写法:让 MyJFrame类 实现 ActionListener,然后再去重写里面所有的抽象方法。

image-20240420133725918

此时在小括号中就不需要去 new 了,直接写 this 即可。

下面的 jtb2 也是一样的,给 jtb2 也去添加一个 ActionListener,后面的 this 表示的就是本类的对象。

如下代码,我给两个按钮都添加了 动作监听,当 动作监听 这个事件被触发之后,就会执行本类里面刚刚重写的 actionPerformed()方法 中对应的代码

//创建一个按钮对象
JButton jtb1 = new JButton("点我啊");
//给按钮设置位置和宽高
jtb1.setBounds(0,0,100,50);
// 给按钮添加事件
jtb1.addActionListener(this);


//创建一个按钮对象
JButton jtb2 = new JButton("再点我啊");
//给按钮设置位置和宽高
jtb2.setBounds(100,0,100,50);
jtb2.addActionListener(this);

在重写 actionPerformed() 方法中,就可以对按钮进行判断。

e.getSource():可以获取当前被操作的按钮对象,接收的时候是使用 Object(父类类型) 接收的。

@Override
public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
}

再往下,就可以使用 source变量 跟上面的按钮做一个 == 比较。

@Override
public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
    if (source == jtb1) {
        // 如果点击了按钮1,它的大小就会变成 200,200
        jtb1.setSize(200, 200);
    } else if (source == jtb2) {
        // 如果点击了按钮2,它的位置就会随机改变
        Random r = new Random();
        jtb2.setLocation(r.nextInt(500), r.nextInt(500));
    }
}

由于要在多个方法中用到 jtb1、jtb2按钮,因此将它们放到成员位置。

public class MyJFrame extends JFrame implements ActionListener {
    //创建一个按钮对象
    JButton jtb1 = new JButton("点我啊");
    //创建一个按钮对象
    JButton jtb2 = new JButton("再点我啊");
    public MyJFrame(){
        // 构造方法内容
    }
}

最后,将两个按钮都添加到页面中即可。

// 将按钮添加到整个界面中
this.getContentPane().add(jtb1);
this.getContentPane().add(jtb2);

此时 MyJFrame类 就已经写好了,如果想要将这个界面启动起来,就要再定义一个测试类 Test4

image-20240420135550730

在测试类 new 这个界面就行了。

package com.itheima.test;

public class Test4 {
    public static void main(String[] args) {
        new MyJFrame();
    }
}

这样就可以启动我自己写的这个界面。

启动之后就会调用 MyJFrame类 的空参构造。

效果如下图,点击第二个按钮位置是随机的,点击第一个按钮可以发现它变大了。

rng6f-fdxnn

三、鼠标监听机制 —— MouseListener

1)介绍

我们刚刚学习了最简单的 ActionListener(动作监听) ,但是动作监听它是有弊端的,它只能识别点击, 但是当我用鼠标点击一个按钮后,其实这个点击我可以把它拆解成很多,例如下面四个动作。

将鼠标放到按钮上,还没有点击,这个就叫做 划入动作

用鼠标点击按钮,但是不松开,这个就叫做 按下动作

松开,这个就叫做 松开动作

从按钮上划出,这个就叫做 划出动作

但如果想要让这个按钮有一些复杂的功能,针对于 划入、按下、松开、划出 都要执行不同的操作,此时就只能使用 MouseListener

MouseListener 中除了这四个动作,它把 按下跟松开 归为了一组,叫做 单击动作

image-20240420143427566

如果以后我想要监听按钮的单击事件,就一共有三种写法

  1. 动作监听(ActionListenner
  2. 鼠标监听(MouseListener)中的单击事件
  3. 鼠标监听(MouseListener)中的松开事件

2)代码实现

在这里我新建好了一个类 MyJFrame2,用它 extends JFrame,在构造方法中对整体的界面做了一些设置(宽高、标题、置顶、界面居中、关闭模式、取消默认的图片居中方式),然后在成员位置定义了一个按钮,然后在构造方法中给按钮设置了位置跟宽高,然后把按钮添加到整个界面中。

package com.itheima.test;

import javax.swing.*;

public class MyJFrame2 extends JFrame {

    //创建一个按钮对象
    JButton jtb1 = new JButton("点我啊");

    public MyJFrame2(){
        //设置界面的宽高
        this.setSize(603, 680);
        //设置界面的标题
        this.setTitle("拼图单机版 v1.0");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //设置关闭模式
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
        this.setLayout(null);

        //给按钮设置位置和宽高
        jtb1.setBounds(0,0,100,50);

        //那按钮添加到整个界面当中
        this.getContentPane().add(jtb1);

        //让整个界面显示出来
        this.setVisible(true);
    }
}

需求:对按钮来绑定鼠标事件

//给按钮绑定鼠标事件
jtb1.addMouseListener();

ctrl + b 跟进方法,查看形参,可以看见是 MouseListener

image-20240420145922325

继续跟进,可以看见 MouseListener 也是一个接口。

image-20240420150158703

因此在小括号中,我们要传入的就是 MouseListener接口 的实现类对象。

同样,我们可以让本类去 implements MouseListener ,然后重写里面所有的方法,一共有五个。

image-20240420150832030

可以看见所有的动作都在这里,因此 MouseListener 可以把单击拆解的很细。

mouseClicked:单击(按下并释放)。

mouseEntered:划入(表示鼠标进入组件)。

mouseExited:划出(退出组件)。

mousePressed:按下不松。

mouseReleased:松开。

接着,我们可以把每一个重写方法去写一个输出语句。

@Override
public void mouseClicked(MouseEvent e) {
    System.out.println("单击");
}

@Override
public void mousePressed(MouseEvent e) {
    System.out.println("按下不松");
}

@Override
public void mouseReleased(MouseEvent e) {
    System.out.println("松开");
}

@Override
public void mouseEntered(MouseEvent e) {
    System.out.println("划入");
}

@Override
public void mouseExited(MouseEvent e) {
    System.out.println("划出");
}

最后再在绑定事件中传入 this 即可,表示是给 jtb1 这个按钮绑定鼠标事件,当这个事件被触发之后,就会执行 this,也就是本类里面的这些重写的代码。

//给按钮绑定鼠标事件
jtb1.addMouseListener();

最后回到 Test4类 中,创建 MyJFrame2 对象,来启动我们刚刚写的界面。

public class Test4 {
    public static void main(String[] args) {
        new MyJFrame2();
    }
}

程序启动后,注意看控制台,并且在松开的时候它还会触发单击事件。

0w3ji-b4rh3

3)总结

在刚刚我们已经学习完了鼠标监听:MouseListener,在 MouseListener 中它有5个方法。

mouseClicked:单击(按下并释放)。

mouseEntered:划入(表示鼠标进入组件)。

mouseExited:划出(退出组件)。

mousePressed:按下不松。

mouseReleased:松开。

image-20240420153104268

四、键盘监听机制 —— KeyListener

1)介绍

KeyListener 中一共有三个方法。

keyPressed(KeyEvent e):表示我们在键盘上按下某个键的时候会触发这个事件(按下不松)。

keyReleased(KeyEvent e):松开按键时触发。

keyTyped(KeyEvent e)

第三个方法我们一般是不会用的,了解一下即可,因为第三个方法有局限性,在键盘中有一些按键它是监听不了的,例如 ctrl、alt 等,因此我们只需要使用上面两个键就行了。

image-20240420154025607

2)代码实现

在这里,我又新建了一个 MyJFrame3类(我的第三个界面),然后依旧去给这个界面做了一个设置,因此我们会在这个界面中去实现键盘监听。

package com.itheima.test;

import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class MyJFrame3 extends JFrame {

    public MyJFrame3(){
        //设置界面的宽高
        this.setSize(603, 680);
        //设置界面的标题
        this.setTitle("拼图单机版 v1.0");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //设置关闭模式
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
        this.setLayout(null);
        
        //让整个界面显示出来
        this.setVisible(true);
    }
}

大家在平时操作一些软件的时候会发现,很多软件都会有快捷键,一般来讲这个快捷键就是用键盘监听去实现的。


我们一般是给整个窗体去添加键盘监听,因此这里是通过 this 调用 addKeyListener,然后在括号里面再去写一个实现类的对象。

// 给整个窗体添加键盘监听
this.addKeyListener();

选中 addKeyListener() 方法,ctrl + b 跟进,可以发现形参的类型是 KeyListener

image-20240420154707767

继续 ctrl + b 跟进,可以发现 KeyListener 跟之前的都一样,同样也是一个接口。

image-20240420154812927

因此在括号里面,我们可以传递 KeyListener 的实现类对象。

此时我们就可以让本界面去 implements KeyListener,然后去重写该接口中所有的抽象方法。

默认全选,点击ok即可。

image-20240420155254109

在下面的小括号中,我们直接传入 this 就行了。

这里的第一个 this 表示的是调用者,即本类对象,本类对象其实也就是当前界面对象,表示我要给整个界面添加监听。

那么你要添加哪个监听呢?是由谁说了算的?是由 addKeyListener() 方法说了算的:表示我要给本界面添加键盘监听。

// 给整个窗体添加键盘监听
this.addKeyListener(this);

参数 this 表示:当事件被触发之后,会执行本类中对应代码,即我们重写的代码。

@Override
public void keyTyped(KeyEvent e) {
    // 这个方法我们不用管
}

@Override
public void keyPressed(KeyEvent e) {
    System.out.println("按下不松");
}

@Override
public void keyReleased(KeyEvent e) {
    System.out.println("松开按键");
}

然后回到 Test4测试类 中,创建 MyJFrame3对象,效果如下图:当我按下按键时,两个方法都会触发。

当我一直按住按键不松开,就会一直重复调用 keyPressed() 方法。

PS:在输入的时候建议使用英文状态下的输入,并且在键盘输入时,一定要点击Java程序的运行界面,因为我们绑定事件是绑定在这个界面上的。

k5wue-68vkw

3)区分按键

由于 按下不松 的方法会反复调用,因此我们将 区分按键 的代码写在 松开按键 的方法中。

keyReleased() 方法中,有一个参数,表示 KeyEvent,就是你那个动作对象。

但是呢这个动作对象不重要,重要的是可以用它调用方法,其中就有一个 getKeyCode() 的方法,这个方法就可以获取键盘上每一个按键的编号,并且这个方法的返回值就是 int类型 的。

@Override
public void keyReleased(KeyEvent e) {
    System.out.println("松开按键");
    // 获取键盘上每一个按键的编号
    int code = e.getKeyCode();
    System.out.println(code);
}

该写完代码后,回到 Test4类 中程序运行,点一下游戏界面,分别按 abcd 便会打印出如下图的数字。

b15ly-ajppu

这个打印出的数字跟 ASCII码表 没有什么太大关系,它只不过刚好小写字母的编号ASCII码表上大小的字母 对应起来罢了。

由此我们就能发现,每一个按键,都有一个编号与之对应,此时我们就能在方法中进行 if判断

@Override
public void keyReleased(KeyEvent e) {
    System.out.println("松开按键");
    //获取键盘上每一个按键的编号
    int code = e.getKeyCode();
    if(code == 65){
        System.out.println("现在按的是A");
    }else if(code == 66){
        System.out.println("现在按的是B");
    }
}

该写完代码后,回到 Test4类 中再来点击程序运行,点一下游戏界面,分别按 ab 便会打印出如下图的结果。

当我们按除 a、b 以外的键时其实也会触发 keyReleased() 方法,只不过我们对按键的编号做了一些判断,只有 a、b 的时候才有代码输出。

65k63-3u23w
  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值