添加键盘监听事件为什么没有被执行

package MyGame;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.*;
public class planewars extends JFrame{
	
	//管理所有的组件/方法
	public void init() {
		//注册(调用)
		createwindow();
		control();
	}
	
	//创建白窗口,用到JFrame类
	public void createwindow() {
		//禁止窗口大小调整,因为背景图很小,窗口变大之后背景不会变
		setResizable(false);
		
		//关闭窗口的同时停止程序运行
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		
		//设置窗口在屏幕上显示的位置和宽高
		setBounds(500,60,512,768);
		/*
		屏幕左上角是坐标轴的原点,向右是x轴,向下是y轴
		向右500px,向下60px,512px是屏幕宽度,768px是屏幕高度
		*/
		
		//java窗口默认情况下不显示,我们得设置为显示
		setVisible(true);//不写这行时,默认值是false
		
		//在窗口工具栏上面加上游戏的名字
		setTitle("飞机大战");
		
		//不断的自动重绘窗口
		while(true) {
			repaint();
			//让重绘的速度变慢
			try {
				Thread.sleep(80);//让当前死循环每隔50毫秒停顿一下
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
	//启动入口
	public static void main(String args[]) {
		//启动程序
		planewars pw = new planewars();
		pw.init();
	}
	
	//paint方法会被自动调用
	//画图的笔               //重绘repaint()
	public void paint(Graphics g) {//理解为粉笔
//		g.setColor(Color.red);//选红色的粉笔
//		g.drawLine(20, 100, 150, 200);//x1,y1的坐标为(20,100),x2,y2的坐标为(150,200)

		
//封装
//		//把硬盘上的背景图片加载进程序,,让java能看懂
//		Image bg = new ImageIcon("img/背景.jpg").getImage();
//		g.drawImage(bg, 0, 0, 100, 768, 0, 0, 512, 768, null);
//		//前面的0,0,512,768是窗口左上角和右下角的坐标,
//		//后面的0,0,512,768是图片左上角和右下角的坐标
//		/*在代码中传入 null 作为 observer 参数,意味着你不关心图像加载的状态,
//		 * 也不需要接收图像加载完成或者出现错误等相关的通知。在很多情况下,
//		 * 如果图像已经完全加载好,或者你不打算处理图像加载过程中的状态变化,
//		 * 就可以传入 null。
//		 */
		
		//调用加入背景图
		addBackground(g);
		addOurplane(g);
	}
	
	//背景图片向下移动的速度,每次移动10px
	int bg_y = 0;
	
	//加入背景图
	public void addBackground(Graphics g) {
		//把硬盘上的背景图片加载进程序,,让java能看懂
		Image bg = new ImageIcon("img/背景图片.jpg").getImage();
		
		//窗口中的背景图片(因为要实现滚动起来的效果还需要一张在窗口外的图片)
		g.drawImage(bg, 0, 0+bg_y, 512, 768+bg_y, 0, 0, 512, 768, null);
		//前面的0,0,512,768是窗口左上角坐标和宽度高度,
		//后面的0,0,512,768是图片左上角坐标和宽度高度
		/*在代码中传入 null 作为 observer 参数,意味着你不关心图像加载的状态,
		* 也不需要接收图像加载完成或者出现错误等相关的通知。在很多情况下,
		* 如果图像已经完全加载好,或者你不打算处理图像加载过程中的状态变化,
		* 就可以传入 null。
		*/
		
		//窗口上面看不到的背景图片
		g.drawImage(bg, 0, -768+bg_y, 512, 0+bg_y, 0, 0, 512, 768, null);
		bg_y += 5;
		
		if (bg_y>=768)
		{
			bg_y = 0;
		}
		
	}
	
	//加入我方飞机
	Plane ourplane = new Plane();
	public void addOurplane(Graphics g) {
		ourplane.drawPlane(g);
	}
	
	//键盘控制飞机
	public void control() {
		//添加键盘监听事件
		addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				System.out.println("键盘按下了");
			}
		});
	}
	
	
}

以上代码在按下键盘后并没有出现 键盘按下了 

Java 的事件处理基于委托事件模型(Delegation Event Model)。模型中有三个关键部分:事件源(Event Source)、事件监听器(Event Listener)和事件对象(Event Object)

  • 事件源:产生事件的对象。此代码中,planewars 类继承自 JFrameJFrame 作为事件源。addKeyListener 方法就是在 JFrame(即事件源)上添加键盘事件监听器。
  • 事件监听器:实现了特定监听接口的类的实例。KeyAdapter 是一个抽象类,它实现了 KeyListener 接口。KeyListener 接口定义了三个方法:keyPressed(按键按下)、keyReleased(按键释放)、keyTyped(输入字符)。在你的代码中,通过匿名内部类的方式创建了一个 KeyAdapter 的实例,并重写了 keyPressed 方法,当有按键按下时会执行重写后的 keyPressed 方法。
  • 事件对象:封装了事件相关信息的对象。KeyEvent 类是一个事件对象,它包含了与键盘事件相关的信息,例如按下的键的代码(getKeyCode)、按下的字符(getKeyChar)等。keyPressed 方法接收一个 KeyEvent 类型的参数 e,通过这个参数可以获取到具体的键盘事件信息。

 具体执行过程

  • 当调用 addKeyListener 方法时,会将创建的 KeyAdapter 实例注册到 JFrame(事件源)上。
  • 当用户按下键盘上的键时,系统会创建一个 KeyEvent 对象,并将其传递给注册在 JFrame 上的 keyPressed 方法。
  • keyPressed 方法被调用后,执行其中的代码,此代码中就是 System.out.println("键盘按下了");,从而在控制台输出相应的信息。

事件分发线程阻塞问题

在 Java Swing 中,EDT 负责处理所有与 GUI 相关的操作,例如界面绘制、事件处理等。当你在代码里使用 while (true) 循环时,这个循环会持续占用 EDT,使得 EDT 无法处理其他任务

在 createwindow 方法里,存在一个 while (true) 循环,该循环会持续调用 repaint 方法,并且使用 Thread.sleep(80) 来控制重绘的速度。不过,这个循环会阻塞事件分发线程(Event Dispatch Thread, EDT),导致键盘事件无法被正常处理。

解决办法

运用 javax.swing.Timer 来替代 while (true) 循环,以此实现定时重绘,这样就能保证事件分发线程可以正常处理键盘事件。

将以下代码:

//不断的自动重绘窗口
		while(true) {
			repaint();
			//让重绘的速度变慢
			try {
				Thread.sleep(80);//让当前死循环每隔50毫秒停顿一下
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

改为:

//不断的自动重绘窗口
		Timer timer = new Timer(80, e -> repaint());
		timer.start();

javax.swing.Timer 是一个轻量级的工具,用于在指定的时间间隔后触发事件。它会在后台线程中运行,不会阻塞 EDT。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值