2013-11-28 131 views
2

我想要使用箭頭鍵可以導航一組JRadioButton s。我打算用KeyListeners手動實現這一點,但顯然這種行爲至少應該在過去的8年中(http://bugs.sun.com/view_bug.do?bug_id=4104452)。但是,它不適合我:按箭頭鍵不會執行任何操作。 Windows上的Java版本是7u45。帶箭頭鍵的JRadioButton導航

一個獨立的測試案例,看看我說的是:

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

我一直在使用不同的外觀&感覺,不同的容器,和不同的佈局管理器試過了,但它仍然無法正常工作。

回答

1

謝謝大家的回答。

我發現了我混亂的原因。顯然,當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); 
      } 
     } 
    } 
} 
2

我相信你可以用KeyBindings而不是KeyListeners實現你的目標。在很多情況下,綁定實際上是通過KeyListeners推薦的,因爲第二個綁定可能會產生很多問題(幀捕獲的關鍵活動必須是活動的等)

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的教程部分獲取更多信息。

+0

@Boann遵循這一建議,增加JradioButton將數組和覆蓋的IsEnabled太 – mKorbel

0

這裏是我的JRadioButtons的例子,可以使用箭頭鍵(UP和DOWN)進行導航並修改了幾個代碼。

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用於您的程序並添加更多密鑰。

祝你好運!

+2

鞦韆已經爲焦點遍歷的API。或Swing被設計爲與鍵綁定一起使用。不要使用KeyListener。另外,你應該使用'requestFocus()'方法。閱讀API會告訴你使用的撥用方法。 – camickr