【问题标题】:Incorrect cursor when outside of modal JDialog?在模态 JDialog 之外时光标不正确?
【发布时间】:2015-10-04 02:50:39
【问题描述】:

当使用 setCursor() 方法来更改组件使用的光标时,所有组件都可以正常工作,包括 JFrameJDialog

这里的问题在于 modal JDialog。当鼠标在对话框内部时,光标显示在右侧。但是,当鼠标移到对话框之外时,光标会重新设置为操作系统默认值,即使底层JFrame 使用与对话框相同的自定义光标。

我搜索了很多,找到了一些相关的问题,但没有一个正确答案。

我使用的是 Windows 10; JDK 1.8.0_40。

SSCCE:

package br.shura.knockback;

import java.awt.Cursor;
import java.awt.Dimension;
import javax.swing.*;

public class DialogCursorSSCCE extends JFrame {
  public DialogCursorSSCCE() {
    Cursor cursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
    JButton button = new JButton("Click me to open dialog.");

    button.addActionListener(event -> {
      JDialog dialog = new JDialog();
      JLabel label = new JLabel("Move the mouse outside this dialog.");
      int width = label.getFontMetrics(label.getFont()).stringWidth(label.getText());

      label.setPreferredSize(new Dimension(width + 10, 50));
      dialog.add(label);
      dialog.pack();
      dialog.setCursor(cursor);
      dialog.setLocationRelativeTo(button);
      dialog.setModal(true);
      dialog.setTitle("Dialog");
      dialog.setVisible(true);
    });
    button.setAlignmentX(CENTER_ALIGNMENT);
    button.setMaximumSize(new Dimension(400, 100));
    setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
    add(button);
    setCursor(cursor);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setExtendedState(MAXIMIZED_BOTH);
    setTitle("DialogCursorSSCCE");
  }

  public static void main(String[] args) {
    new DialogCursorSSCCE().setVisible(true);
  }
}

【问题讨论】:

    标签: java swing modal-dialog cursor jdialog


    【解决方案1】:

    但是,当鼠标移出对话框时,光标会重新设置为操作系统默认值,

    我对正在发生的事情的猜测是对话框的边框是操作系统对等组件。因此,当您离开 Swing 组件时,将为 OS 对等体生成鼠标悬停事件,因此光标设置为 OS 默认值。

    当您完全离开对话框时,框架不会收到任何事件,因为对话框是模态的,因此系统光标在框架上时仍会显示。

    注意光标进入标题栏时的变化。

    现在尝试以下操作:

    JDialog dialog = new JDialog();
    dialog.setUndecorated(true);
    dialog.getRootPane().setWindowDecorationStyle(JRootPane.PLAIN_DIALOG);
    

    现在光标只有在到达边框时才会改变,因为 Swing 正在绘制标题栏。

    我也试过了:

      JDialog dialog = new JDialog();
      dialog.setUndecorated(true);
    

    所以没有装饰,但是光标离开窗口时仍然会改变。

    我不知道有什么办法。

    【讨论】:

    • 谢谢!经过几个小时的搜索,考虑到 Java UI 工具包,我认为这是“不可能的”。
    【解决方案2】:

    我讨厌“不可能”的答案——实际上我必须这样做。所以这里是我找到的解决方法:

    1) 使用 ProgressMonitor(比如这里:https://docs.oracle.com/javase/tutorial/uiswing/components/progress.html) - 不是我的情况,因为我必须强烈自定义它

    2) 模拟模态。这是我的意思的基本想法(是的,我知道我不会以这种方式锁定主框架的 GUI - 你也可以这样做 - 幸运的是我:不是我的情况)

            mainFrame.addComponentListener(componentListener = new ComponentListener() {
                @Override
                public void componentShown(ComponentEvent e) {
                }
    
                @Override
                public void componentResized(ComponentEvent e) {
                    placeDialog();
                }
    
                @Override
                public void componentMoved(ComponentEvent e) {
                    placeDialog();
                }
    
                @Override
                public void componentHidden(ComponentEvent e) {
                }
            });
            ((Window) mainFrame).addWindowListener(windowListener = new WindowListener() {
                @Override
                public void windowOpened(WindowEvent e) {
                    placeDialog();
                }
    
                @Override
                public void windowIconified(WindowEvent e) {
                    dialog.setVisible(false);
                }
    
                @Override
                public void windowDeiconified(WindowEvent e) {
                    placeDialog();
                }
    
                @Override
                public void windowDeactivated(WindowEvent e) {
                }
    
                @Override
                public void windowClosing(WindowEvent e) {
                }
    
                @Override
                public void windowClosed(WindowEvent e) {
                    closeDialog();
                }
    
                @Override
                public void windowActivated(WindowEvent e) {
                    placeDialog();
                }
            });
        }
    
        private void placeDialog() {
            dialog.setVisible(true);
            dialog.requestFocus();
            dialog.setLocation(mainFrame.getLocation().x + mainFrame.getWidth()/2 - dialog.getWidth()/2,
            mainFrame.getLocation().y + mainFrame.getHeight()/2 - dialog.getHeight()/2);
        }
    

    毕竟不要忘记从 mainFrame 中删除侦听器(就像在最后部分中一样)

    希望有更好的解决方案有很多反对者:)

    【讨论】:

    • 嗯,这很聪明......我会在我在家的时候测试它,如果它有效,会为你提供反馈。
    猜你喜欢
    • 1970-01-01
    • 2010-12-13
    • 2012-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多