Java 在JTextField中点击空格键会触发父窗口的键绑定
我在Java 在JTextField中点击空格键会触发父窗口的键绑定,java,swing,key-bindings,Java,Swing,Key Bindings,我在JFrame的根窗格中注册了一个事件,该事件在按下空格键时发生反应(打开另一个窗口)。我在JFrame中还有一个JTextField。当用户处于my textfield的编辑模式并点击空格键时,空格事件应该仅由textfield使用,而不是转发到JFrame的actionmap 我该怎么做 以下是该问题的可运行演示: import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.Key
JFrame
的根窗格中注册了一个事件,该事件在按下空格键时发生反应(打开另一个窗口)。我在JFrame中还有一个JTextField
。当用户处于my textfield的编辑模式并点击空格键时,空格事件应该仅由textfield使用,而不是转发到JFrame
的actionmap
我该怎么做
以下是该问题的可运行演示:
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);
}
}
使用空格键作为全局触发器不是一个好主意。但如果你真的需要它,这里有一个方法:
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;
}
/**
*搜索作为给定类实例的给定组件的所有子级。
*
*@param aRoot开始搜索对象。
*@param aClass类进行搜索。
*@param组件的类。
*@返回给定组件的所有子级的列表,这些子级是给定类的实例。永不为空。
*/
公共静态列表getAllChildrenOfClass(容器aRoot,类aClass){
最终列表结果=新建ArrayList();
final Component[]children=aRoot.getComponents();
用于(最终部分c:儿童){
if(类I实例(c)){
结果.添加(类铸件(c));
}
if(容器的c实例){
结果.addAll(getAllChildrenOfClass((容器)c,aClass));
}
}
返回结果;
}
我通过创建一个简单的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
}
}
}
公共类JTextFieldNoKeyBinding扩展了JTextField
{
公共JTextFieldNoKeyBinding()
{
//按键由JTextField处理,但不使用,
//因此,它们最终也被密钥绑定框架处理。
//阻止它们的唯一方法是捕获所有可打印的键:请参阅
// https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
对于(char c=32;c)你使用了什么输入映射级别?也许考虑提供一个可运行的例子来演示你的问题。我尝试了所有3个输入映射级别,它们都在按预期的方式工作(PosirOrthoFixCuxSeMeCopp,当你在FixSuxy窗口,当聚焦时),前两个功能如我所述正常工作,第三个级别根本不起作用,因为根窗格没有焦点,我不应该使用您的第一个解决方案解决它,谢谢。但这是一个功能还是一个JTextField转发已使用的键盘字符的bug?@trainRobble我认为这是一个功能。在某些情况下,是多事件可能需要进行处理。