Я хотел сделать KeyListener, чтобы останавливать программу, когда я нажимаю клавишу ESC. Но это работает только тогда, когда я больше ничего не делал (нажимал кнопку). Прошу прощения, если это что-то очень очевидное, но я не могу найти ошибку.
package basics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Graphic extends JFrame implements ActionListener, KeyListener {
private JButton button;
public Graphic() {
button = new JButton();
button.addActionListener(this);
button.setIcon(new ImageIcon("Image.jpg"));
this.getContentPane().add(button);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button){
//some code
}
}
public static void main(String[] args) {
JFrame bec = new Graphic();
bec.setDefaultCloseOperation(Graphic.EXIT_ON_CLOSE);
bec.setSize(1731, 563);
bec.setVisible(true);
bec.setTitle("title");
bec.requestFocus();
bec.addKeyListener(new Graphic());
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
System.exit(0);
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}




Ваша задача - поймать KeyListener во всех компонентах. Сделать это можно так:
public Graphic() {
// ...
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(e -> {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
dispose();
return true;
}
return false;
});
}
Когда вы добавите это, ваше приложение будет закрыто сразу после того, как вы нажмете кнопку ESC. Если вы хотите заблокировать его, например если некоторые из ваших компонентов имеют фокус (например, JTextField, и вы хотите выполнить определенное действие), тогда вам нужно сфокусировать компонент в этом слушателе и избегать вызова dispose().
Вы уверены, что нет TextArea или других объектов, на которые можно сфокусироваться? Они могут получить фокус, и ключевые события будут передаваться им, а не слушателю.
UPD: Извините, не увидел, что у вас там кроме кнопки ничего нет.
KeyListener страдает от проблем, связанных с фокусируемостью и другими элементами управления в графическом интерфейсе. Простым решением было бы использовать API Действия. При таком подходе программа просто определяет для данного компонента «привязку» или «сопоставление» между любой интересующей клавишей и объектом действия (команды), который будет вызываться при нажатии (или отпускании) этой клавиши. Привязки клавиш связаны с конкретным компонентом графического интерфейса.
В этом случае подходящим решением может быть:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
public class Graphic extends JFrame implements ActionListener {
private JButton button;
public Graphic() {
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel"); //$NON-NLS-1$
getRootPane().getActionMap().put("Cancel", new AbstractAction(){ //$NON-NLS-1$
public void actionPerformed(ActionEvent e)
{
dispose();
}
});
button = new JButton();
button.addActionListener(this);
button.setIcon(new ImageIcon("Image.jpg"));
this.getContentPane().add(button);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button){
//some code
}
}
public static void main(String[] args) {
JFrame bec = new Graphic();
bec.setDefaultCloseOperation(Graphic.EXIT_ON_CLOSE);
bec.setSize(1731, 563);
bec.setVisible(true);
bec.setTitle("title");
bec.requestFocus();
}
}
Я проголосовал за, но это был бы еще лучший ответ, если бы вы упомянули различия между KeyListener и привязками клавиш (что заставляет его работать).
@AndrewThompson Спасибо за предложение. Я добавил дальнейшее объяснение
But it only works when I did nothing else (pressed the button).
Нет, не работает (вообще). Взгляните на этот код:
public static void main(String[] args) {
JFrame bec = new Graphic();
// ..
bec.addKeyListener(new Graphic());
}
Ключевой слушатель добавляется ко второму экземпляру Graphic, который никогда не отображается.
Другая причина, по которой это не сработает: потому что KeyListener (даже если он добавлен в правильный экземпляр) требует, чтобы компонент, к которому он добавлен, был как фокусируемым (JFrame по умолчанию - нет), так и имел фокус ввода (чего у этого кадра никогда не будет, учитывая, что он не фокусируется).
Решение: для Swing мы обычно используем привязки клавиш, а не KeyListener нижнего уровня. Key binging предлагает способы указать, при каких условиях он будет вызываться, некоторые из которых не требуют, чтобы компонент был в фокусе.
@Rcordoval Да, работал только при
Frameфокусе. Я изменил пример, чтобы исправить это.