The following example shows how to enable cyclic focus transfer *
* import org.jdesktop.swinghelper.buttonpanel.*; * import javax.swing.*; * * public class SimpleDemo { * public static void main(String[] args) throws Exception { * SwingUtilities.invokeLater(new Runnable() { * public void run() { * final JFrame frame = new JFrame(); * frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); * * JXButtonPanel panel = new JXButtonPanel(); * panel.setCyclic(true); * * panel.add(new JButton("One")); * panel.add(new JButton("Two")); * panel.add(new JButton("Three")); * * frame.add(panel); * frame.setSize(200, 200); * frame.setLocationRelativeTo(null); * frame.setVisible(true); * } * }); * } * } ** * If your buttons inside JXButtonPanel are added to one ButtonGroup * arrow keys will transfer selection between them as well as they do it for focus
* Note: you can control this behaviour with setGroupSelectionFollowFocus(boolean) *
* import org.jdesktop.swinghelper.buttonpanel.*; * import javax.swing.*; * * public class RadioButtonDemo { * public static void main(String[] args) throws Exception { * SwingUtilities.invokeLater(new Runnable() { * public void run() { * final JFrame frame = new JFrame(); * frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); * * JXButtonPanel panel = new JXButtonPanel(); * ButtonGroup group = new ButtonGroup(); * * JRadioButton rb1 = new JRadioButton("One"); * panel.add(rb1); * group.add(rb1); * JRadioButton rb2 = new JRadioButton("Two"); * panel.add(rb2); * group.add(rb2); * JRadioButton rb3 = new JRadioButton("Three"); * panel.add(rb3); * group.add(rb3); * * rb1.setSelected(true); * frame.add(panel); * * frame.setSize(200, 200); * frame.setLocationRelativeTo(null); * frame.setVisible(true); * } * }); * } * } ** * @author Alexander Potochkin * * https://swinghelper.dev.java.net/ * http://weblogs.java.net/blog/alexfromsun/ */ public class JXButtonPanel extends JPanel { private boolean isCyclic; private boolean isGroupSelectionFollowFocus; /** * {@inheritDoc} */ public JXButtonPanel() { super(); init(); } /** * {@inheritDoc} */ public JXButtonPanel(LayoutManager layout) { super(layout); init(); } /** * {@inheritDoc} */ public JXButtonPanel(boolean isDoubleBuffered) { super(isDoubleBuffered); init(); } /** * {@inheritDoc} */ public JXButtonPanel(LayoutManager layout, boolean isDoubleBuffered) { super(layout, isDoubleBuffered); init(); } private void init() { setFocusTraversalPolicyProvider(true); setFocusTraversalPolicy(new JXButtonPanelFocusTraversalPolicy()); ActionListener actionHandler = new ActionHandler(); registerKeyboardAction(actionHandler, ActionHandler.FORWARD, KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); registerKeyboardAction(actionHandler, ActionHandler.FORWARD, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); registerKeyboardAction(actionHandler, ActionHandler.BACKWARD, KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); registerKeyboardAction(actionHandler, ActionHandler.BACKWARD, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); setGroupSelectionFollowFocus(true); } /** * Returns whether arrow keys should support * cyclic focus traversal ordering for for this JXButtonPanel. */ public boolean isCyclic() { return isCyclic; } /** * Sets whether arrow keys should support * cyclic focus traversal ordering for this JXButtonPanel. */ public void setCyclic(boolean isCyclic) { this.isCyclic = isCyclic; } /** * Returns whether arrow keys should transfer button's * selection as well as focus for this JXButtonPanel.
* * Note: this property affects buttons which are added to a ButtonGroup */ public boolean isGroupSelectionFollowFocus() { return isGroupSelectionFollowFocus; } /** * Sets whether arrow keys should transfer button's * selection as well as focus for this JXButtonPanel.
* * Note: this property affects buttons which are added to a ButtonGroup */ public void setGroupSelectionFollowFocus(boolean groupSelectionFollowFocus) { isGroupSelectionFollowFocus = groupSelectionFollowFocus; } private static ButtonGroup getButtonGroup(AbstractButton button) { ButtonModel model = button.getModel(); if (model instanceof DefaultButtonModel) { return ((DefaultButtonModel) model).getGroup(); } return null; } private class ActionHandler implements ActionListener { private static final String FORWARD = "moveSelectionForward"; private static final String BACKWARD = "moveSelectionBackward"; public void actionPerformed(ActionEvent e) { FocusTraversalPolicy ftp = JXButtonPanel.this.getFocusTraversalPolicy(); if (ftp instanceof JXButtonPanelFocusTraversalPolicy) { JXButtonPanelFocusTraversalPolicy xftp = (JXButtonPanelFocusTraversalPolicy) ftp; String actionCommand = e.getActionCommand(); Component fo = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); Component next; xftp.setAlternativeFocusMode(true); if (FORWARD.equals(actionCommand)) { next = xftp.getComponentAfter(JXButtonPanel.this, fo); } else if (BACKWARD.equals(actionCommand)) { next = xftp.getComponentBefore(JXButtonPanel.this, fo); } else { throw new AssertionError("Unexpected action command: " + actionCommand); } xftp.setAlternativeFocusMode(false); if (fo instanceof AbstractButton) { AbstractButton b = (AbstractButton) fo; b.getModel().setPressed(false); } if (next != null) { if (fo instanceof AbstractButton && next instanceof AbstractButton) { ButtonGroup group = getButtonGroup((AbstractButton) fo); AbstractButton nextButton = (AbstractButton) next; if (group != getButtonGroup(nextButton)) { return; } if (isGroupSelectionFollowFocus() && group != null && group.getSelection() != null && !nextButton.isSelected()) { nextButton.setSelected(true); } next.requestFocusInWindow(); } } } } } private class JXButtonPanelFocusTraversalPolicy extends LayoutFocusTraversalPolicy { private boolean isAlternativeFocusMode; public boolean isAlternativeFocusMode() { return isAlternativeFocusMode; } public void setAlternativeFocusMode(boolean alternativeFocusMode) { isAlternativeFocusMode = alternativeFocusMode; } protected boolean accept(Component c) { if (!isAlternativeFocusMode() && c instanceof AbstractButton) { AbstractButton button = (AbstractButton) c; ButtonGroup group = JXButtonPanel.getButtonGroup(button); if (group != null && group.getSelection() != null && !button.isSelected()) { return false; } } return super.accept(c); } public Component getComponentAfter(Container aContainer, Component aComponent) { Component componentAfter = super.getComponentAfter(aContainer, aComponent); if (!isAlternativeFocusMode()) { return componentAfter; } if (JXButtonPanel.this.isCyclic()) { return componentAfter == null ? getFirstComponent(aContainer) : componentAfter; } if (aComponent == getLastComponent(aContainer)) { return aComponent; } return componentAfter; } public Component getComponentBefore(Container aContainer, Component aComponent) { Component componentBefore = super.getComponentBefore(aContainer, aComponent); if (!isAlternativeFocusMode()) { return componentBefore; } if (JXButtonPanel.this.isCyclic()) { return componentBefore == null ? getLastComponent(aContainer) : componentBefore; } if (aComponent == getFirstComponent(aContainer)) { return aComponent; } return componentBefore; } } } /** * @author Alexander Potochkin * * https://swinghelper.dev.java.net/ * http://weblogs.java.net/blog/alexfromsun/ */ class JXButtonPanelDemo extends JFrame { private ButtonGroup radioGroup = new ButtonGroup(); public JXButtonPanelDemo() { super("JXButtonPanel demo"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); JPanel topPanel = new JPanel(new GridLayout(1, 0)); final JXButtonPanel radioGroupPanel = createRadioJXButtonPanel(); topPanel.add(radioGroupPanel); final JXButtonPanel checkBoxPanel = createCheckBoxJXButtonPanel(); topPanel.add(checkBoxPanel); add(topPanel); add(createButtonJXButtonPanel(), BorderLayout.SOUTH); pack(); JMenuBar bar = new JMenuBar(); JMenu menu = new JMenu("Options"); JMenuItem item = new JMenuItem("Unselect radioButtons"); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_MASK)); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // hack for 1.5 // in 1.6 ButtonGroup.clearSelection added JRadioButton b = new JRadioButton(); radioGroup.add(b); b.setSelected(true); radioGroup.remove(b); } }); menu.add(item); bar.add(menu); setJMenuBar(bar); setSize(300, 300); setLocationRelativeTo(null); } private JXButtonPanel createRadioJXButtonPanel() { JXButtonPanel panel = new JXButtonPanel(); panel.setLayout(new GridLayout(0, 1)); JRadioButton one = new JRadioButton("One"); panel.add(one); radioGroup.add(one); JRadioButton two = new JRadioButton("Two"); panel.add(two); radioGroup.add(two); JRadioButton three = new JRadioButton("Three"); panel.add(three); radioGroup.add(three); JRadioButton four = new JRadioButton("Four"); panel.add(four); radioGroup.add(four); one.setSelected(true); panel.setBorder(BorderFactory.createTitledBorder("JXButtonPanel")); return panel; } private JXButtonPanel createCheckBoxJXButtonPanel() { JXButtonPanel panel = new JXButtonPanel(); panel.setLayout(new GridLayout(0, 1)); JCheckBox one = new JCheckBox("One"); panel.add(one); JCheckBox two = new JCheckBox("Two"); panel.add(two); JCheckBox three = new JCheckBox("Three"); panel.add(three); JCheckBox four = new JCheckBox("Four"); panel.add(four); panel.setBorder(BorderFactory.createTitledBorder("JXButtonPanel")); return panel; } private JPanel createButtonJXButtonPanel() { JPanel ret = new JPanel(new BorderLayout()); JLabel label = new JLabel("Does JXButtonPanel support arrow keys ?"); label.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); JPanel temp = new JPanel(); temp.add(label); ret.add(temp); JXButtonPanel panel = new JXButtonPanel(); panel.setCyclic(true); panel.add(new JButton("Yes")); panel.add(new JButton("Sure")); panel.add(new JButton("Absolutely !")); panel.setBorder(BorderFactory.createTitledBorder(null, "JXButtonPanel.setCyclic(true)", TitledBorder.CENTER, TitledBorder.BOTTOM)); ret.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); ret.add(panel, BorderLayout.SOUTH); return ret; } public static void main(String[] args) throws Exception { SwingUtilities.invokeLater(new Runnable() { public void run() { new JXButtonPanelDemo().setVisible(true); } }); } }