关注 JButtons 在 Java 中实际上是如何工作的?

Joe*_*ker 5 java swing focus button

我在 Java Swing 中发现了一个奇怪的异常。当用户按下空格键时,按时间顺序添加到 UI 的第一个 JButton 总是会触发,假设他在执行此操作之前没有单击另一个按钮。如果调用getRootPane().setDefaultButton(JButton)和 ,甚至会发生此行为。JButton.requestFocus()当请求 JButton 焦点时,似乎至少有 2 种不同的“焦点”。“焦点”或突出显示之一是按钮上文本周围的虚线矩形,而另一个是指定按钮周围的较粗轮廓。

只要按下空格键,就会触发带有虚线轮廓文本的按钮。每当按下回车键时,带有粗边框的按钮就会触发。

我准备了一个可编译的最小示例来说明这种行为。根本不涉及键映射/绑定。

import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

public class ButtonFocusAnomalyExample extends JFrame {
    public ButtonFocusAnomalyExample() {
        super();
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        int frameWidth = 300;
        int frameHeight = 300;
        setSize(frameWidth, frameHeight);
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int x = (d.width - getSize().width) / 2;
        int y = (d.height - getSize().height) / 2;
        setLocation(x, y);
        setTitle("Any Frame");
        setResizable(false);
        Container cp = getContentPane();
        cp.setLayout(null);
        setVisible(true);
        new DialogMinimal(this, true); // Runs the Dialog
    }

    public static void main(String[] args) {
        new ButtonFocusAnomalyExample();
    }

    static class DialogMinimal extends JDialog {
        private final JTextField output = new JTextField();

        public DialogMinimal(final JFrame owner, final boolean modal) {
            super(owner, modal);
            setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            int frameWidth = 252;
            int frameHeight = 126;
            setSize(frameWidth, frameHeight);
            Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
            int x = (d.width - getSize().width) / 2;
            int y = (d.height - getSize().height) / 2;
            setLocation(x, y);
            setTitle("Minimal Button Focus Example");
            Container cp = getContentPane();
            cp.setLayout(null);
            JButton bYes = new JButton();
            bYes.setBounds(0, 0, 100, 33);
            bYes.setText("Yes (Space)");
            bYes.addActionListener(this::bYes_ActionPerformed);
            JPanel buttonPanel = new JPanel(null, true);
            buttonPanel.add(bYes);
            JButton bNo = new JButton();
            bNo.setBounds(108, 0, 120, 33);
            bNo.setText("No (Enter/Return)");
            getRootPane().setDefaultButton(bNo); // Set "No" as default button
            bNo.requestFocus(); // Get focus on "No" button
            bNo.addActionListener(this::bNo_ActionPerformed);
            buttonPanel.add(bNo);
            buttonPanel.setBounds(8, 8, 400, 92);
            buttonPanel.setOpaque(false);
            cp.add(buttonPanel);
            output.setBounds(8, 50, 220, 32);
            cp.add(output);
            setResizable(false);
            setVisible(true);
        }

        public void bYes_ActionPerformed(final ActionEvent evt) {
            output.setText("Yes"); // Still fires on every space bar press
        }

        public void bNo_ActionPerformed(final ActionEvent evt) {
            output.setText("No"); // Only fires on every return/enter press
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它看起来是这样的:

按钮焦点示例

可执行代码也可以在这里找到。

我现在的问题是:

  1. 这些不同的侧重点是什么?
  2. 如何更改按钮文本周围显示为虚线轮廓的焦点,以便空格键回车键触发“否”按钮的事件?

Joe*_*ker 0

关于问题 1:“聚焦在由虚线轮廓表示的按钮和带有粗连续轮廓的按钮上有什么区别?

答:“焦点”不存在两种。两种方法都按照各自的名称执行操作:

JButton.requestFocus()(更好的是JButton.requestFocusInWindow())请求集中在一个按钮上,同时getRootPane().setDefaultButton(JButton)设置一个选定的按钮,LAF 单独处理该按钮。


关于问题2:“为什么我的具体实现会这样,我怎样才能实现我想要的行为?”

答案:对话的方式是问题所在。setVisible(true)在模态窗口上调用后,您无法请求焦点。

因此,可能的解决方案是:

  1. 创建对话框时将模态设置为 false,例如通过调用而不是and/ornew DialogMinimal(this, false);来获取焦点,但如果对话框必须是模态的,则这不是解决方案。bNo.requestFocusInWindow()getRootPane().setDefaultButton(bNo);bNo.requestFocus();

或者

  1. 根据用户 camickr 的建议,实现RequestFocusListenerDialog Focus中找到的内容。
public DialogMinimal(final JFrame owner, final boolean modal) {
    Button bNo = new JButton();
    [...]
    // bNo.requestFocusInWindow(); // obsolete now
    getRootPane().setDefaultButton(bNo); // To fire on enter key
    bNo.addAncestorListener(new RequestFocusListener()); // To fire on space bar
    [...]
}

Run Code Online (Sandbox Code Playgroud)