Почему KeyListener работает только тогда, когда кнопка не нажата?

Я хотел сделать 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) {
    }

} 
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
95
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ваша задача - поймать KeyListener во всех компонентах. Сделать это можно так:

public Graphic() {
    // ...
    KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(e -> {
        if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
            dispose();
            return true;
        }
        return false;
    });
}

Когда вы добавите это, ваше приложение будет закрыто сразу после того, как вы нажмете кнопку ESC. Если вы хотите заблокировать его, например если некоторые из ваших компонентов имеют фокус (например, JTextField, и вы хотите выполнить определенное действие), тогда вам нужно сфокусировать компонент в этом слушателе и избегать вызова dispose().

@Rcordoval Да, работал только при Frame фокусе. Я изменил пример, чтобы исправить это.

oleg.cherednik 02.06.2018 08:19

Вы уверены, что нет 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 и привязками клавиш (что заставляет его работать).

Andrew Thompson 02.06.2018 09:08

@AndrewThompson Спасибо за предложение. Я добавил дальнейшее объяснение

Rcordoval 02.06.2018 09:19
Нииче .. Подождите .. что? Кто, черт возьми, проголосовал за этот ответ? Кто бы это ни сделал, не хотите ли поделиться своими рассуждениями? Я в замешательстве.
Andrew Thompson 02.06.2018 09:23

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 предлагает способы указать, при каких условиях он будет вызываться, некоторые из которых не требуют, чтобы компонент был в фокусе.

Другие вопросы по теме