簡體   English   中英

帶箭頭鍵的JRadioButton導航

[英]JRadioButton navigation with arrow keys

我試圖使用箭頭鍵使一組JRadioButton可以導航。 我打算使用KeyListeners手動實現此功能,但是顯然,這種行為至少應該在最近8年內起作用( http://bugs.sun.com/view_bug.do?bug_id=4104452 )。 但是,它對我不起作用:按箭頭鍵不會執行任何操作。 Java版本在Windows上為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);
    }
}

我嘗試使用不同的外觀和感覺,不同的容器和不同的布局管理器,但它仍然無法正常工作。

您需要將右/左(上/下?)鍵添加到每個單選按鈕的焦點遍歷策略中。 例如,添加左右箭頭鍵:

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

有關詳細信息,請閱讀有關如何使用焦點子系統的Swing教程中的部分。

我相信你可以使用KeyBindings而不是KeyListeners來實現你的目標。 在許多情況下,實際上建議綁定比KeyListeners,因為第二個可以產生許多問題(捕獲關鍵活動的幀必須是活動的等等)

謝謝大家的答案。

我發現了造成困惑的原因。 顯然,當Sun bug報告系統說錯誤狀態為“已關閉”且其“已解決日期”為“2005-07-19”時,這並不意味着該錯誤已得到修復。 顯然,它只是記錄為其他一些(較新的?)bug的副本。 自首次報道以來已近16年,它仍未得到修復。 隨你。

所需的行為比我意識到的要微妙得多。 我在各種程序中嘗試了本機Windows對話框:

  • 大多數類似按鈕的組件:按鈕,復選框和單選按鈕,實現用於焦點導航的箭頭鍵。 在Java中,它對應於AbstractButton類。 (JMenuItem也是該類的子類,但是它具有自己獨特的箭頭鍵行為。)
  • 在此導航期間僅選擇/選中單選按鈕。
  • 必須跳過無法聚焦(包括禁用或不可見)的組件。
  • 嘗試在組中的第一個按鈕之前或最后一個按鈕之后導航:在某些對話框中,它從頭到尾循環; 在其他人身上,它不可逆轉地移動到非按鈕組件上; 對其他人則什么也不做。 我嘗試了所有這些不同的行為,但沒有一個比其他行為特別好。

我在下面實現了循環行為,因為它感覺更加流暢。 導航無聲地跳過了過去的非AbstractButton組件,從而形成了按鈕專用的一種單獨的聚焦周期。 這是可疑的,但有時需要將一組相關的復選框或單選按鈕與其他組件混合使用。 測試一個共同的父組件以識別組也是一種合理的行為,但是在一個對話框中我不能使用單獨的組件純粹出於布局原因(在FlowLayout中實現換行符)。

正如所建議的那樣,我研究了InputMaps和ActionMaps,而不是使用KeyListener。 我總是避免使用地圖,因為它們看起來過於復雜,但是我想我看到了能夠輕松覆蓋綁定的優點。

這段代碼使用輔助外觀為整個應用程序范圍內的所有AbstractButton組件安裝所需的行為(這是我在這里找到的一種不錯的技術)。 我用幾個不同的對話框和窗口測試了它,似乎沒問題。 如果它引起問題,我將更新此帖子。

呼叫:

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

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

祝好運!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM