谢谢大家的回答。
我发现了我混乱的原因。显然,当Sun的缺陷报告系统称错误状态为“已关闭”且其“已解决日期”为“2005-07-19”时,并不意味着该错误已被修复。显然,它仅记录为some other (newer?) bug的副本。自首次报道以来已有近16年的时间,它仍然不是固定的。随你。
需要的行为比我意识到的要微妙得多。我在各种程序的本地Windows对话框中进行了实验:
- 大多数类似按钮的组件:按钮,复选框和单选按钮实现焦点导航的箭头键。在Java中,这对应于AbstractButton类。 (JMenuItem也是它的一个子类,但它有自己独特的箭头键行为。)
- 在此导航过程中仅选择/检查单选按钮。
- 不可聚焦(包括禁用或不可见)组件必须跳过。
- 试图在组中的第一个按钮之前或最后一个按钮之后导航不一致:在某些对话框中,它从头到尾循环;在别人身上它不可逆地移动到非按钮组件上;而在其他方面它什么都不做。我尝试了所有这些不同的行为,没有一个比其他人更好。
我在下面实现了一个循环行为,因为它感觉稍微流畅。导航以静默方式跳过非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);
}
}
}
}
@Boann遵循这一建议,增加JradioButton将数组和覆盖的IsEnabled太 – mKorbel