謝謝大家的回答。
我發現了我混亂的原因。顯然,當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