在 Mac OS X 中最大化或全屏显示窗口后,Java 停止捕获鼠标移动事件

Posted

技术标签:

【中文标题】在 Mac OS X 中最大化或全屏显示窗口后,Java 停止捕获鼠标移动事件【英文标题】:Java stops capturing mouse movement events after maximizing or full screening a window in Mac OS X 【发布时间】:2013-12-01 22:10:45 【问题描述】:

当我在 OS X 上通过按下最大化按钮或全屏按钮来放大窗口时,不再捕获鼠标移动事件(尽管可以拖动)。

我在下面添加了一个演示程序来突出显示该问题。可以使用Java Tutorials website 上的MouseEventDemo web start example 复制最大化问题。

经过一些故障排除后,我注意到如果鼠标离开窗口(例如,移动到窗口顶部以访问菜单栏)然后返回,鼠标移动会被重新捕获。似乎这个问题可能与调整动画大小期间鼠标位置和窗口之间的关系有关,因为鼠标在调整大小之前不在帧中,但在调整之后它不一定在过程中移动。

import java.awt.Window;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.lang.reflect.Method;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Main implements MouseMotionListener 

    JLabel label = new JLabel("label");

    public static void main(String[] args) 
        Main main = new Main();
        main.init();
    

    public void init() 
        JFrame frame = new JFrame();
        frame.setSize(640, 480);
        frame.setLocationRelativeTo(null);
        frame.getContentPane().add(label);
        frame.addMouseMotionListener(this);
        frame.setVisible(true);

        if (isMacOSX()) 
            enableFullScreenMode(frame);
        
    

    public void mouseDragged(MouseEvent e) 
        label.setText(e.toString());
    

    public void mouseMoved(MouseEvent e) 
        label.setText(e.toString());
    

    private static boolean isMacOSX() 
        return System.getProperty("os.name").indexOf("Mac OS X") >= 0;
    

    public static void enableFullScreenMode(Window window) 
        try 
            Class<?> clazz = Class.forName("com.apple.eawt.FullScreenUtilities");
            Method method = clazz.getMethod("setWindowCanFullScreen", new Class<?>[]  Window.class, boolean.class );
            method.invoke(null, window, true);
         catch (Throwable t) 
            t.printStackTrace();
        
    

运行上面的代码应该会显示标签何时更新和不更新。

我正在使用 Java SE 7 [1.7.0_45] 运行 OS X 版本 10.9 Build 13A3017。

【问题讨论】:

@Petesh,我的意思是问是否有解决此问题的方法或已知的解决方法,因为这似乎是一个常见问题。我已将此问题通知 Oracle,因为他们要求他们文档中的示例存在问题。 【参考方案1】:

根据经验,如果在最大化的情况下切换回应用程序,问题就会消失。添加强制框架toFront()ComponentListener 似乎有效。顺便说一句,Swing GUI 对象应该在event dispatch thread 上构建和操作。

frame.addComponentListener(new ComponentAdapter() 

    @Override
    public void componentResized(ComponentEvent e) 
        frame.toFront();
    
);

经测试:

import java.awt.EventQueue;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.lang.reflect.Method;
import javax.swing.JFrame;
import javax.swing.JLabel;

/** @see http://***.com/a/20054242/230513 */
public class Main implements MouseMotionListener 

    JLabel label = new JLabel("label");

    public static void main(String[] args) 
        EventQueue.invokeLater(new Runnable() 

            @Override
            public void run() 
                Main main = new Main();
                main.init();
            
        );
    

    public void init() 
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(label);
        frame.pack();
        frame.setSize(640, 480);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        if (isMacOSX()) 
            enableFullScreenMode(frame);
        
        frame.addMouseMotionListener(this);
        frame.addComponentListener(new ComponentAdapter() 

            @Override
            public void componentResized(ComponentEvent e) 
                frame.toFront();
            
        );
    

    @Override
    public void mouseDragged(MouseEvent e) 
        label.setText(e.toString());
    

    @Override
    public void mouseMoved(MouseEvent e) 
        label.setText(e.toString());
    

    private static boolean isMacOSX() 
        return System.getProperty("os.name").indexOf("Mac OS X") >= 0;
    

    public static void enableFullScreenMode(Window window) 
        try 
            Class<?> clazz = Class.forName("com.apple.eawt.FullScreenUtilities");
            Method method = clazz.getMethod("setWindowCanFullScreen",
                    new Class<?>[]Window.class, boolean.class);
            method.invoke(null, window, true);
         catch (Throwable t) 
            t.printStackTrace();
        
    

【讨论】:

