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

JButtons上的MouseInputListener失效问题求助

问题描述

我有两个作为JPanel子类内部类的JButton,已经在这些按钮上实现了MouseInputListener,但监听器无法正常工作。我也尝试过把MouseInputListener放在外部类上,同样无效。我还有一个继承JFrame且实现MouseInputListener的类,怀疑是组件层级或覆盖问题,但无法确定。

相关代码

Frame类

public class Frame extends JFrame implements MouseInputListener{
    static Dimension windowSize = Toolkit.getDefaultToolkit().getScreenSize();
    static int windowWidth = (int) windowSize.getWidth();
    static int windowHeight = (int) windowSize.getHeight();
    TopBar design = new TopBar(windowWidth, windowHeight);
    public Frame(){
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run(){
            setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            pack();
            setSize(windowSize);
            setLocationRelativeTo(null); 
            setVisible(true);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setResizable(true);
            add(design);
            setState(java.awt.Frame.NORMAL);
            //setBackground(Color.decode("#449d9d"));
        }
    });
    addMouseListener(this);
    addMouseMotionListener(this);
    }   
    @Override
    public void mouseClicked(MouseEvent e) { 
    }
    @Override
    public void mousePressed(MouseEvent e) {  
    }
    @Override
    public void mouseReleased(MouseEvent e) { 
    }
    @Override
    public void mouseEntered(MouseEvent e) {
    }
    @Override
    public void mouseExited(MouseEvent e) {

    }
    @Override
    public void mouseDragged(MouseEvent e) { 
    }
    @Override
    public void mouseMoved(MouseEvent e) {
        System.out.println(getWidth() + " " + getHeight());
        remove(design);
        windowWidth = getWidth();
        windowHeight = getHeight();
        design = new TopBar(windowWidth, windowHeight);
        add(design);
    }}

TopBar类

public class TopBar extends JPanel{
int windowWidth;
int windowHeight;
public class resourcesButton extends JButton implements MouseInputListener{
    public resourcesButton(int windowWidth, int windowHeight){
        setText("Resourses");
        setFont(new Font("Helvetica", Font.PLAIN, 24));
        setBorder(new RoundedBorder(Color.decode("#f75280"),2,16,16));
        setBackground(Color.decode("#f75280"));
        setForeground(Color.decode("#0c120c"));
        setOpaque(true);
        setCursor(new Cursor(Cursor.HAND_CURSOR));
        setBounds((int)(windowWidth/1.5), (int)(windowHeight/6.5)/2/3, (int)(windowWidth/8.33), (int)(9*(windowWidth/8.33)/16));
        setHorizontalAlignment(SwingConstants.CENTER);
        setVerticalAlignment(SwingConstants.CENTER);

        addMouseListener(this);
        addMouseMotionListener(this);
    }
    @Override
    public void mouseClicked(MouseEvent e) {}
    @Override
    public void mousePressed(MouseEvent e) {}
    @Override
    public void mouseReleased(MouseEvent e) {}
    @Override
    public void mouseEntered(MouseEvent e) {
        setCursor(new Cursor(Cursor.HAND_CURSOR));
    }
    @Override
    public void mouseExited(MouseEvent e) {
        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    }
    @Override
    public void mouseDragged(MouseEvent e) {
        System.out.println("Check");
    }
    @Override
    public void mouseMoved(MouseEvent e) {}
}
public class startButton extends JButton implements MouseInputListener{
    public startButton(int windowWidth, int windowHeight){
        setText("Start Diagnosis");
        setFont(new Font("Helvetica", Font.PLAIN, windowHeight/45));
        setBorder(new RoundedBorder(Color.decode("#f75280"),2,16,16));
        setBackground(Color.decode("#f75280"));
        setForeground(Color.decode("#0c120c"));
        setOpaque(true);
        setCursor(new Cursor(Cursor.HAND_CURSOR));
        setBounds((int)(windowWidth/1.25), (int)(windowHeight/6.5)/2/3, (int)(windowWidth/8.33), (int)(9*(windowWidth/8.33)/16));
        setHorizontalAlignment(SwingConstants.CENTER);
        setVerticalAlignment(SwingConstants.CENTER);

        addMouseListener(this);
        addMouseMotionListener(this);
    }
    @Override
    public void mouseClicked(MouseEvent e) {}
    @Override
    public void mousePressed(MouseEvent e) {}
    @Override
    public void mouseReleased(MouseEvent e) {}
    @Override
    public void mouseEntered(MouseEvent e) {
        setCursor(new Cursor(Cursor.HAND_CURSOR));
    }
    @Override
    public void mouseExited(MouseEvent e) {
        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    }
    @Override
    public void mouseDragged(MouseEvent e) {
        System.out.println("Check");
    }
    @Override
    public void mouseMoved(MouseEvent e) {}
}
public TopBar(int windowWidth, int windowHeight){
    setLayout(null);
    this.windowWidth = windowWidth;
    this.windowHeight = windowHeight;
    add(new resourcesButton(windowWidth, windowHeight));
    add(new startButton(windowWidth, windowHeight));
}
@Override
protected void paintComponent (Graphics graphic){
    BufferedImage logo = null;
    try {
        logo = ImageIO.read(new File("SySLogo.png"));

    } catch (IOException e) {
        e.printStackTrace();
    }
    super.paintComponent(graphic);
    Graphics2D g2d = (Graphics2D) graphic.create();
    g2d.setColor(Color.decode("#449d9d"));
    g2d.fillRect(0, 0, windowWidth, (int)(windowHeight/6.5));
    try {
        g2d.drawImage(resizeImage(logo, (int)(windowWidth/8.33), (int)(windowHeight/8.6481)), windowWidth/16, (int)(windowHeight/48), this);
    } catch (IOException e) {
        g2d.drawImage(logo, windowWidth/16, (int)(windowHeight/48), this);
        e.printStackTrace();
    }
    g2d.setColor(Color.decode("#f2eee3"));
    g2d.drawLine(0, (int)(windowHeight/6.5), windowWidth, (int)(windowHeight/6.5));
}
private BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException {
    BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
    Graphics2D graphics2D = resizedImage.createGraphics();
    graphics2D.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null);
    graphics2D.dispose();
    return resizedImage;
}

