You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Swing窗口自定义光标更新失效问题求助

问题分析与解决

核心问题:操作了错误的Window实例

你在MainMenu的keyPressed方法里新建了一个Window对象并调用updateCursor,但这个新创建的Window从未被显示(没调用open()),你实际要修改的是最初打开的那个Window实例的光标,而不是新实例的。

其他次要问题

  1. JFrame默认使用BorderLayout,连续添加MainMenu和Game到窗口,后者会覆盖前者,导致MainMenu无法接收任何事件。
  2. MainMenu实现了KeyListener但未注册到任何组件,且JPanel默认不可聚焦,即使没被覆盖也接收不到键盘事件。
  3. 窗口初始化顺序错误,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());
    }
}
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) {}
}

额外说明

  1. 使用CardLayout来管理菜单和游戏界面的切换,避免组件互相覆盖。
  2. 让MainMenu通过构造函数持有原Window的引用,这样调用的是实际显示窗口的updateCursor方法。
  3. 给MainMenu设置可聚焦并注册KeyListener,确保能接收键盘事件。
  4. 调整窗口初始化顺序,将setVisible(true)放在最后,保证所有组件都配置完成后再显示。

内容的提问来源于stack exchange,提问作者Adamm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:05:19