KeyListener không phản hồi cho JFrame


80

Tôi đang cố gắng thực hiện một KeyListenercho của tôi JFrame. Trên phương thức khởi tạo, tôi đang sử dụng mã này:

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"); }
});

Khi tôi chạy nó, testthông báo xuất hiện trong bảng điều khiển của tôi. Tuy nhiên, khi tôi nhấn một phím, tôi không nhận được bất kỳ thông báo nào khác, như KeyListenerthể thậm chí không có ở đó.

Tôi đã nghĩ rằng có thể là do không tập trung vào JFrame
và vì vậy họ KeyListenerkhông nhận được bất kỳ sự kiện nào. Nhưng, tôi khá chắc chắn là như vậy.

Có điều gì đó mà tôi đang thiếu?

Câu trả lời:


51

Bạn phải thêm keyListener của mình vào mọi thành phần mà bạn cần. Chỉ thành phần có tiêu điểm mới gửi các sự kiện này. Ví dụ: nếu bạn chỉ có một TextBox trong JFrame của mình, TextBox đó có tiêu điểm. Vì vậy, bạn cũng phải thêm KeyListener vào thành phần này.

Quá trình này như nhau:

myComponent.addKeyListener(new KeyListener ...);

Lưu ý: Một số thành phần không thể lấy tiêu điểm như JLabel.

Để đặt chúng thành có thể lấy tiêu điểm, bạn cần:

myComponent.setFocusable(true);

2
vâng, bạn đã đúng, khi chương trình bắt đầu, bạn có thể thấy rằng tiêu điểm nằm trên nút A. thêm một keylistener vào mỗi nút đã khắc phục điều này. điều đó hơi kỳ lạ, tôi nghĩ rằng việc thêm keylistener vào JFrame sẽ hoạt động nhưng tôi đoán là không. Cảm ơn!
Tomek

tôi đã tạo một Trình nghe trên JFrame mà Nghe từ bàn phím. Tôi muốn làm cho nó hoạt động ở chế độ thụ động, ngay cả khi cửa sổ không ở phía trước (lấy nét). JFrame không Nghe ở chế độ thụ động.
Usman

132

Nếu bạn không muốn đăng ký một trình lắng nghe trên mọi thành phần,
bạn có thể thêm trìnhKeyEventDispatcher lắng nghe của riêng mình vào 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 là ứng dụng rộng rãi, nếu bạn có nhiều khung hình, bạn sẽ gặp rắc rối?
neoedmund

2
Vì vậy, điều này sẽ làm việc, cái gì đó như: foreach ( "các thành phần thể đặt tiêu điểm trong khung" như _) {_.addkeylistener (frameKeylistener);}
neoedmund

16

InputMaps và ActionMaps được thiết kế để nắm bắt các sự kiện chính của thành phần, nó và tất cả các thành phần phụ của nó hoặc toàn bộ cửa sổ. Điều này được kiểm soát thông qua tham số trong JComponent.getInputMap (). Xem Cách Sử dụng Các Ràng buộc Chính để có tài liệu.

Vẻ đẹp của thiết kế này là người ta có thể chọn và chọn những nét chính nào là quan trọng để theo dõi và thực hiện các hành động khác nhau dựa trên những nét chính đó.

Mã này sẽ gọi dispose () trên JFrame khi phím thoát được nhấn vào bất kỳ đâu trong cửa sổ. JFrame không bắt nguồn từ JComponent nên bạn phải sử dụng một thành phần khác trong JFrame để tạo liên kết khóa. Ngăn nội dung có thể là một thành phần như vậy.

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

KeyListenerlà mức thấp và chỉ áp dụng cho một thành phần duy nhất. Bất chấp những nỗ lực để làm cho nó có thể sử dụng được nhiều hơn vẫn JFrametạo ra một số thành phần thành phần, rõ ràng nhất là ngăn nội dung. JComboBoxGiao diện người dùng cũng thường được triển khai theo cách tương tự.

Cần lưu ý rằng các sự kiện chuột hoạt động theo một cách kỳ lạ hơi khác với các sự kiện chính.

Để biết chi tiết về những gì bạn nên làm, hãy xem câu trả lời của tôi trên Lối tắt bàn phím dành cho ứng dụng - Java Swing .


