Класс, расширяющий KeyListener, не отвечает на нажатия клавиш

Извините, что это дубликат - я очень спешил, когда писал это, и у меня не было времени проверить. Хотя я полагаю, что это было бы быстрее, теперь, когда я думаю об этом ...


Возможный дубликат:
Java KeyListener для JFrame не отвечает?

Я пишу приложение и пытаюсь сделать горячие клавиши. Я решил использовать KeyListener, поскольку это все, что я знаю на данный момент. Однако класс не реагирует на нажатия клавиш. Как мне исправить эту ошибку? Если есть альтернатива KeyListener, которая будет делать то же самое, дайте мне знать и, желательно, предоставьте пример того, как это будет работать.

Основной класс

import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
import panels.TabBar;

public class __mn implements KeyListener {
    static JFrame disp = new JFrame("dat app");

    static TabBar tabs = new TabBar();

    public static void main(String[] args) {
        disp.setLayout(new BorderLayout());
        disp.add(tabs, BorderLayout.PAGE_START);
        disp.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        disp.setSize(TabBar.PREF_WIDTH, 500);
        disp.setResizable(false);
        disp.setLocationRelativeTo(null);
        disp.addKeyListener(new __mn());
        disp.setVisible(true);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        System.out.println(e.paramString());
    }

    //Unused
    @Override public void keyReleased(KeyEvent e) {
        System.out.println(e.paramString());
    } 
    @Override public void keyTyped(KeyEvent e) {
        System.out.println(e.paramString());
    }
}

TabBar класс

package panels;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;

//http://www.dreamincode.net/forums/topic/245148-java-key-binding-tutorial-and-demo-program/

public class TabBar extends JPanel implements ActionListener {
    private static final long serialVersionUID = 1L;

    public static final int NONE = -1;
    public static final int INBOX = 0;
    public static final int SEND_MSG = 1;
    public static final int PRIVATE_CHAT = 2;
    public static final int FEEDBACK = 3;

    public static final int PREF_WIDTH = 425;

    private static final String[] tabNames = {"Inbox", "Send a message", "Private chat", "Feedback"};

    private static final JButton btnInbox = new JButton(tabNames[INBOX]);
    private static final JButton btnSendMSG = new JButton(tabNames[SEND_MSG]);
    private static final JButton btnPrivChat = new JButton(tabNames[PRIVATE_CHAT]);
    private static final JButton btnFeedback = new JButton(tabNames[FEEDBACK]);

    public int currentTab = -1;

    public TabBar() {
        this(new FlowLayout());
    }

    public TabBar(LayoutManager layout) {
        super(layout);

        add(btnInbox);
        add(btnSendMSG);
        add(btnPrivChat);
        add(btnFeedback);

        btnInbox.addActionListener(this);
        btnSendMSG.addActionListener(this);
        btnPrivChat.addActionListener(this);
        btnFeedback.addActionListener(this);

        setBackground(Color.BLACK);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.paramString());
        if (e.getSource() == btnInbox)
            currentTab = INBOX;
        else if (e.getSource() == btnSendMSG)
            currentTab = SEND_MSG;
        else if (e.getSource() == btnPrivChat)
            currentTab = PRIVATE_CHAT;
        else if (e.getSource() == btnFeedback)
            currentTab = FEEDBACK;
        else currentTab = NONE;
    }

    public void hotkeyPressed(char pressed) {
        pressed = Character.toLowerCase(pressed);
        System.out.println("Hotkey pressed: " + pressed);
        switch (pressed) {
        case 'i':
            setTab(INBOX);
            break;
        case 's':
            setTab(SEND_MSG);
            break;
        case 'p':
            setTab(PRIVATE_CHAT);
            break;
        case 'f':
            setTab(FEEDBACK);
            break;
        default:
            break;
        }
    }

    private void setTab(int tab) {
        System.out.println("Somthing pressed! tab=" + tab);
        currentTab = tab;
        switch (tab) {
        case INBOX:
            btnInbox.requestFocusInWindow();
        }
    }
}

person Fund Monica's Lawsuit    schedule 08.01.2013    source источник
comment
Не стесняйтесь редактировать код, если считаете это ненужным. - вы должны это сделать.   -  person djechlin    schedule 08.01.2013
comment
Вы видите ошибку в консоли? Где именно вызывается метод hotkeyPressed? Какие ключи вы пытаетесь протестировать?   -  person mtk    schedule 08.01.2013
comment
Быстрый ответ, конечно .. Всем спасибо.   -  person Fund Monica's Lawsuit    schedule 08.01.2013
comment
@LeeMeador Ты просто превратил это в ответ, чтобы я мог его принять? :)   -  person Fund Monica's Lawsuit    schedule 08.01.2013
comment
Вы можете использовать KeyBinding, как показано здесь. KeyListeners слишком низкоуровневые с точки зрения Swing.   -  person nIcE cOw    schedule 08.01.2013


Ответы (2)


Как мне исправить эту ошибку?

Не столько ошибка, сколько несовместимость.

Если есть альтернатива KeyListener, которая будет делать то же самое, дайте мне знать и, желательно, предоставьте пример того, как это будет работать.

