Невідповідний KeyListener для JFrame


80

Я намагаюся реалізувати KeyListenerдля мого JFrame. У конструкторі я використовую цей код:

System.out.println("test");
addKeyListener(new KeyListener() {
    public void keyPressed(KeyEvent e) { System.out.println( "tester"); }

    public void keyReleased(KeyEvent e) { System.out.println("2test2"); }

    public void keyTyped(KeyEvent e) { System.out.println("3test3"); }
});

Коли я запускаю його, testповідомлення з’являється на моїй консолі. Однак, коли я натискаю клавішу, я не отримую жодного з інших повідомлень, ніби того KeyListenerнавіть не було.

Я думав, що це може бути тому, що увага зосереджена не на JFrame
і тому вони KeyListenerне отримують жодних подій. Але, я майже впевнений, що це так.

Щось мені не вистачає?

Відповіді:


51

Ви повинні додати свій keyListener до кожного необхідного вам компонента. Лише компонент із фокусом надсилатиме ці події. Наприклад, якщо у вашому JFrame є лише один TextBox, цей TextBox має фокус. Тому ви також повинні додати KeyListener до цього компонента.

Процес той самий:

myComponent.addKeyListener(new KeyListener ...);

Примітка: Деякі компоненти не можна сфокусувати, як JLabel.

Щоб налаштувати їх на фокусування, потрібно:

myComponent.setFocusable(true);

2
так, ви мали рацію, коли програма запускається, ви трохи бачите, що фокус зроблений на кнопці А. Додавання списку клавіш до кожної кнопки це виправило. це трохи дивно, я б подумав, що додавання списку клавіш до JFrame буде працювати, але я думаю, ні. Дякую!
Томек

я зробив слухач на JFrame, який слухає з клавіатури. Я хочу змусити його працювати в пасивному режимі, навіть якщо вікно не спереду (сфокусоване). JFrame не слухає в пасивному режимі.
Усман,

133

Якщо ви не хочете реєструвати прослуховувач для кожного компонента,
ви можете додати свогоKeyEventDispatcher до KeyboardFocusManager:

public class MyFrame extends JFrame {    
    private class MyDispatcher implements KeyEventDispatcher {
        @Override
        public boolean dispatchKeyEvent(KeyEvent e) {
            if (e.getID() == KeyEvent.KEY_PRESSED) {
                System.out.println("tester");
            } else if (e.getID() == KeyEvent.KEY_RELEASED) {
                System.out.println("2test2");
            } else if (e.getID() == KeyEvent.KEY_TYPED) {
                System.out.println("3test3");
            }
            return false;
        }
    }
    public MyFrame() {
        add(new JTextField());
        System.out.println("test");
        KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        manager.addKeyEventDispatcher(new MyDispatcher());
    }

    public static void main(String[] args) {
        MyFrame f = new MyFrame();
        f.pack();
        f.setVisible(true);
    }
}

5
KeyboardFocusManager є загальнодоступним, якщо у вас багато кадрів, у вас виникнуть проблеми?
neoedmund

2
Отже, це повинно працювати, приблизно так: foreach ("фокусуючі компоненти у кадрі" як _) {_.addkeylistener (frameKeylistener);}
neoedmund

16

InputMaps та ActionMaps були розроблені для фіксації ключових подій для компонента, його та всіх його підкомпонентів або всього вікна. Це контролюється за допомогою параметра в JComponent.getInputMap (). Див. Розділ Використання прив’язок клавіш для отримання документації.

Принадність цього дизайну полягає в тому, що можна вибрати, які ключові штрихи важливі для моніторингу, і здійснювати різні дії, виходячи з цих ключових мазків.

Цей код буде викликати dispose () на JFrame, коли клавіша входу буде натиснута в будь-якому місці вікна. JFrame не походить від JComponent, тому вам доведеться використовувати інший компонент у JFrame для створення прив'язки ключів. Таким компонентом може бути область вмісту.

InputMap inputMap; 
ActionMap actionMap;
AbstractAction action;
JComponent component;

inputMap  = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
actionMap = component.getActionMap();

action    = new AbstractAction()
{
   @Override
   public void actionPerformed(ActionEvent e)
   {
      dispose();
   }
};

inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "dispose");
actionMap.put("dispose", action);

10

KeyListenerє низьким рівнем і стосується лише одного компонента. Незважаючи на спроби зробити його більш придатним для використання, JFrameстворюється ряд компонентних компонентів, найбільш очевидним є область вмісту. JComboBoxІнтерфейс користувача також часто реалізується подібним чином.

Варто зазначити, що події миші працюють дивним чином, трохи відрізняючись від ключових подій.

Детальніше про те, що слід робити, див. У моїй відповіді на комбінацію клавіш - Java Swing .


10

У мене така ж проблема, поки я не прочитав, що справжня проблема полягає у FOCUS, ваш JFrame вже додав слухачів, але кадр туру ніколи не знаходиться у фокусі, тому що у вас є багато компонентів у вашому JFrame, які також є фокусуючими, тому спробуйте:

JFrame.setFocusable(true);

Щасти


2
Я виявив, що це працює лише до тих пір, поки я не використовую щось, що є на моєму JFrame, тоді KeyListener більше не відповідає
user3328784

9

Дейон (і будь-хто інший, хто задає подібне запитання), ви можете скористатися наведеним вище кодом Пітера, але замість того, щоб друкувати на стандартний вивід, ви перевіряєте код ключа PRESSED, RELEASED або TYPED.