感谢您的回复。该问题似乎在应用程序切换后消失了。这里是a citation to the full screen icon code。有趣的是,您的代码在按下全屏图标时解决了这个问题,而不是最大化按钮。你是这种情况吗? 是的,最大化按钮干扰鼠标移动事件;我没有注意到,因为应用程序切换也会恢复侦听器。组件上的鼠标移动似乎不受影响。【参考方案2】:

Oracle 回复了我提交的 the bug report,并在他们的 cmets 中指出该问题应在 Java 8 中解决。上面的代码在使用 OS X 10.9 的 1.8.0 JRE(内部版本 1.8.0-b132)上按预期工作.2.

【讨论】:

【参考方案3】:

通过将 windowStateListener 添加到框架并执行以下操作,我能够找到解决此问题最大化部分的方法:

frame.addWindowStateListener(new WindowStateListener()

    @Override
    public void windowStateChanged(WindowEvent e)
    
        if (Frame.MAXIMIZED_BOTH == e.getNewState())
        
            frame.toBack();
            frame.toFront();
        
    
);

如果您将此与从垃圾神解决的调整大小工作结合起来,您应该处于良好状态。

【讨论】:

【参考方案4】:

使用 jdk 1.6.0_65 (yosemite) 时不会触发 mouseReleased、mouseClicked 事件,对于 jdk 1.8.0_25 (yosemite) 会触发事件,并且在 jdk 1.6 (maverick) 上也会触发事件。可能是什么问题?

java -版本 java版本“1.6.0_65” Java(TM) SE 运行时环境(构建 1.6.0_65-b14-466.1-11M4716) Java HotSpot(TM) 64 位服务器 VM(内部版本 20.65-b04-466.1,混合模式)

窗口激活 窗口打开 鼠标按下 窗口关闭 窗口停用

java版本“1.8.0_25” Java(TM) SE 运行时环境 (build 1.8.0_25-b17) Java HotSpot(TM) 64 位服务器 VM(内部版本 25.25-b02,混合模式)

窗口激活 窗口打开 鼠标按下 鼠标释放 鼠标点击 窗口关闭 窗口停用

导入 java.awt.; 导入 java.awt.event.;

公共类 TestMouseEventsOnYosemite

public static void main(String[] args) 
    TestFrame testFrame = new TestFrame();
    testFrame.setVisible(true);
    testFrame.toFront();


public static class TestFrame extends Frame implements WindowListener 

    public TestFrame() 
        setLayout(null);
        addNotify();
        Button okButon = new Button("    Ok    ");
        add(okButon);

        okButon.addMouseListener(new MouseAdapter() 

            public void mouseReleased(MouseEvent event) 
                System.out.println("mouseReleased");
            

            public void mousePressed(MouseEvent event) 
                System.out.println("mousePressed");
            

            public void mouseClicked(MouseEvent event) 
                System.out.println("mouseClicked");
            
        
        );

        okButon.addKeyListener(new KeyAdapter() 
            public void keyPressed(KeyEvent event) 
                System.out.println("keyPressed");
            
        
        );

        int dy = okButon.getPreferredSize().height;

        okButon.setBounds(30, 50, 100, dy);

        setSize(150, 150);
        addWindowListener(this);
        setResizable(true);
        okButon.requestFocusInWindow();
    

    public void windowActivated(WindowEvent e) 
        System.out.println("windowActivated");
    

    public void windowClosed(WindowEvent e) 
        System.out.println("windowClosed");
    

    public void windowDeactivated(WindowEvent e) 
        System.out.println("windowDeactivated");
    

    public void windowDeiconified(WindowEvent e) 
        System.out.println("windowDeiconified");
    

    public void windowIconified(WindowEvent e) 
        System.out.println("windowIconified");
    

    public void windowOpened(WindowEvent e) 
        System.out.println("windowOpened");
    

    public void windowClosing(WindowEvent e) 
        System.out.println("windowClosing");
    

【讨论】:

以上是关于在 Mac OS X 中最大化或全屏显示窗口后,Java 停止捕获鼠标移动事件的主要内容,如果未能解决你的问题,请参考以下文章

Adobe AIR 全屏(OS X Mavericks)菜单栏

C ++ glfw3:全屏模式下的一个(两个)窗口不是真正的全屏(Mac Os)

如何在 Qt 的 Mac OS X 工具窗口中删除最大化按钮

如何在 MAC OS X 中获得最大 TCP 接收/发送窗口?

如何在 C++ 中获得 Mac OS X 中主显示器的分辨率?

qt中将窗口关闭后为啥还会显示到历史窗口