Java Swing窗口自定义光标更新失效问题求助
问题分析与解决
核心问题:操作了错误的Window实例
你在MainMenu的keyPressed方法里新建了一个Window对象并调用updateCursor,但这个新创建的Window从未被显示(没调用open()),你实际要修改的是最初打开的那个Window实例的光标,而不是新实例的。
其他次要问题
- JFrame默认使用BorderLayout,连续添加
MainMenu和Game到窗口,后者会覆盖前者,导致MainMenu无法接收任何事件。 - MainMenu实现了KeyListener但未注册到任何组件,且JPanel默认不可聚焦,即使没被覆盖也接收不到键盘事件。
- 窗口初始化顺序错误,
setVisible(true)应该放在组件添加和配置完成后,避免组件未初始化就显示。
修正后的代码
Window.java
import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.io.File; import java.io.IOException; public class Window { public static final int WIDTH = 817, HEIGHT = 640; public static final int NORMAL = 0, ACTIVE = 1, INACTIVE = 2; JFrame frame = new JFrame(""); Cursor cursor_normal, cursor_active, cursor_inactive; public void open() { // 先创建MainMenu并传递当前Window引用 MainMenu mainMenu = new MainMenu(this); Game game = new Game(); // 使用CardLayout实现菜单和游戏界面切换,避免组件覆盖 CardLayout cardLayout = new CardLayout(); JPanel contentPane = new JPanel(cardLayout); contentPane.add(mainMenu, "menu"); contentPane.add(game, "game"); frame.setContentPane(contentPane); frame.setResizable(false); frame.setSize(WIDTH, HEIGHT); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.addKeyListener(new InputManager()); frame.addMouseListener(new InputManager()); loadCursors(); updateCursor(NORMAL); // 最后设置窗口可见 frame.setVisible(true); } public void loadCursors() { try { cursor_normal = Toolkit.getDefaultToolkit().createCustomCursor( ImageIO.read(new File(new SpritesManager().cursor_normal)), new Point(0, 0), "custom cursor (normal)"); cursor_active = Toolkit.getDefaultToolkit().createCustomCursor( ImageIO.read(new File(new SpritesManager().cursor_active)), new Point(0, 0), "custom cursor (active)"); cursor_inactive = Toolkit.getDefaultToolkit().createCustomCursor( ImageIO.read(new File(new SpritesManager().cursor_inactive)), new Point(0, 0), "custom cursor (inactive)"); } catch (IOException e) { throw new RuntimeException(e); } } public void updateCursor(int cursorType) { switch (cursorType) { case NORMAL -> frame.setCursor(cursor_normal); case ACTIVE -> frame.setCursor(cursor_active); case INACTIVE -> frame.setCursor(cursor_inactive); } // 如果需要让面板的光标同步,可以同时设置contentPane的光标 // frame.getContentPane().setCursor(frame.getCursor()); } }
MainMenu.java
import javax.swing.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; public class MainMenu extends JPanel implements KeyListener { private final Window parentWindow; // 通过构造函数接收原Window实例的引用 public MainMenu(Window window) { this.parentWindow = window; // 注册KeyListener到自身 this.addKeyListener(this); // 设置面板可聚焦,才能接收键盘事件 this.setFocusable(true); this.requestFocusInWindow(); } @Override public void keyTyped(KeyEvent e) {} @Override public void keyPressed(KeyEvent e) { // 操作原Window实例的光标 parentWindow.updateCursor(Window.ACTIVE); } @Override public void keyReleased(KeyEvent e) {} }
额外说明
- 使用CardLayout来管理菜单和游戏界面的切换,避免组件互相覆盖。
- 让MainMenu通过构造函数持有原Window的引用,这样调用的是实际显示窗口的updateCursor方法。
- 给MainMenu设置可聚焦并注册KeyListener,确保能接收键盘事件。
- 调整窗口初始化顺序,将
setVisible(true)放在最后,保证所有组件都配置完成后再显示。
内容的提问来源于stack exchange,提问作者Adamm
相关产品推荐
相关产品推荐

