在 JTextField 中点击 space 键会触发父 window 的键绑定
Hitting space key in JTextField triggers key binding of parent window
我在 JFrame
的根窗格中注册了一个事件,该事件在按下 space 键时做出反应(打开另一个 window)。我在该 JFrame 中也有一个 JTextField
。当用户处于我的文本字段的编辑模式并点击 space 键时,space 事件应仅由文本字段使用,而不应转发到 JFrame
的动作映射。
我该怎么做?
这是该问题的可运行演示:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
public class TestDialog {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spaceAction");
frame.getRootPane().getActionMap().put("spaceAction", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("spaceAction");
}
});
JTextField tf = new JTextField("textfield");
JLabel label = new JLabel("otherComponent");
label.setFocusable(true);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(tf);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
使用 space 键作为全局触发器不是一个好主意。但如果你真的需要它,这里是方法:
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.text.JTextComponent;
public class DialogTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spaceAction");
frame.getRootPane().getActionMap().put("spaceAction", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (EventQueue.getCurrentEvent() instanceof KeyEvent) {
KeyEvent ke = (KeyEvent) EventQueue.getCurrentEvent();
if (!(ke.getComponent() instanceof JTextComponent)) {
System.out.println("spaceAction");
} else {
System.out.println("Ignore event in text component");
}
} else {
System.out.println("spaceAction");
}
}
});
JTextField tf = new JTextField("textfield");
JLabel label = new JLabel("otherComponent");
label.setFocusable(true);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(tf);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
更好的方法是从根窗格开始遍历组件树,只为您需要的组件(例如所有标签)添加键绑定。这是我的遍历方法
/**
* Searches for all children of the given component which are instances of the given class.
*
* @param aRoot start object for search.
* @param aClass class to search.
* @param <E> class of component.
* @return list of all children of the given component which are instances of the given class. Never null.
*/
public static <E> List<E> getAllChildrenOfClass(Container aRoot, Class<E> aClass) {
final List<E> result = new ArrayList<E>();
final Component[] children = aRoot.getComponents();
for (final Component c : children) {
if (aClass.isInstance(c)) {
result.add(aClass.cast(c));
}
if (c instanceof Container) {
result.addAll(getAllChildrenOfClass((Container) c, aClass));
}
}
return result;
}
我通过创建一个简单的 JTextField 子类解决了这个问题,该子类使用用户按下的所有可打印字符。这样您就不必修改封闭组件。
public class JTextFieldNoKeyBinding extends JTextField
{
public JTextFieldNoKeyBinding()
{
// Key presses are processed by JTextField but NOT consumed,
// so they end up being also processed by the key binding framework.
// The only way to block them is to capture all the printable keys: see
// https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
for (char c = 32; c <= 126; c++)
{
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(c, 0), "doNothing");
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(c, InputEvent.SHIFT_DOWN_MASK), "doNothing");
}
getActionMap().put("doNothing", new NoAction());
}
private class NoAction extends AbstractAction
{
@Override
public void actionPerformed(ActionEvent e)
{
//do nothing
}
}
}
我在 JFrame
的根窗格中注册了一个事件,该事件在按下 space 键时做出反应(打开另一个 window)。我在该 JFrame 中也有一个 JTextField
。当用户处于我的文本字段的编辑模式并点击 space 键时,space 事件应仅由文本字段使用,而不应转发到 JFrame
的动作映射。
我该怎么做?
这是该问题的可运行演示:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
public class TestDialog {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spaceAction");
frame.getRootPane().getActionMap().put("spaceAction", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("spaceAction");
}
});
JTextField tf = new JTextField("textfield");
JLabel label = new JLabel("otherComponent");
label.setFocusable(true);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(tf);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
使用 space 键作为全局触发器不是一个好主意。但如果你真的需要它,这里是方法:
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.text.JTextComponent;
public class DialogTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spaceAction");
frame.getRootPane().getActionMap().put("spaceAction", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (EventQueue.getCurrentEvent() instanceof KeyEvent) {
KeyEvent ke = (KeyEvent) EventQueue.getCurrentEvent();
if (!(ke.getComponent() instanceof JTextComponent)) {
System.out.println("spaceAction");
} else {
System.out.println("Ignore event in text component");
}
} else {
System.out.println("spaceAction");
}
}
});
JTextField tf = new JTextField("textfield");
JLabel label = new JLabel("otherComponent");
label.setFocusable(true);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(tf);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
更好的方法是从根窗格开始遍历组件树,只为您需要的组件(例如所有标签)添加键绑定。这是我的遍历方法
/**
* Searches for all children of the given component which are instances of the given class.
*
* @param aRoot start object for search.
* @param aClass class to search.
* @param <E> class of component.
* @return list of all children of the given component which are instances of the given class. Never null.
*/
public static <E> List<E> getAllChildrenOfClass(Container aRoot, Class<E> aClass) {
final List<E> result = new ArrayList<E>();
final Component[] children = aRoot.getComponents();
for (final Component c : children) {
if (aClass.isInstance(c)) {
result.add(aClass.cast(c));
}
if (c instanceof Container) {
result.addAll(getAllChildrenOfClass((Container) c, aClass));
}
}
return result;
}
我通过创建一个简单的 JTextField 子类解决了这个问题,该子类使用用户按下的所有可打印字符。这样您就不必修改封闭组件。
public class JTextFieldNoKeyBinding extends JTextField
{
public JTextFieldNoKeyBinding()
{
// Key presses are processed by JTextField but NOT consumed,
// so they end up being also processed by the key binding framework.
// The only way to block them is to capture all the printable keys: see
// https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
for (char c = 32; c <= 126; c++)
{
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(c, 0), "doNothing");
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(c, InputEvent.SHIFT_DOWN_MASK), "doNothing");
}
getActionMap().put("doNothing", new NoAction());
}
private class NoAction extends AbstractAction
{
@Override
public void actionPerformed(ActionEvent e)
{
//do nothing
}
}
}