问题分析与修复

核心问题

  1. 频繁重建组件导致监听器失效:Frame的mouseMoved方法中,每次鼠标移动都会删除旧的TopBar并新建一个添加。这会让之前创建的按钮被销毁,绑定的监听器自然失效,而且这种操作会严重拖慢程序运行。
  2. 空布局的隐患:TopBar用setLayout(null)手动设置按钮位置,窗口大小变化时按钮不会自动调整,还可能出现点击区域和视觉区域不匹配的情况,导致鼠标事件无法被正确触发。
  3. 冗余的监听器注册:Frame本身注册了MouseInputListener,可能会拦截下层组件的鼠标事件,但最关键的还是组件频繁重建的问题。

修复步骤

1. 替换Frame的鼠标移动监听为窗口大小变化监听

删掉Frame中mouseMoved的组件重建逻辑,改用ComponentListener监听窗口大小变化,通知TopBar更新而不是重建:

public Frame(){
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run(){
            setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            pack();
            setSize(windowSize);
            setLocationRelativeTo(null); 
            setVisible(true);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setResizable(true);
            add(design);
            // 添加窗口大小变化监听
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    windowWidth = getWidth();
                    windowHeight = getHeight();
                    design.updateSize(windowWidth, windowHeight);
                }
            });
            setState(java.awt.Frame.NORMAL);
        }
    });
    // 删掉Frame的MouseInputListener注册,不需要在JFrame层面监听鼠标移动
    // addMouseListener(this);
    // addMouseMotionListener(this);
}

// 移除原有的mouseMoved方法

2. 给TopBar添加更新方法,保留按钮引用

修改TopBar,保存按钮的引用,添加更新方法来调整按钮位置,避免重建组件:

public class TopBar extends JPanel{
    int windowWidth;
    int windowHeight;
    private resourcesButton resBtn;
    private startButton startBtn;

    public TopBar(int windowWidth, int windowHeight){
        setLayout(null);
        this.windowWidth = windowWidth;
        this.windowHeight = windowHeight;
        resBtn = new resourcesButton(windowWidth, windowHeight);
        startBtn = new startButton(windowWidth, windowHeight);
        add(resBtn);
        add(startBtn);
    }

    // 更新窗口大小和按钮位置
    public void updateSize(int newWidth, int newHeight){
        this.windowWidth = newWidth;
        this.windowHeight = newHeight;
        // 重新设置按钮的位置和大小
        resBtn.setBounds((int)(newWidth/1.5), (int)(newHeight/6.5)/2/3, (int)(newWidth/8.33), (int)(9*(newWidth/8.33)/16));
        startBtn.setBounds((int)(newWidth/1.25), (int)(newHeight/6.5)/2/3, (int)(newWidth/8.33), (int)(9*(newWidth/8.33)/16));
        repaint();
    }

    // 原有paintComponent方法不变,确保使用最新的windowWidth/windowHeight
    @Override
    protected void paintComponent (Graphics graphic){
        // 原有代码...
    }

    // 简化按钮的监听器,用MouseAdapter替代MouseInputListener,只重写需要的方法
    public class resourcesButton extends JButton{
        public resourcesButton(int windowWidth, int windowHeight){
            setText("资源");
            // 原有样式代码...
            setCursor(new Cursor(Cursor.HAND_CURSOR));
            // 只保留需要的拖拽逻辑
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    System.out.println("Check");
                }
            });
        }
    }

    // startButton同理简化
    public class startButton extends JButton{
        public startButton(int windowWidth, int windowHeight){
            setText("开始诊断");
            // 原有样式代码...
            setCursor(new Cursor(Cursor.HAND_CURSOR));
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    System.out.println("Check");
                }
            });
        }
    }
}

3. 优化按钮的光标处理

JButton设置setCursor(Cursor.HAND_CURSOR)后,默认会在鼠标进入/退出时自动切换光标,不需要手动在mouseEntered和mouseExited中重复设置,删掉这些冗余代码即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 23:09:35