Не используйте _1 _ / _ 2_ для компонентов Swing, поскольку есть проблемы с фокусом, которые можно решить, вызвав requestFocusInWindow() на компоненте после того, как он станет видимым, чтобы убедиться, что он имеет фокус, и, конечно, setFoucsable(true); должен быть вызван, если компонент не фокусируемый < / em> как JLabel.

Компоненты Swing должны использовать KeyBindings, который преодолевает вышеупомянутые проблемы путем автоматический вызов setFocusable(true) на компоненте, к которому добавлен KeyBinding и т. д.

Вот небольшой пример:

void addKeyBinding(JComponent jc) {
        jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "A pressed");
        jc.getActionMap().put("esc pressed", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                System.out.println("A pressed");
            }
        });

        jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "A released");
        jc.getActionMap().put("A released", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                System.out.println("A released");
            }
        });
}

вы должны вызвать этот метод с экземпляром JComponent, то есть JPanel и т. д., который вы также хотите добавить Keybinding:

JPanel p=new JPanel();

addKeyBinding(p);

Другие предложения по коду:

  • Всегда создавайте компоненты Swing на Event Dispatch Thread через блок SwingUtilities.invokeXXX

  • Не реализуйте Listener like ActionListener в самом классе, если только этот класс не будет использоваться как Listener только, или если вы хотите предоставить реализующие классы / Listeners методы другим классам.

person David Kroukamp    schedule 08.01.2013
comment
+1 для привязки клавиш; до недавнего времени я бы спорил, что игры могут быть исключением, но я был неправ. :-) - person trashgod; 08.01.2013
comment
@trashgod +1 хе-хе Keybindings - это единственное и единственное предположение, что нам просто нужно было узнать, зачем их использовать, и после многих проблем с KeyListeners в моих играх я обнаружил, что KeyBindings сначала больше печатает, но в конце концов окупается - person David Kroukamp; 08.01.2013
comment
Это кажется идеальным для того, что я имел в виду. Спасибо! Но есть ли причина сделать это отдельным методом и не добавлять привязки клавиш в конструктор? - person Fund Monica's Lawsuit; 09.01.2013
comment
@NickHartley не только для читабельности кода - person David Kroukamp; 09.01.2013
comment
@DavidKroukamp Тогда я, вероятно, добавлю массив в качестве аргумента, чтобы его было легче настроить. Спасибо! - person Fund Monica's Lawsuit; 09.01.2013
comment
Хммм ... Я использую Java 6, и это, похоже, не работает. Есть ли еще что-нибудь, о чем мне нужно подумать? Исходный код теперь идентичен приведенному выше, но я добавил еще три привязки и изменил ключи и маски. Если вам нужен исходный код, просто скопируйте / вставьте то, что у вас есть выше, три раза и измените маску на KeyEvent.CTRL_DOWN_MASK. Спасибо! - person Fund Monica's Lawsuit; 09.01.2013
comment
@NickHartley Моя оригинальная работа? Да? ну, чем ваши дополнения - проблема ... скорее всего, вам следует изменить текст с A Release или Aolved на что-то другое (это уникально для этой привязки клавиш, иначе это будет перезаписать предыдущий KeyBinding этим string, вы не можете сказать, потому что вы скопировали и вставили и добавили еще 3 метода, он не работает, поэтому ваш код не является проблемой, id нужен исходный код, есть много вещей, которые могут пойти не так ... - person David Kroukamp; 09.01.2013
comment
Собственно, я только что протестировал оригинал, и он не работает. Но я понял проблему ... Я пытался использовать JFrame, а не JPanel. Я ошибаюсь. XD - person Fund Monica's Lawsuit; 10.01.2013
comment
Алос, простите меня, если я не сказал это достаточно четко, но когда я сказал I changed the keys, я имел в виду, что я изменил не только клавишу pressses, но и клавишу String, используемую в ActionMap и InputMap. В любом случае, это была глупая ошибка с моей стороны, которая вызвала сбой, так что все в порядке. - person Fund Monica's Lawsuit; 10.01.2013

JFrame состоит из множества различных компонентов и, как таковой, не может быть сфокусирован по своей природе. Без фокуса он не может получать ключевые события. Для этого есть много решений. Я собираюсь предложить только это:

Не отвечающий KeyListener для JFrame

И я предлагаю вам Google "keylistener на jframe java", чтобы найти дополнительную информацию. Как я уже сказал, есть много способов решить эту проблему.

person Lee Meador    schedule 08.01.2013
comment
Это не лучшая практика, для компонентов Swing следует использовать привязки клавиш. - person David Kroukamp; 08.01.2013
comment
+1 за хорошее объяснение, но @DavidKroukamp прав, предлагая Привязки клавиш. - person trashgod; 08.01.2013
comment
Привязка клавиш предлагается либо в связанной статье, либо в одном из нескольких самых популярных ответов в поиске Google. Я не видел причин повторять все, что там сказано. Рад, что @DavidKroukamp может подсказать, где Ник может лучше сосредоточить свое чтение. - person Lee Meador; 09.01.2013