6

是否可以为 JOptionPane 对话框中的按钮分配热键和助记符?我希望能够在 JOptionPane 生成的带有选项 Yes、No 和 Cancel 的消息对话框中,按 Y 击中 Yes 按钮,N 击中 No 按钮并退出以激活退出按钮。同样,在带有“确定”和“取消”按钮的对话框中,我希望能够通过 Enter 和 Escape 激活它们。

我已经尝试将 JButtons 传递到 JOptionPane 的按钮对象数组中,并设置了 Mnemonics。助记符起作用,并且按钮在对话框中正确显示,但是,当它们被激活时,它们不能正常工作。最明显的是他们没有处理对话框。

将热键和助记符添加到 JOptionPane 对话框的按钮的正确方法是什么?

4

4 回答 4

2

您可以创建您的JOptionPane, 然后循环浏览窗格的组件(子项等),检查是否有任何组件instanceof JButton,如果是,请检查文本,并设置正确的助记符。

JOptionPane p = new JOptionPane();
Component[] c = p.getComponents();
于 2009-10-01T20:00:41.653 回答
2

使用 UIManager 如下:

UIManager.put("OptionPane.okButtonMnemonic", "79");  // for Setting 'O' as mnemonic
UIManager.put("OptionPane.cancelButtonMnemonic", "67"); // for Setting 'C' as mnemonic
于 2012-08-15T22:46:25.033 回答
1

您可以通过 Swing Worker 查找在父窗口中打开的新窗口。然后检查它是否是一个JDialog并提取按钮区域。然后将键分配给按钮。这适用于 JOptionPane 的静态方法。

这是 Swing Worker 类 - 只需实例化它并在调用之前执行它以显示 JOptionPane:

import java.awt.Component;
import java.awt.Window;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.swing.FocusManager;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class HotKeyWorker extends SwingWorker<JComponent, Integer>
{
    private static final long TIMEOUT = 30000; //Don't wait forever for the JOptionPane
    private final String buttonAreaName;
    private final Map<String, ButtonData> buttonDataMap;
    private final Window owner;
    
    public HotKeyWorker(Component owner, Map<String, ButtonData> buttonDataMap)
    {
        this.buttonDataMap = buttonDataMap;
        if(owner instanceof Window)
            this.owner = (Window)owner;
        else if(owner != null)
            this.owner = SwingUtilities.windowForComponent(owner);
        else
            this.owner = null;
        buttonAreaName = getButtonAreaName();
    }

    @Override
    public JComponent doInBackground()
    {
        if(owner == null) return null;
        if(buttonAreaName == null) return null;
        long timeout = System.currentTimeMillis() + TIMEOUT;
        Window dialog = null;
        while(dialog == null && System.currentTimeMillis() < timeout)
        {
            dialog = FocusManager.getCurrentManager().getFocusedWindow();
            if(dialog != null)
                if(dialog.getOwner() != owner) 
                    dialog = null;
        }
        if(dialog instanceof JDialog)
            return getButtonArea(((JDialog)dialog).getRootPane());
        return null;
    }

    @Override
    public void done()
    {
        try
        {
            JComponent buttonArea = get();
            if(buttonArea != null)
                for(Component c : buttonArea.getComponents())
                    if(c instanceof JButton)
                        setHotKey((JButton)c);

        }
        catch(InterruptedException | ExecutionException ex) { /* Failed */ } 
    }

    private JComponent getButtonArea(JComponent component)
    {
        JComponent result = null;
        if(component.getName() != null)
            if(component.getName().equals(buttonAreaName) && component.getParent() instanceof JOptionPane)
                return component;
        for(Component c : component.getComponents())
            if(c instanceof JComponent)
                if((result = getButtonArea((JComponent)c)) != null)
                    return result;
        return result;
    }

    private void setHotKey(JButton button)
    {
        if(button.getText().isEmpty()) return;
        ButtonData data = buttonDataMap.get(button.getText());
        if(data == null) return;
        button.setText(data.updatedButtonText);
        button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(data.hotKeyCode, 0), data.actionKey);
        button.getActionMap().put(data.actionKey, new ButtonPress(button));
    }
    
    private String getButtonAreaName()
    {
        JButton trace = new JButton();
        Object[] options = { trace };
        JOptionPane temp = new JOptionPane();
        temp.setOptions(options);
        Component buttonArea = trace.getParent();
        if(buttonArea != null)
            return buttonArea.getName();
        return null;
    }
}

这些是使事情变得更清洁的两个帮助类:

