我正在尝试使用箭头键导航一组JRadioButton
s。我打算用KeyListeners手动实现它,但显然这种行为至少可以在过去8年中起作用(http://bugs.sun.com/view_bug.do?bug_id=4104452)。但是,它对我不起作用:按箭头键不起作用。 Windows版本的Java版本为7u45。
一个独立的测试用例,看看我在说什么:
import java.awt.*;
import javax.swing.*;
public class Test {
public static void main(final String[] args) {
if (!EventQueue.isDispatchThread()) {
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
main(args);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
return;
}
try {
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
//UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Throwable t) {
throw new RuntimeException(t);
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ButtonGroup group = new ButtonGroup();
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
JRadioButton rb;
rb = new JRadioButton("Option A");
panel.add(rb);
group.add(rb);
rb = new JRadioButton("Option B");
panel.add(rb);
group.add(rb);
rb = new JRadioButton("Option C");
panel.add(rb);
group.add(rb);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
我尝试过使用不同的外观&感觉,不同的容器和不同的布局管理器,但它仍然不起作用。
答案 0 :(得分:3)
您需要将右/左(上/下?)键添加到每个单选按钮的焦点遍历策略中。例如,添加右/左箭头键:
Set set = new HashSet( rb.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS ) );
set.add( KeyStroke.getKeyStroke( "RIGHT" ) );
rb.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, set );
set = new HashSet( rb.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS ) );
set.add( KeyStroke.getKeyStroke( "LEFT" ) );
rb.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, set );
阅读How to Use the Focus Subsystem上的Swing教程中的部分以获取更多信息。
答案 1 :(得分:2)
我相信你可以使用KeyBindings而不是KeyListeners来实现你的目标。在许多情况下,实际上建议绑定比KeyListeners,因为第二个可以产生许多问题(捕获关键活动的帧必须是活动的等等。)
答案 2 :(得分:2)
谢谢大家的答案。
我发现了我混淆的原因。显然,当Sun bug报告系统说错误状态为“已关闭”且其“已解决日期”为“2005-07-19”时,这并不意味着该错误已得到修复。显然,它只是作为some other (newer?) bug的副本记录。自首次报道以来已近16年,它仍未得到修复。不管。
所需的行为比我意识到的要微妙得多。我在各种程序中的本机Windows对话框中进行了实验:
我在下面实现了一个循环行为,因为它感觉稍微流畅一点。导航静默地跳过非AbstractButton组件,形成一个独立的焦点循环私有按钮。这是可疑的,但有时需要将一组相关的复选框或单选按钮与其他组件混合使用。测试一个共同的父组件以识别组也是一种合理的行为,但是这在一个对话框中不起作用,我只是出于布局原因使用单独的组件(在FlowLayout中实现换行符)。
正如所建议的,我研究了InputMaps和ActionMaps,而不是使用KeyListener。我总是避免使用地图,因为它们看起来过于复杂,但我想我看到了能够轻松覆盖绑定的优势。
此代码使用辅助外观来为应用程序范围内的所有AbstractButton组件安装所需的行为(这是我发现的关于here的一种很好的技术)。我用几个不同的对话框和窗口测试了它,似乎没问题。如果它导致问题我会更新这篇文章。
呼叫:
ButtonArrowKeyNavigation.install();
在应用程序启动时安装它。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonArrowKeyNavigation {
private ButtonArrowKeyNavigation() {}
public static void install() {
UIManager.addAuxiliaryLookAndFeel(lookAndFeel);
}
private static final LookAndFeel lookAndFeel = new LookAndFeel() {
private final UIDefaults defaults = new UIDefaults() {
@Override
public javax.swing.plaf.ComponentUI getUI(JComponent c) {
if (c instanceof AbstractButton && !(c instanceof JMenuItem)) {
if (c.getClientProperty(this) == null) {
c.putClientProperty(this, Boolean.TRUE);
configure(c);
}
}
return null;
}
};
@Override public UIDefaults getDefaults() { return defaults; };
@Override public String getID() { return "ButtonArrowKeyNavigation"; }
@Override public String getName() { return getID(); }
@Override public String getDescription() { return getID(); }
@Override public boolean isNativeLookAndFeel() { return false; }
@Override public boolean isSupportedLookAndFeel() { return true; }
};
private static void configure(JComponent c) {
InputMap im = c.getInputMap(JComponent.WHEN_FOCUSED);
ActionMap am = c.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "focusPreviousButton");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "focusPreviousButton");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "focusNextButton");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "focusNextButton");
am.put("focusPreviousButton", focusPreviousButton);
am.put("focusNextButton", focusNextButton);
}
private static final Action focusPreviousButton = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
move((AbstractButton)e.getSource(), -1);
}
};
private static final Action focusNextButton = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
move((AbstractButton)e.getSource(), +1);
}
};
private static void move(AbstractButton ab, int direction) {
Container focusRoot = ab.getFocusCycleRootAncestor();
FocusTraversalPolicy focusPolicy = focusRoot.getFocusTraversalPolicy();
Component toFocus = ab, loop = null;
for (;;) {
toFocus = direction > 0
? focusPolicy.getComponentAfter(focusRoot, toFocus)
: focusPolicy.getComponentBefore(focusRoot, toFocus);
if (toFocus instanceof AbstractButton) break;
if (toFocus == null) return;
// infinite loop protection; should not be necessary, but just in
// case all buttons are somehow unfocusable at the moment this
// method is called:
if (loop == null) loop = toFocus; else if (loop == toFocus) return;
}
if (toFocus.requestFocusInWindow()) {
if (toFocus instanceof JRadioButton) {
((JRadioButton)toFocus).setSelected(true);
}
}
}
}
答案 3 :(得分:0)
以下是我使用箭头键(向上和向下)可以导航的JRadioButtons示例,并为您修改了几个代码。
public class JRadioButton extends JPanel {
private JRadioButton[] buttons;
public JRadioButtonTest(int row) {
ButtonGroup group = new ButtonGroup();
buttons = new JRadioButton[row];
for (int i = 0; i < buttons.length; i++) {
final int curRow = i;
buttons[i] = new JRadioButton("Option " + i);
buttons[i].addKeyListener(enter);
buttons[i].addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
if (curRow > 0)
buttons[curRow - 1].requestFocus();
break;
case KeyEvent.VK_DOWN:
if (curRow < buttons.length - 1)
buttons[curRow + 1].requestFocus();
break;
default:
break;
}
}
});
group.add(buttons[i]);
add(buttons[i]);
}
}
private KeyListener enter = new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == KeyEvent.VK_ENTER) {
((JButton) e.getComponent()).doClick();
}
}
};
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JRadioButton(3));
frame.pack();
frame.setVisible(true);
}
}
当调用箭头键时,核心实现方法在正确的JRadioButton上调用requestFocus()。按Enter键时的额外KeyListener。
您可以将此KeyListener用于您的程序并添加更多密钥。
祝你好运!