【问题标题】:Blocking Swing input without using modal dialog在不使用模式对话框的情况下阻止 Swing 输入
【发布时间】:2013-01-17 14:25:26
【问题描述】:

我想阻止一个窗口的输入,但仍然可以移动它。

如果有一个模态对话框类型允许生成它的窗口移动,那么我会很高兴。

假设我有一个打开另一个窗口的窗口。然后第二个窗口会打开一个模态对话框,它会阻止对其他两个窗口的输入(很好),但也会将这两个窗口锁定在适当的位置(为什么 - Amigas 没有这样做:)?)。

我的问题是,我可能需要在第一个窗口中直观地阅读某些内容以便在对话框中使用,但这可能是不可能的,因为第二个窗口被锁定在适当的位置,覆盖了它。

我想我几乎用玻璃板解决了这个问题。我将下面的类设置为窗口根窗格的玻璃窗格,然后在我想阻止时调用 setVisible(true) 并在我想解锁窗口时调用 setVisible(false)。锁定后,窗口会变灰以表明这一点。

除了关闭窗口外,鼠标输入被阻止,这现在很好 - 问题是我仍然可以在被阻止窗口上的组件周围使用选项卡,如果我进入可编辑的组件,我可以用键盘编辑它,不管我的 KeyListener 是空的。

有没有一种简单的方法可以防止玻璃窗格后面的组件获得焦点?

我希望它可以在“InputSink”类本身上完成。

我曾尝试添加自己的自私焦点遍历策略并在可见时请求焦点,但这没有效果。

我还尝试了一个示例,我发现添加了一个 FocusListener,如果玻璃窗格可见,它的 focusLost 方法会请求焦点,但这是多余的,因为窗口总是停留在前面。

有人知道这两个极端之间的解决方案吗?这就是我所拥有的:

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.MouseAdapter;

import javax.swing.JPanel;


public class InputSink extends JPanel {


public InputSink() {
    this(0.2f); //Default opacity.
}
public InputSink(float alpha) {
    setOpaque(false);
    setBackground(new Color(0, 0, 0, alpha)); //Just store it here.
    addMouseListener(new MouseAdapter() {});
    addKeyListener(new KeyAdapter() {});
    setFocusTraversalPolicy(new FocusTraversalPolicy() {
        @Override
        public Component getLastComponent(Container aContainer) {
            return InputSink.this;
        }
        @Override
        public Component getFirstComponent(Container aContainer) {
            return InputSink.this;
        }
        @Override
        public Component getDefaultComponent(Container aContainer) {
            return InputSink.this;
        }
        @Override
        public Component getComponentBefore(Container aContainer, Component aComponent) {
            return InputSink.this;
        }
        @Override
        public Component getComponentAfter(Container aContainer, Component aComponent) {
            return InputSink.this;
        }
    });
}

public void paintComponent(final Graphics gfx) { //Handle grey-out.
    gfx.setColor(getBackground());
    Rectangle rect = gfx.getClipBounds();
    gfx.fillRect(rect.x, rect.y, rect.width, rect.height);
}


@Override
public void setVisible(boolean visible) {
    super.setVisible(visible);
    if (visible)
        requestFocus();
}

}

所以我按照 Guillaume Polet 的建议使用的版本是

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class InputSink extends JPanel {

KeyEventDispatcher blockingDispatcher = new KeyEventDispatcher() {
    @Override
    public boolean dispatchKeyEvent(KeyEvent e) {
        return InputSink.this == ((JFrame) SwingUtilities.getWindowAncestor((Component) e.getSource())).getGlassPane(); //Consume!
    }
};

public InputSink) {
    this(0.2f); //Default opacity.
}
public InputSinkfloat alpha) {
    setOpaque(false);
    setBackground(new Color(0, 0, 0, alpha)); //Just store it here.
    addMouseListener(new MouseAdapter() {});
    addKeyListener(new KeyAdapter() {});
}

public void paintComponent(final Graphics gfx) { //Handle grey-out.
    gfx.setColor(getBackground());
    Rectangle rect = gfx.getClipBounds();
    gfx.fillRect(rect.x, rect.y, rect.width, rect.height);
}