public class ButtonData
{
    public String updatedButtonText;
    public int hotKeyCode;
    public String actionKey;
    public ButtonData(String updatedButtonText, int hotKeyCode, String actionKey)
    {
        this.updatedButtonText = updatedButtonText;
        this.hotKeyCode = hotKeyCode;
        this.actionKey = actionKey;
    }
}

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;

public class ButtonPress extends AbstractAction
{
    private final JButton button;
    public ButtonPress(JButton button) { this.button = button; }
    @Override
    public void actionPerformed(ActionEvent event) { button.doClick(); }
}

这是你将如何使用它:

ButtonData yesData = new ButtonData("<html><span style=\"color:Blue;\">Y</span>es</html>", KeyEvent.VK_Y, "yes");
ButtonData noData = new ButtonData("<html><span style=\"color:Blue;\">N</span>o</html>", KeyEvent.VK_N, "no");
ButtonData cancelData = new ButtonData("<html><span style=\"color:Blue;\">C</span>ancel</html>", KeyEvent.VK_C, "cancel");
Map<String, ButtonData> map = new HashMap<>();
map.put("Yes", yesData);
map.put("No", noData);
map.put("Cancel", cancelData);
HotKeyWorker worker = new HotKeyWorker(component, map);
worker.execute();
int result = JOptionPane.showConfirmDialog(component, "Just a text", "Confirm", JOptionPane.YES_NO_CANCEL_OPTION);

这是一个工作示例:

import javax.swing.JComponent;
import java.awt.Window;
import javax.swing.FocusManager;
import javax.swing.JDialog;
import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
import javax.swing.AbstractAction;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.KeyStroke;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;

public class Test 
{
    public static void main(String[] args) 
    {
        Frame frame = new Frame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
    
    private static class Frame extends JFrame
    {
        public Frame()
        {
            init();
        }

        private void init()
        {
            setSize(300,100);
            setTitle("Hot Key Test");
            add(new Panel());
        }

        private class Panel extends JPanel
        {
            private final JButton testButton;
            private final JLabel resultLabel;
            public Panel()
            {
                String testKey = "test";
                testButton = new JButton("<html><span style=\"color:Blue;\">T</span>est</html>");
                testButton.addActionListener((event) -> { test(this); });
                testButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0), testKey);
                testButton.getActionMap().put(testKey, new ButtonPress(testButton));
                resultLabel = new JLabel("No Result");
                init();
            }

            private void init()
            {
                add(testButton);
                add(resultLabel);
            }

            private void test(Component component)
            {
                String anotherTestKey = "Test";
                JButton anotherTestButton = new JButton("<html><span style=\"color:Blue;\">T</span>est</html>");
                anotherTestButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0), anotherTestKey);
                anotherTestButton.getActionMap().put(anotherTestKey, new ButtonPress(anotherTestButton));
                JLabel anotherTestLabel = new JLabel("No Result has been selected");
                JPanel testPanel = new JPanel();
                testPanel.add(anotherTestButton);
                testPanel.add(anotherTestLabel);
                anotherTestButton.addActionListener((event) -> { anotherTest(testPanel, anotherTestLabel); });


