简体   繁体   中英

Editable JComboBox in Java 8 does not forward Enter key to default button

I have a editable JComboBox, with focus on this combobox, when I press Enter key, the behavior differs between Java 8 compared to older versions.

This code works as expected in Java 7 and below, but not in Java 8 (tested in Oracle JVM in Mac):

package org.wiztools.comboboxdefaultbutton;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

/**
 *
 * @author subhash
 */
public class MyFrame extends JFrame {

    private static MyFrame me;

    public MyFrame() {
        Container c = getContentPane();
        c.setLayout(new BorderLayout());

        // Press Enter key with focus on this component:
        JComboBox jcb = new JComboBox(new String[]{"Option: One", "Option: Two"});
        jcb.setEditable(true);
        c.add(jcb, BorderLayout.CENTER);

        JButton jb = new JButton("Ok");
        jb.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(me, "Button pressed!");
            }
        });
        SwingUtilities.getRootPane(c).setDefaultButton(jb);
        c.add(jb, BorderLayout.EAST);

        pack();
        setVisible(true);
    }

    public static void main(String[] arg) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                me = new MyFrame();
            }
        });
    }
}

How do I make this code work in Java 8 and below uniformly?

I found the following code in the Handler class of BasicComboBoxUI in JDK7. The Handler is added to the editor as an ActionListener:

//
// ActionListener
//
// Fix for 4515752: Forward the Enter pressed on the
// editable combo box to the default button

// Note: This could depend on event ordering. The first ActionEvent
// from the editor may be handled by the JComboBox in which case, the
// enterPressed action will always be invoked.
public void actionPerformed(ActionEvent evt) {
    Object item = comboBox.getEditor().getItem();
    if (item != null) {
     if(!comboBox.isPopupVisible() && !item.equals(comboBox.getSelectedItem())) {
      comboBox.setSelectedItem(comboBox.getEditor().getItem());
     }
     ActionMap am = comboBox.getActionMap();
     if (am != null) {
        Action action = am.get("enterPressed");
        if (action != null) {
            action.actionPerformed(new ActionEvent(comboBox, evt.getID(),
                                   evt.getActionCommand(),
                                   evt.getModifiers()));
        }
    }
}

I guess you can check the JDK8 source to see if any change has been made.

If changes have been made then you might need to create your own ActionListener the invokes the "enterPressed" Action of the combo box and add this Action to the editor of the combo box manually.

A short workaround for this 4-year-old bug:

comboBox.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        if (!comboBox.isPopupVisible() && e != null && e.getKeyCode() == KeyEvent.VK_ENTER) {
            Container parent = comboBox.getParent();
            if (parent != null) parent.dispatchEvent(e);
        }
    }
});

For some reason, BasicComboBoxUI skips the container hierarchy and passes the enter key event directly to the JRootPane to "call the default button binding" (whatever that is, it's not the default button in JOptionPane dialogs). This listener manually passes the key event to the parent container.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM