2013-11-28 3 views
2

Я пытаюсь получить группу JRadioButton s для навигации с помощью клавиш со стрелками. Я собирался реализовать это вручную с помощью KeyListeners, но, похоже, это поведение уже должно работать, по крайней мере, последние 8 лет (http://bugs.sun.com/view_bug.do?bug_id=4104452). Однако это не работает для меня: нажатие клавиш со стрелками ничего не делает. Версия Java - 7u45 для Windows.Навигация 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 для получения дополнительной информации.

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

Ядро реализации метода вызова requestFocus() на правильной JRadioButton когда клавиша со стрелкой называется. Extra KeyListener для нажатия клавиши Enter.

Вы можете использовать этот KeyListener для своей программы и добавить еще ключ.

Удачи вам!

+2

Свинг уже есть API для обхода фокуса. Или Swing был разработан для использования с Key Bindings. Не используйте KeyListener. Кроме того, вы должны 'НЕ' использовать метод' requestFocus() '. Прочтите API, он скажет вам, какой метод использовать. – camickr

Смежные вопросы