                ButtonData yesData = new ButtonData("<html><span style=\"color:Blue;\">Y</span>es</html>", KeyEvent.VK_Y, "yes");
                ButtonData noData = new ButtonData("<html><span style=\"color:Blue;\">N</span>o</html>", KeyEvent.VK_N, "no");
                ButtonData cancelData = new ButtonData("<html><span style=\"color:Blue;\">C</span>ancel</html>", KeyEvent.VK_C, "cancel");
                Map<String, ButtonData> map = new HashMap<>();
                map.put("Yes", yesData);
                map.put("No", noData);
                map.put("Cancel", cancelData);
                HotKeyWorker worker = new HotKeyWorker(component, map);
                worker.execute();
                int result = JOptionPane.showConfirmDialog(component, testPanel, "Confirm", JOptionPane.YES_NO_CANCEL_OPTION);
                switch(result)
                {
                    case 0 : resultLabel.setText("Yes Pressed"); break;
                    case 1 : resultLabel.setText("No Pressed"); break;
                    case 2 : resultLabel.setText("Cancel Pressed"); break;
                    default: resultLabel.setText("OptionPane Closed");
                }
            }

            public void anotherTest(Component component, JLabel label)
            {
                ButtonData fredData = new ButtonData("<html><span style=\"color:Blue;\">F</span>red</html>", KeyEvent.VK_F, "fred");
                ButtonData wilmaData = new ButtonData("<html><span style=\"color:Blue;\">W</span>ilma</html>", KeyEvent.VK_W, "wilma");
                ButtonData barneyData = new ButtonData("<html>B<span style=\"color:Blue;\">a</span>rney</html>", KeyEvent.VK_A, "barney");
                ButtonData bettyData = new ButtonData("<html>B<span style=\"color:Blue;\">e</span>tty</html>", KeyEvent.VK_E, "betty");
                Map<String, ButtonData> map = new HashMap<>();
                map.put("Fred", fredData);
                map.put("Wilma", wilmaData);
                map.put("Barney", barneyData);
                map.put("Betty", bettyData);
                HotKeyWorker worker = new HotKeyWorker(component, map);
                worker.execute();

                String[] options = {"Fred", "Wilma", "Barney", "Betty" };
                int result = JOptionPane.showOptionDialog(component, "Who do you like?", "Confirm", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, 0);
                switch(result)
                {
                    case 0 : label.setText("I like Fred"); break;
                    case 1 : label.setText("I like Wilma"); break;
                    case 2 : label.setText("I like Barney"); break;
                    case 3 : label.setText("I like Betty"); break;
                    default: label.setText("I decline to answer");
                }
            }
            
            private class HotKeyWorker extends SwingWorker<JComponent, Integer>
            {
                private static final long TIMEOUT = 30000; //Don't wait forever for the JOptionPane
                private final String buttonAreaName;
                private final Map<String, ButtonData> buttonDataMap;
                private final Window owner;

                public HotKeyWorker(Component owner, Map<String, ButtonData> buttonDataMap)
                {
                    this.buttonDataMap = buttonDataMap;
                    if(owner instanceof Window)
                        this.owner = (Window)owner;
                    else if(owner != null)
                        this.owner = SwingUtilities.windowForComponent(owner);
                    else
                        this.owner = null;
                    buttonAreaName = getButtonAreaName();
                }

                @Override
                public JComponent doInBackground()
                {
                    if(owner == null) return null;
                    if(buttonAreaName == null) return null;
                    long timeout = System.currentTimeMillis() + TIMEOUT;
                    Window dialog = null;
                    while(dialog == null && System.currentTimeMillis() < timeout)
                    {
                        dialog = FocusManager.getCurrentManager().getFocusedWindow();
                        if(dialog != null)
                            if(dialog.getOwner() != owner) 
                                dialog = null;
                    }
                    if(dialog instanceof JDialog)
                        return getButtonArea(((JDialog)dialog).getRootPane());
                    return null;
                }

                @Override
                public void done()
                {
                    try
                    {
                        JComponent buttonArea = get();
                        if(buttonArea != null)
                            for(Component c : buttonArea.getComponents())
                                if(c instanceof JButton)
                                    setHotKey((JButton)c);

                    }
                    catch(InterruptedException | ExecutionException ex) { /* Failed */ } 
                }

                private JComponent getButtonArea(JComponent component)
                {
                    JComponent result = null;
                    if(component.getName() != null)
                        if(component.getName().equals(buttonAreaName) && component.getParent() instanceof JOptionPane)
                            return component;
                    for(Component c : component.getComponents())
                        if(c instanceof JComponent)
                            if((result = getButtonArea((JComponent)c)) != null)
                                return result;
                    return result;
                }

                private void setHotKey(JButton button)
                {
                    if(button.getText().isEmpty()) return;
                    ButtonData data = buttonDataMap.get(button.getText());
                    if(data == null) return;
                    button.setText(data.updatedButtonText);
                    button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(data.hotKeyCode, 0), data.actionKey);
                    button.getActionMap().put(data.actionKey, new ButtonPress(button));
                }

                private String getButtonAreaName()
                {
                    JButton trace = new JButton();
                    Object[] options = { trace };
                    JOptionPane temp = new JOptionPane();
                    temp.setOptions(options);
                    Component buttonArea = trace.getParent();
                    if(buttonArea != null)
                        return buttonArea.getName();
                    return null;
                }
            }
            
            private class ButtonData
            {
                public String updatedButtonText;
                public int hotKeyCode;
                public String actionKey;
                public ButtonData(String updatedButtonText, int hotKeyCode, String actionKey)
                {
                    this.updatedButtonText = updatedButtonText;
                    this.hotKeyCode = hotKeyCode;
                    this.actionKey = actionKey;
                }
            }
            
            private class ButtonPress extends AbstractAction
            {
                private final JButton button;
                public ButtonPress(JButton button) { this.button = button; }
                @Override
                public void actionPerformed(ActionEvent event) { button.doClick(); }
            }
        }
    }
}

这适用于所有 JOptionPane 静态方法。只要您不在父组件的窗口中随机弹出窗口,这将正常工作。注意:JOptionPane 的父组件不能为空。