10

Tôi gặp vấn đề tương tự cho đến khi tôi đọc được rằng vấn đề thực sự là về TẬP TRUNG, JFrame của bạn đã thêm Trình nghe nhưng khung tham quan không bao giờ ở Tập trung vì bạn có rất nhiều thành phần bên trong JFrame của mình cũng có thể lấy tiêu điểm, vì vậy hãy thử:

JFrame.setFocusable(true);

Chúc may mắn


2
Tôi thấy rằng làm việc này cho đến khi tôi sử dụng cái gì đó là trên JFrame tôi thì KeyListener không còn đáp ứng
user3328784

9

Deion (và bất kỳ ai khác đặt câu hỏi tương tự), bạn có thể sử dụng mã của Peter ở trên nhưng thay vì in ra đầu ra tiêu chuẩn, bạn kiểm tra mã khóa PRESSED, RELEASED hoặc 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

để nắm bắt các sự kiện chính của TẤT CẢ các trường văn bản trong JFrame , người ta có thể sử dụng bộ xử lý bài đăng sự kiện chính. Đây là một ví dụ hoạt động, sau khi bạn thêm các bao gồm rõ ràng.

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;
    }
}

Đối với một ví dụ làm việc, bạn có thể xem xét thêm các nhập khẩu. Tôi thường thêm 'nhập gói' để giữ cho chúng ngắn gọn. Nếu không, hãy +1. Kỹ thuật thú vị.
Andrew Thompson,

2

Hmm .. hàm tạo của bạn cho lớp nào? Có lẽ là một số lớp mở rộng JFrame? Tiêu điểm cửa sổ nên ở cửa sổ, tất nhiên, nhưng tôi không nghĩ đó là vấn đề.

Tôi đã mở rộng mã của bạn, cố gắng chạy nó và nó hoạt động - các lần nhấn phím dẫn đến kết quả in ra. (chạy với Ubuntu thông qua 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);
    }
}

Tôi cũng nhận được tất cả các đầu ra tin nhắn. Chạy trong dòng lệnh Windows.
Darrel

2
Bạn nhận được tất cả các thông báo vì trong ví dụ này, JFrame có tiêu điểm. hãy thử thêm một thành phần TextBox vào JFrame và xem điều gì sẽ xảy ra.
bruno conde

2

Điều này sẽ giúp

    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

Tôi đã được gặp cùng một vấn đề. Tôi đã làm theo lời khuyên của Bruno cho bạn và nhận thấy rằng việc thêm KeyListener chỉ vào nút "đầu tiên" trong JFrame (tức là ở trên cùng bên trái) đã thực hiện được một mẹo nhỏ. Nhưng tôi đồng ý với bạn đó là một giải pháp đáng lo ngại. Vì vậy, tôi đã mày mò và tìm ra một cách gọn gàng hơn để sửa chữa nó. Chỉ cần thêm dòng

myChildOfJFrame.requestFocusInWindow();

vào phương thức chính của bạn, sau khi bạn đã tạo phiên bản của lớp con JFrame và đặt nó ở chế độ hiển thị.


cảm ơn, có cùng một vấn đề. kỳ quặc thành phần mất tập trung ngay cả khi đó là khung nội dung ...
Androbin

-3

lol .... tất cả những gì bạn phải làm là đảm bảo rằng

addKeyListener (this);

được đặt chính xác trong mã của bạn.


8
Bạn thực sự nên giải thích "nơi chính xác" để biến điều này thành một câu trả lời hữu ích.
Cho đến

-3

Bạn có thể có các JComponents tùy chỉnh đặt JFrame mẹ của chúng có thể lấy tiêu điểm.

Chỉ cần thêm một hàm tạo và truyền vào JFrame. Sau đó, thực hiện một cuộc gọi đến setFocusable () trong paintComponent.

Bằng cách này, JFrame sẽ luôn nhận được KeyEvents bất kể các thành phần khác có được nhấn hay không.


4
-1 chắc chắn không - đó là hoàn chỉnh <strong từ bị kiểm duyệt> trong hơn một khía cạnh: a) không đứng đắn subclassing b) tài liệu tham khảo không đứng đắn qua c) thay đổi trạng thái không phù hợp khi vẽ d) ..
Kleopatra
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.