@Override
public void setVisible(boolean visible) {
    super.setVisible(visible);
    if (visible)
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(blockingDispatcher);
    else
        KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(blockingDispatcher);
}

}

谢谢!

【问题讨论】:

  • 为什么您的应用程序不能将数据从第一个窗口复制到对话框?
  • 我需要做出(人工)决定,并根据我在第一个窗口中看到的内容在对话框中输入一些内容。第二个窗口在它前面,如果它打开一个模式对话框,那么我不能移动第二个窗口或第一个窗口,所以如果我需要的位在第二个窗口后面,我需要关闭对话框,移动第二个窗口,剪切/粘贴,写下或记住有问题的值,然后再次打开对话框。
  • 我明白了。将做出(人工)决定所需的信息从第一个窗口复制到对话框中。为什么一个窗口中有 2 个窗口而不是 2 个面板?一个窗口的两个面板可以解决重叠问题。
  • 我想这将是右侧“相关”下问题的一个答案:“为什么模式对话框是邪恶的?”..
  • Gilbert - 回答:我们的程序就是这样设置的。有一个主窗口(第一个窗口),您可以从中开始操作。然后,这些操作在第二个窗口中显示进度的实时流程图。然后,此流程图窗口在需要输入时打开模式对话框。不幸的是,输入有时取决于主窗口中的某些内容。

标签: java swing focus


【解决方案1】:

您可以将KeyEventDispatcher 添加到KeyboardFocusManager 以阻止键盘输入。

下面的小演示:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class TestGlassPane {

    private static final int COUNTDOWN = 10;

    private static final String CLICK_ME = "Click me";

    private static final Color GRAY = new Color(192, 192, 192, 128);

    private JFrame frame;

    private JButton button;

    private Timer timer;

    private int countdown;

    private KeyEventDispatcher blockingDispatcher;

    private static class GrayPanel extends JComponent {
        @Override
        protected void paintComponent(Graphics g) {
            g.setColor(GRAY);
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    public TestGlassPane() {
        blockingDispatcher = new KeyEventDispatcher() {

            @Override
            public boolean dispatchKeyEvent(KeyEvent e) {
                return true;
            }
        };

    }

    protected void initUI() {
        frame = new JFrame(TestGlassPane.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        button = new JButton(CLICK_ME);
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                blockUserInput();
            }
        });
        GrayPanel glassPane = new GrayPanel();
        glassPane.addMouseListener(new MouseAdapter() {
        });
        frame.setGlassPane(glassPane);
        frame.add(button);
        frame.setSize(200, 200);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    protected void blockUserInput() {
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(blockingDispatcher);
        frame.getGlassPane().setVisible(true);
        countdown = COUNTDOWN;
        timer = new Timer(1000, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                countdown--;
                if (countdown == 0) {
                    timer.stop();
                    frame.getGlassPane().setVisible(false);
                    button.setText(CLICK_ME);
                    KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(blockingDispatcher);
                } else {
                    button.setText("We will be back in " + countdown + " seconds");
                }
            }
        });
        timer.start();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestGlassPane().initUI();
            }
        });
    }

}

通常,可以使用 Space 键激活按钮,但您会看到它实际上被阻止了。

【讨论】:

  • 如果我过滤类似:return InputSink.this == ((JFrame) SwingUtilities.getWindowAncestor((Component) e.getSource())).getGlassPane(); 而不是只在 KeyEventDispatcher 中返回 true(可能隐含在您将其称为演示!),则此方法有效。
【解决方案2】:

因为第二个窗口被锁定

JDialog 对我来说一直是“可移动的”(使用 windows)。

另一种阻止输入的可能性:

当你显示你的非模态对话框时,包括这一行

frame.setEnabled(false);

还在对话框中添加一个 windowListener,以便在关闭时

frame.setEnabled(true);

在 Windows 上似乎可以正常工作,其他平台未知

【讨论】:

  • 如果我添加 'frame.setEnabled(!visible);'在我的 InputSink 的 setVisible(boolean) 方法中,框架是 InputSink 设置为玻璃面板的 JFrame,输入被完全阻止,但是在阻止时,窗口也被锁定到位因为无法选择移动..(目前正在Windows 7下测试)?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-09
  • 2011-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-06
相关资源
最近更新 更多