为什么在使用 Enter 键而不是单击时所有操作按钮功能都不起作用?

Why not all of the action button functionality work when using Enter key instead of clicking?

我的操作按钮读取一个 JTextField 值并使用原始值中仅包含的 数字 更新同一字段。它还自动将数字复制到剪贴板并使 JLabel 可见,让用户知道它已被复制。一旦用户在文本字段中再次键入,此标签就会再次消失。

示例:用户输入 "abc123cde456" 并单击操作按钮。输出是“123456”。显示一个标签,告知值已被复制。

我已使用 getRootPane().setDefaultButton(button) 将此按钮设置为默认按钮,因此用户可以使用 Enter 键触发它。问题是,当使用键而不是鼠标单击时,输出和复制到剪贴板工作,但 JLabel 不可见。

我注意到当我在按下键之前将焦点从 JTextField 上移开时,Enter 键可以正常工作,但在我的布局中做到这一点的唯一方法是在按钮顶部按住鼠标并单击 "drag it" 外面,所以焦点转到按钮而不是 JTextField,像这样:

passing focus to button before hitting Enter key

下面的代码 - 顺便说一下,我正在使用 NetBeans IDE:

JFrame class

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;

public class FrameZ extends javax.swing.JFrame {

    public FrameZ() {
        initComponents();
        labelCopied.setVisible(false);
        inputTxt.addActionListener(actionButton.getActionListeners()[0]);
        getRootPane().setDefaultButton(actionButton);
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        inputTxt = new javax.swing.JTextField();
        actionButton = new javax.swing.JButton();
        labelCopied = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        inputTxt.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyTyped(java.awt.event.KeyEvent evt) {
                inputTxtKeyTyped(evt);
            }
        });

        actionButton.setText("Only numbers");
        actionButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                actionButtonActionPerformed(evt);
            }
        });

        labelCopied.setText("Copied!");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGap(0, 0, Short.MAX_VALUE)
                        .addComponent(labelCopied)
                        .addGap(72, 72, 72))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(inputTxt, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 18, Short.MAX_VALUE)
                        .addComponent(actionButton)
                        .addContainerGap())))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(actionButton)
                    .addComponent(inputTxt, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(labelCopied)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>                        

    private void actionButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             

        String inputValue = inputTxt.getText();
        StringBuilder digitsOnly = new StringBuilder();

        for (int i = 0; i < inputValue.length(); i++) {
            char c = inputValue.charAt(i);
            if (Character.isDigit(c)) {
                digitsOnly.append(c);
            }
        }

        inputTxt.setText(digitsOnly.toString());

        //copying to clipboard:
        StringSelection strSelect = new StringSelection(inputTxt.getText());
        Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard();
        clpbrd.setContents(strSelect, null);

        labelCopied.setVisible(true);
    }                                            

    private void inputTxtKeyTyped(java.awt.event.KeyEvent evt) {                                  
            labelCopied.setVisible(false);
    }                                 

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new FrameZ().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JButton actionButton;
    private javax.swing.JTextField inputTxt;
    private javax.swing.JLabel labelCopied;
    }

主要class

    public class Main {
    public static void main(String args[]){
        FrameZ frm = new FrameZ();

        frm.setVisible(true);
        frm.setResizable(false);
        frm.setLocationRelativeTo(null);
    }
}

要解决问题,您需要:

  1. 使用调试器单步执行代码
  2. 将 System.out.println(...) 语句添加到您的代码中

以上任何一个都可以帮助您理解逻辑流程,看看它是否是您期望的。

我不使用 IDE 所以我做了以下更改以添加一些调试代码:

private void actionButtonActionPerformed(java.awt.event.ActionEvent evt) {
System.out.println("action");

private void inputTxtKeyTyped(java.awt.event.KeyEvent evt) {
    System.out.println("hide");

如果您进行这些更改并在文本字段中键入数据并使用 Enter 键,您将看到如下输出:

hide
hide
hide
hide
action
hide

为什么会这样?好吧,看来 Enter 键会生成一个 KeyTyped 事件,并且该事件在 ActionEvent

之后处理

解决此问题的一种方法是将使标签可见的逻辑包装在 SwingUtilities.invokeLater(...) 中。这将导致代码被添加到事件队列的末尾,因此它将在 Enter 键生成的事件之后执行:

    //labelCopied.setVisible(true);
    SwingUtilities.invokeLater(new Runnable()
    {
        public void run()
        {
            labelCopied.setVisible(true);
        }
    });