java - 如何使 JRadioButton 在通过向 showMessageDialog 传递方法创建的弹出表单中具有焦点?

标签 java swing focus traversal

已编辑--提供所需类(class)

编辑#2 - 所需的类(class)来自 here因此不需要调试,尽管我确实将 Vector 更改为 ArrayList

enter image description here

我通过使用 showMessageDialog 并将方法 (popup) 传递给 Object message 参数创建了此弹出(模式)表单。我添加了遍历策略,扩展为允许光标键移动焦点,除了以下情况外,它的效果很好。

我唯一解决不了的问题是如何让光标键立即起作用。 “确定”按钮最初具有焦点,唯一可以移动焦点的键是 TAB。一旦 JRadioButton 获得焦点,光标键就可以很好地移动焦点。

// LOSE THIS!! import Main.MyFocusTraversalPolicy;
import java.awt.GridLayout;
import java.awt.KeyboardFocusManager;
import java.awt.event.*;
import static java.awt.event.KeyEvent.*;
import java.util.HashSet;
import java.util.Set;
import javax.swing.*;

public class NewMain {
  static JPanel pnlPopup;
  static MyFocusTraversalPolicy ftPolicy;
  static JRadioButton rbtAsShown    = new JRadioButton();
  static JRadioButton rbtRandomly   = new JRadioButton();
  static JRadioButton rbtUser       = new JRadioButton();
  static JLabel lblAvailableDefinitionMethod = new JLabel();

  private static int popup() {
    pnlPopup = new JPanel(new GridLayout(4, 1));
    ButtonGroup buttonGroup1  = new ButtonGroup();
    buttonGroup1.add(rbtAsShown);
    rbtAsShown.setSelected(false);
    rbtRandomly.setSelected(true);
    rbtUser.setSelected(false);
    rbtAsShown.setText("As already shown above");

    rbtAsShown.addFocusListener(new FocusAdapter(){
      public void focusGained(FocusEvent e){
        rbtAsShown.setSelected(true);
      }});

    rbtRandomly.addFocusListener(new FocusAdapter(){
      public void focusGained(FocusEvent e){
        rbtRandomly.setSelected(true);
      }});

    rbtUser.addFocusListener(new FocusAdapter(){
      public void focusGained(FocusEvent e){
        rbtUser.setSelected(true);
      }});

    rbtAsShown.addItemListener(new ItemListener(){
      public void itemStateChanged(ItemEvent evt){
        rbtRandomly.setSelected(false);
        rbtUser.setSelected(false);
      }});

    buttonGroup1.add(rbtRandomly);
    rbtRandomly.setText("Randomly");

    rbtRandomly.addItemListener(new ItemListener(){
      public void itemStateChanged(ItemEvent evt){
        rbtAsShown.setSelected(false);
        rbtUser.setSelected(false);
      }});

    buttonGroup1.add(rbtUser);
    rbtUser.setText("I'll type them");
    rbtUser.addItemListener(new java.awt.event.ItemListener(){
      public void itemStateChanged(java.awt.event.ItemEvent evt){
        rbtRandomly.setSelected(false);
        rbtAsShown.setSelected(false);
      }});

    lblAvailableDefinitionMethod.setText("How should 'Available letters' be defined?");
    rbtUser.setMnemonic(VK_I);
    rbtRandomly.setMnemonic(VK_R);
    rbtAsShown.setMnemonic(VK_A);
    pnlPopup.add(lblAvailableDefinitionMethod);
    pnlPopup.add(rbtRandomly);
    pnlPopup.add(rbtUser);
    pnlPopup.add(rbtAsShown);

    Set downKeys;
    downKeys = pnlPopup.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
    Set newDownKeys = new HashSet(downKeys);
    newDownKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0));
    pnlPopup.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, newDownKeys);    

    Set upKeys;
    upKeys = pnlPopup.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
    Set newUpKeys = new HashSet(upKeys);
    newUpKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0));
    pnlPopup.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, newUpKeys);    

    pnlPopup.setFocusTraversalPolicy(ftPolicy);
    rbtRandomly.requestFocusInWindow();
    pnlPopup.setFocusCycleRoot(true);

    return JOptionPane.showConfirmDialog(null, 
                                         pnlPopup, 
                                         "Define Available letters", 
                                         JOptionPane.OK_CANCEL_OPTION, 
                                         JOptionPane.PLAIN_MESSAGE);
  }

  public static void main(String[] args){
    if(popup() == -1) JOptionPane.showMessageDialog(null,"Cancelled");
    else              JOptionPane.showMessageDialog(null,"OK");
  }
}

