在JTextField中命中空格键会触发父窗口的键绑定

时间:2016-04-14 08:09:27

标签: java swing key-bindings

我在JFrame的根窗格中注册了一个事件,该事件在按下空格键时会做出反应(打开另一个窗口)。我在JFrame中也有一个JTextField。当用户处于我的文本字段的编辑模式并点击空格键时,空间事件应该仅由文本字段使用,而不是转发到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);

    }

}

2 个答案:

答案 0 :(得分:1)

将空格键用作全局触发器并不是一个好主意。但如果你真的需要它,那么就是这样:

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;
}

答案 1 :(得分:0)

我通过创建一个简单的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
        }
    }
}