Клавиша Enter работает как клавиша Tab для каждого компонента, кроме JButton.

Я разрабатываю приложение Swing, в котором я хочу ввести ключ в качестве клавиши табуляции для всех компонентов JFrame, кроме компонентов JButton и диалоговых окон. Для этого я установил ENTER и TAB в качестве клавиш обхода фокуса по умолчанию.

KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
Set<KeyStroke> keys = new HashSet<>();
keys.add(enter);
keys.add(tab);
KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);

Это работает хорошо, но я хочу, чтобы клавиша ENTER работала как действие в JButton и диалоговых окнах.


person Anand Dubey    schedule 20.05.2018    source источник


Ответы (2)


Можно, но другим способом: глобальный слушатель событий. Чтобы зарегистрировать прослушиватель глобальных событий, вы должны использовать класс Toolkit:

Toolkit.getDefaultToolkit().addAWTEventListener(listener, mask);

Вот пример для вашего случая:

import java.awt.AWTEvent;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

public class FocusTransferTest {

    public static void main(String[] args) {
        JFrame frm = new JFrame("Test focus transfer");
        JPanel panel = new JPanel();
        panel.add(new JTextField(10));
        panel.add(new JTextField(10));
        panel.add(new JTextField(10));
        JButton btn = new JButton("Press me");
        btn.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frm, "It's a message", "Info", 
                        JOptionPane.INFORMATION_MESSAGE);
            }
        });
        panel.add(btn);
        Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEnterKeyListener(), 
                AWTEvent.KEY_EVENT_MASK);
        frm.add(panel);
        frm.pack();
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.setLocationRelativeTo(null);
        frm.setVisible(true);
    }

    private static class AWTEnterKeyListener implements AWTEventListener {

        @Override
        public void eventDispatched(AWTEvent event) {
            if (event instanceof KeyEvent) {
                KeyEvent key = (KeyEvent) event;
                if (key.getKeyCode() == KeyEvent.VK_ENTER  && key.getModifiersEx() == 0 
                        && key.getID() == KeyEvent.KEY_PRESSED) {
                    if (key.getComponent() instanceof AbstractButton) {
                        ((AbstractButton) key.getComponent()).doClick();
                    } else {
                        key.getComponent().transferFocus();
                    }
                }
            }
        }

    }
}
person Sergiy Medvynskyy    schedule 21.05.2018
comment
Спасибо @Сергей Медвинский - person Anand Dubey; 21.05.2018
comment
@AnandDubey, если мой ответ полезен, проголосуйте за него и отметьте как решение. Спасибо. - person Sergiy Medvynskyy; 21.05.2018
comment
пожалуйста, проголосуйте У ОП недостаточно представителей. проголосовать. OP Пожалуйста, примите ответ и еще раз взгляните на ответ на этот вопрос и посмотрите, сможете ли вы его принять! - person Andrew Thompson; 21.05.2018

Я думаю, что даже решение с использованием AWTEventListener будет работать, я бы посоветовал избегать использования AWTEventListener, если доступно другое решение. Это потому, что он настолько мощный, что перехватывает все виды событий глобально, прежде чем они достигнут своих реальных целей, поэтому, если что-то пойдет не так (например, исключение NullPointerException) в середине, все приложение перестанет работать.

Мое предлагаемое решение использует карту ввода и карту действий, которая добавляет обработку клавиши Enter для любого сфокусированного компонента в конкретном контейнере.

Преимущество:

  • Более безопасный, потому что он влияет только на компоненты в контейнере, а не на все компоненты.

Недостаток:

  • Один и тот же код обработки должен применяться ко всем контейнерам, которым требуется такое поведение, но это может быть легко достигнуто с помощью статического служебного метода.

Вот пример программы:

public MainFrame() {
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(500, 500);
    setLayout(new GridLayout(2, 2));

    addAllComponents();
    addEnterKeyAsFocusTraversal();
}

private void addAllComponents() {
    add(new JTextField());
    add(new JTextField());

    add(new JButton("OK"));
    add(new JButton("Cancel"));
}

private void addEnterKeyAsFocusTraversal() {
    final String ENTER_KEY_ACTION = "EnterKeyAction";

    // Here uses the content pane of type Container so a cast is required, 
    // in other case it could be the root container which may already be an instance of JComponent. 
    JComponent contentPane = (JComponent) getContentPane();

    contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ENTER_KEY_ACTION);
    contentPane.getActionMap().put(ENTER_KEY_ACTION, createEnterKeyAction());
}

private AbstractAction createEnterKeyAction() {
    return new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();

            if (focusOwner != null) {
                if (focusOwner instanceof AbstractButton) {
                    ((AbstractButton) focusOwner).doClick();
                } else {
                    focusOwner.transferFocus();
                }
            }
        }
    };
}
person Antony Ng    schedule 21.05.2018