这是类MyFocusTraversalPolicy。抱歉我忘了。

import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.util.ArrayList;

    public class MyFocusTraversalPolicy extends FocusTraversalPolicy
    {
        ArrayList<Component> order;  // class var 

        public MyFocusTraversalPolicy(ArrayList<Component> o) {
            this.order = new ArrayList<>            (o.size());
            this.order.addAll                       (o);
        }
        public Component getComponentAfter(Container focusCycleRoot,
                                           Component aComponent)
        {
            int idx = (order.indexOf(aComponent) + 1) % order.size();
            return order.get(idx);
        }

        public Component getComponentBefore(Container focusCycleRoot,
                                            Component aComponent)
        {
            int idx = order.indexOf(aComponent) - 1;
            if (idx < 0) {
                idx = order.size() - 1;
            }
            return order.get(idx);
        }

        public Component getDefaultComponent(Container focusCycleRoot) {
            return order.get(0);
        }

        public Component getLastComponent(Container focusCycleRoot) {
            return order.get(order.size());
        }

        public Component getFirstComponent(Container focusCycleRoot) {
            return order.get(0);
        }
    }

由于 showOptionDialog 的工作原理,我认为 rbtRandomly.requestFocusInWindow(); 不会将焦点赋予按钮 rbtRandomly

如何才能使光标键在表单弹出时立即在按钮之间移动光标?

附注 我使用弹出表单是因为我需要 OKCancel 按钮来确认用户选择或希望不继续。我想要一个带有按钮的弹出表单,否则 UI 看起来和感觉都很粗糙。

enter image description here

最佳答案

当 @Aqua 用链接发表评论时,我几乎已经解决了我的问题。 “按照我的方式”做这件事需要做更多的工作,所以我打算点击链接。以下是生成此窗口的方法,其中光标键在 3 个按钮之间移动焦点,并且还有一个好处是让 EscEnter 执行Cancel code> 和 OK 操作。它还基于 @HovercraftFullOfEels here 提供的答案,建议使用 JDialog 而不是 JOptionPane.showMessageDialog

enter image description here

  static JDialog dialog = null;
  static boolean retval ;
  static MyFocusTraversalPolicy ftPolicy;
  static JRadioButton rbtAsShown    = new JRadioButton();
  static JRadioButton rbtRandomly   = new JRadioButton();
  static JRadioButton rbtUser       = new JRadioButton();

  private static boolean popup() {
    JLabel lblAvailableDefinitionMethod = new JLabel();
    KeyBoundButton btnOK;
    JPanel pnlPopup;
    ButtonGroup buttonGroup1  = new ButtonGroup();
    buttonGroup1.add(rbtAsShown);
    rbtAsShown.setSelected(false);
    rbtRandomly.setSelected(false);
    rbtUser.setSelected(false);
    rbtAsShown.setText("As already shown above");

    rbtAsShown.addFocusListener(new FocusAdapter(){
      public void focusGained(FocusEvent e){
        rbtAsShown.setSelected(true);
      }});

    rbtRandomly.addFocusListener(new FocusAdapter(){
      public void focusGained(FocusEvent e){
        rbtRandomly.setSelected(true);
      }});

    rbtUser.addFocusListener(new FocusAdapter(){
      public void focusGained(FocusEvent e){
        rbtUser.setSelected(true);
      }});

    rbtAsShown.addItemListener(new ItemListener(){
      public void itemStateChanged(ItemEvent evt){
        rbtRandomly.setSelected(false);
        rbtUser.setSelected(false);
      }});

    buttonGroup1.add(rbtRandomly);
    rbtRandomly.setText("Randomly");

    rbtRandomly.addItemListener(new ItemListener(){
      public void itemStateChanged(ItemEvent evt){
        rbtAsShown.setSelected(false);
        rbtUser.setSelected(false);
      }});

    buttonGroup1.add(rbtUser);
    rbtUser.setText("I'll type them");
    rbtUser.addItemListener(new java.awt.event.ItemListener(){
      public void itemStateChanged(java.awt.event.ItemEvent evt){
        rbtRandomly.setSelected(false);
        rbtAsShown.setSelected(false);
      }});

    lblAvailableDefinitionMethod.setText("How should 'Available letters' be defined?");
    rbtUser.setMnemonic(VK_I);
    rbtRandomly.setMnemonic(VK_R);
    rbtAsShown.setMnemonic(VK_A);

    btnOK = new KeyBoundButton("OK", VK_ENTER, 0){
      @Override public void action(ActionEvent e){
        retval = true;
        dialog.dispose();
      }};

    KeyBoundButton btnCancel = new KeyBoundButton("Cancel", VK_ESCAPE, 0){
      @Override public void action(ActionEvent e){
        retval = false;
        dialog.dispose();
      }};

    pnlPopup = new JPanel(new GridBagLayout());
    pnlPopup.add(lblAvailableDefinitionMethod, new GBConstraints(0,0));
    pnlPopup.add(rbtRandomly, new GBConstraints(0,1));
    pnlPopup.add(rbtUser,     new GBConstraints(0,2));
    pnlPopup.add(rbtAsShown,  new GBConstraints(0,3));
    pnlPopup.add(btnOK,       new GBConstraints(0,4).anchor(WEST));
    pnlPopup.add(btnCancel,   new GBConstraints(0,4).anchor(EAST));

    Set downKeys; 
    downKeys = pnlPopup.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
    Set newDownKeys = new HashSet(downKeys);
    newDownKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0));
    pnlPopup.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, newDownKeys);    

    Set upKeys;
    upKeys = pnlPopup.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
    Set newUpKeys = new HashSet(upKeys);
    newUpKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0));
    pnlPopup.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, newUpKeys);    

    ArrayList<Component> order1 = new ArrayList<>(3);
      order1.add(rbtRandomly);
      order1.add(rbtUser);
      order1.add(rbtAsShown);
    ftPolicy = new MyFocusTraversalPolicy(order1);    
    pnlPopup.setFocusTraversalPolicy(ftPolicy);
    pnlPopup.setFocusCycleRoot(true);
    dialog = new JDialog(null, "Dialog Title", Dialog.ModalityType.APPLICATION_MODAL);
    dialog.getContentPane().add(pnlPopup);
    dialog.pack();
    dialog.setLocationRelativeTo(null);
    dialog.setVisible(true);    
    return retval && (rbtAsShown.isSelected() || rbtUser.isSelected() || rbtRandomly.isSelected());
  }

  public static void main(String[] args){
    if(popup()){
      if(rbtAsShown.isSelected()) JOptionPane.showMessageDialog(null,"As shown");
      if(rbtRandomly.isSelected())JOptionPane.showMessageDialog(null,"Random");
      if(rbtUser.isSelected())    JOptionPane.showMessageDialog(null,"User");
    }
    else
      JOptionPane.showMessageDialog(null,"Cancelled");
  }

这里是KeyBoundButton,它将转义映射为取消,输入映射为确定。

import java.awt.event.ActionEvent;
import javax.swing.*;
import static javax.swing.KeyStroke.getKeyStroke;

public abstract class KeyBoundButton extends JButton{

  public abstract void action(ActionEvent e);

  public KeyBoundButton(String buttonText, char acc, int key, int mask){
    Action myAction = new AbstractAction()
    {
      @Override public void actionPerformed(ActionEvent e)
      {
        action(e);
      }
    };  

    setAction(myAction);
    setText(buttonText);
    setMnemonic(acc);
    getInputMap(WHEN_IN_FOCUSED_WINDOW)
                  .put(getKeyStroke(key, mask),buttonText);
    getActionMap().put(                        buttonText, myAction);

  }

  public KeyBoundButton(String buttonText, int key, int mask)
  {
    this(buttonText, buttonText.charAt(0), key, mask );
  }
}

关于java - 如何使 JRadioButton 在通过向 showMessageDialog 传递方法创建的弹出表单中具有焦点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29658650/

相关文章:

android - 如何检测任何 subview 何时收到点击

java - 如何组织辅助函数

java - TestNG 测试多个类别中的优先级

java - OFFSET 在带有可变参数的 repaint() 方法中起什么作用?

java - 将 awt/Cursor 转换为 JavaFX 场景/Cursor?

CSS 不同的链接样式

java - 使用 Spring-Factory 创建 ENUM bean,但从未调用 @PostConstruct

java - 如何搜索存储在 hashmap 中的匹配对?

Java:用于加密的更轻代码

android - ListView 中有多个 EditText,点击聚焦一个 EditText,焦点跳转到第一个