@Override
public boolean dispatchKeyEvent(KeyEvent e) {
    if (e.getID() == KeyEvent.KEY_PRESSED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    } else if (e.getID() == KeyEvent.KEY_RELEASED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    } else if (e.getID() == KeyEvent.KEY_TYPED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    }
    return false;
}

4

для того, щоб зафіксувати ключові події ВСІХ текстових полів у JFrame , можна використовувати процесор обробки ключових подій. Ось робочий приклад, після додавання очевидних включень.

public class KeyListenerF1Demo extends JFrame implements KeyEventPostProcessor {
    public static final long serialVersionUID = 1L;

    public KeyListenerF1Demo() {
        setTitle(getClass().getName());

        // Define two labels and two text fields all in a row.
        setLayout(new FlowLayout());

        JLabel label1 = new JLabel("Text1");
        label1.setName("Label1");
        add(label1);

        JTextField text1 = new JTextField(10);
        text1.setName("Text1");
        add(text1);

        JLabel label2 = new JLabel("Text2");
        label2.setName("Label2");
        add(label2);

        JTextField text2 = new JTextField(10);
        text2.setName("Text2");
        add(text2);

        // Register a key event post processor.
        KeyboardFocusManager.getCurrentKeyboardFocusManager()
                .addKeyEventPostProcessor(this);
    }

    public static void main(String[] args) {
        JFrame f = new KeyListenerF1Demo();
        f.setName("MyFrame");
        f.pack();
        f.setVisible(true);
    }

    @Override
    public boolean postProcessKeyEvent(KeyEvent ke) {
        // Check for function key F1 pressed.
        if (ke.getID() == KeyEvent.KEY_PRESSED
                && ke.getKeyCode() == KeyEvent.VK_F1) {

            // Get top level ancestor of focused element.
            Component c = ke.getComponent();
            while (null != c.getParent())
                c = c.getParent();

            // Output some help.
            System.out.println("Help for " + c.getName() + "."
                    + ke.getComponent().getName());

            // Tell keyboard focus manager that event has been fully handled.
            return true;
        }

        // Let keyboard focus manager handle the event further.
        return false;
    }
}

Для робочого прикладу ви можете додати імпорт. Зазвичай я додаю "імпорт пакетів", щоб вони були короткими. В іншому випадку +1. Цікава техніка.
Ендрю Томпсон,

2

Хм .. для якого класу призначений ваш конструктор? Можливо, якийсь клас, що розширює JFrame? Звичайно, фокус вікна повинен бути у вікні, але я не думаю, що це проблема.

Я розширив ваш код, спробував запустити його, і він спрацював - натискання клавіш виводилося як результат друку. (запустити з Ubuntu через Eclipse):

public class MyFrame extends JFrame {
    public MyFrame() {
        System.out.println("test");
        addKeyListener(new KeyListener() {
            public void keyPressed(KeyEvent e) {
                System.out.println("tester");
            }

            public void keyReleased(KeyEvent e) {
                System.out.println("2test2");
            }

            public void keyTyped(KeyEvent e) {
                System.out.println("3test3");
            }
        });
    }

    public static void main(String[] args) {
        MyFrame f = new MyFrame();
        f.pack();
        f.setVisible(true);
    }
}

Я також отримую всі вихідні повідомлення. Запустити в командному рядку Windows.
Даррел

2
Ви отримуєте всі повідомлення, оскільки в цьому прикладі JFrame має фокус. спробуйте додати компонент TextBox до JFrame і подивіться, що станеться.
bruno conde

2

Це має допомогти

    yourJFrame.setFocusable(true);
    yourJFrame.addKeyListener(new java.awt.event.KeyAdapter() {


        @Override
        public void keyTyped(KeyEvent e) {
            System.out.println("you typed a key");
        }

        @Override
        public void keyPressed(KeyEvent e) {
            System.out.println("you pressed a key");
        }

        @Override
        public void keyReleased(KeyEvent e) {
            System.out.println("you released a key");
        }
    });

1

У мене була та ж проблема. Я дослухався до порад Бруно і виявив, що додавання KeyListener просто до кнопки "перша" у JFrame (тобто у верхньому лівому куті) зробило трюк. Але я погоджуюся з вами, що це тривожне рішення. Тож я возився і знайшов більш акуратний спосіб це виправити. Просто додайте рядок

myChildOfJFrame.requestFocusInWindow();

до вашого основного методу, після того, як ви створили свій екземпляр свого підкласу JFrame і встановили його видимим.


спасибі, була та сама проблема. дивним чином компонент втрачає фокус, навіть якщо це область вмісту ...
Андробін

-3

ха-ха .... все, що вам потрібно зробити, це переконатися в цьому

addKeyListener (це);

правильно розміщено у вашому коді.


8
Вам слід дійсно пояснити "правильне місце", щоб зробити цю корисну відповідь.
До

-3

Ви можете замовити користувацькі компоненти JComponents, щоб встановити їх батьківський JFrame для фокусування.

Просто додайте конструктор і передайте JFrame. Потім зателефонуйте setFocusable () у paintComponent.

Таким чином JFrame завжди отримуватиме KeyEvents незалежно від того, натиснуті інші компоненти.


4
-1 однозначно ні - це повне <сильне слово, цензуроване> більш ніж в одному відношенні: а) непристойний підклас б) непристойне передавання посилання в) невідповідна зміна стану під час фарбування г) ..
kleopatra
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.