当然,仅仅实例化一个 JOptionPane 并自定义它可能更容易。以下是执行此操作的类:

import java.awt.Component;
import javax.swing.Icon;
import javax.swing.JOptionPane;
import javax.swing.JDialog;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.AbstractAction;
import javax.swing.Action;
import java.awt.event.ActionEvent;

public class HotKeyOptionPane
{
    public static int showOptionDialog(Component parentComponent,
                                    Object message, String title,
                                    int optionType, int messageType,
                                    Icon icon, HotKey[] options,
                                    Object initialValue)
    {
        JButton[] buttons = new JButton[options.length];
        for(int i = 0; i < options.length; i++)
            buttons[i] = new JButton(options[i].text);
        JOptionPane pane = new JOptionPane(message, messageType, optionType, icon, buttons, initialValue);
        for(int option = 0; option < buttons.length; option++)
            setButtonAction(buttons[option], options[option].keyCode, option, pane);
        JDialog dialog = pane.createDialog(parentComponent, title);
        dialog.setVisible(true);
 
        if (pane.getValue() instanceof Integer)
            return (Integer)pane.getValue();
        return -1;   
    }
    
    private static void setButtonAction(JButton button, int hotKey, Integer option, JOptionPane pane)
    {
        Action action = new AbstractAction()
        {
            @Override
            public void actionPerformed(ActionEvent event)
            {
                pane.setValue(option);
                pane.firePropertyChange(JOptionPane.VALUE_PROPERTY, JOptionPane.DEFAULT_OPTION, option);    
            }
        };
        button.addActionListener(action);
        button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(hotKey, 0), button.getText());
        button.getActionMap().put(button.getText(), action);
    }      
}

public class HotKey 
{
    public String text;
    public int keyCode;
    public HotKey(String text, int keyCode)
    {
        this.text = text;
        this.keyCode = keyCode;
    }
}

这就是你将如何使用它们:

import javax.swing.JComponent;
import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import javax.swing.JLabel;
import java.awt.event.KeyEvent;
import javax.swing.KeyStroke;

public class Test 
{
    public static void main(String[] args) 
    {
        Frame frame = new Frame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
    
    private static class Frame extends JFrame
    {
        public Frame()
        {
            init();
        }

        private void init()
        {
            setSize(300,100);
            setTitle("Hot Key Test");
            add(new Panel());
        }

        private class Panel extends JPanel
        {
            private final JButton testButton;
            private final JLabel resultLabel;
            public Panel()
            {
                String testKey = "test";
                testButton = new JButton("<html><span style=\"color:Blue;\">T</span>est</html>");
                testButton.addActionListener((event) -> { test(this); });
                testButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0), testKey);
                testButton.getActionMap().put(testKey, new ButtonPress(testButton));
                resultLabel = new JLabel("No Result");
                init();
            }

            private void init()
            {
                add(testButton);
                add(resultLabel);
            }
            
            private void test(Component component)
            {
                HotKey yesOption = new HotKey("<html><span style=\"color:Blue;\">Y</span>es</html>", KeyEvent.VK_Y);
                HotKey noOption = new HotKey("<html><span style=\"color:Blue;\">N</span>o</html>", KeyEvent.VK_N);
                HotKey cancelOption = new HotKey("<html><span style=\"color:Blue;\">C</span>ancel</html>", KeyEvent.VK_C);
                HotKey[] options = { yesOption, noOption, cancelOption };
                int result = HotKeyOptionPane.showOptionDialog(component, "Just a test", "Testing", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, 0);
                switch(result)
                {
                    case 0 : resultLabel.setText("Yes Pressed"); break;
                    case 1 : resultLabel.setText("No Pressed"); break;
                    case 2 : resultLabel.setText("Cancel Pressed"); break;
                    default: resultLabel.setText("OptionPane Closed");
                }
            }
        }
    }
}

“就数学定律而言,就现实而言,它们是不确定的,就它们而言,它们是确定的,它们并不与现实有关。”

艾尔伯特爱因斯坦

于 2020-07-14T09:28:58.590 回答
0

将按钮作为参数而不是字符串发送

    JButton button1 = new JButton( "<html>" + nextQuestion1 + "</html>");
    button1.setMnemonic('a');
    JButton button2 = new JButton(nextQuestion2 + "VUHU");
    JButton button3 = new JButton(abort);
    Object[] possibleValues = new Object[]{button1,button2,button3};
    int selectedValue = showOptionDialog(owner, question, possibleValues);
于 2016-12-08T11:24:00.090 回答