JApplet & KeyListener

3
我正在开发一款应用,用于我的计算机科学课程。任务是写一个计算器,但不能使用JTextField或JTextArea。我想到了一个实现KeyListener的方法,在appletviewer和JFrame中都能很好地工作,但在Google Chrome中根本不起作用(可能也适用于其他浏览器)。
以下是我的代码片段。
//- BinaryCalc.java
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;

public class BinaryCalc extends JApplet implements KeyListener {

    private JPanel panel;

    public BinaryCalc() {
        super();

        panel = new JPanel();
        this.add(panel);

        panel.addKeyListener(this);
        panel.requestFocusInWindow();
    }

    @Override
    public void init() {
        JOptionPane.showMessageDialog(this, "applet");
        panel.setFocusable(true);
        panel.requestFocus();
    }

    public void keyPressed(KeyEvent e) {
        JOptionPane.showMessageDialog(this, (char) e.getKeyCode());
    }

    public void keyReleased(KeyEvent e) {}

    public void keyTyped(KeyEvent e) {}

    public JPanel getPanel() { return panel; }

    public static void main(String args[]) {
        JFrame frame = new JFrame("Binary Calculator");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setMinimumSize(new Dimension(320, 240));

        BinaryCalc kalkulator = new BinaryCalc();
        frame.add(kalkulator);

        frame.pack();
        frame.setVisible(true);
        kalkulator.getPanel().requestFocusInWindow();
    }

}

一个包含我的小程序的HTML文件。
<!doctype html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link rel="stylesheet" type="text/css" href="style.css">
    <title>Kalkulator binarny</title>
</head>

<body>
    <div style="text-align: center; border-bottom: 1px solid black">
        <h1>Kalkulator Binarny</h1>
    </div>

    <br/>
    <object style="display: block; margin: auto;" type="application/x-java-applet" width="320" height="240">
    <param name="code" value="BinaryCalc.class" />
    <!--- <param name="archive" value="Liczba.jar" /> -->
        What a terrible failure: applet failed to load!
    </object>   
    <br/>
</body>

</html>

有什么想法吗?

@DavidKroukamp 这只是“应该”还是“必须”?顺便说一下,我的代码片段是 SSCCE :) - Robin92
1
KeyListener已经过时,而且在焦点等方面存在问题。我建议只有在您期望从键盘的任何键接收事件时才使用KeyListener,即使如此,也一定要确保调用附加到KeyListener的组件的焦点,以避免更多问题。 - David Kroukamp
@DavidKroukamp,你的想法和我的KeyListener一样,也就是说在浏览器中无法工作。 - Robin92
你看到我的回答了吗?代码中有很多问题导致你的片段出现异常。 - David Kroukamp
如果你将你的JPanel替换成JFrame,然后看看它的效果会更好。 - Roman C
2个回答

5
  • 使用KeyBindings而不是KeyListener

  • 不要为JApplet创建自己的JFrame,只需在应用程序实例上调用getContentPane()并在其中添加所有组件即可。

  • 所有组件都应该在JApplet重写的init()方法中创建,并包装在SwingUtilities#invokeAndWait(..)块中。

  • JAppletApplet没有main(..)方法(除了测试目的)

  • 使用requestFocusInWindow()而不是requestFocus()

我强烈建议您阅读:

以下是适合我的示例。它只是将不可编辑的JTextField添加到JPanel,然后为JPanel添加KeyEvent.VK_0KeyEvent.VK_1KeyBinding。如果用户键入01,它将显示在不可编辑的JTextField中:

enter image description here

import java.awt.BorderLayout;
import java.awt.event.*;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

public class BinaryCalc extends JApplet {

    private JTextField jtf;

    @Override
    public void init() {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                @Override
                public void run() {

                    JPanel panel = new JPanel();
                    setKeyBindings(panel);
                    jtf = new JTextField(10);
                    //so we cant edited it without pressing a key
                    jtf.setEditable(false);

                    panel.add(jtf);
                    getContentPane().add(panel, BorderLayout.CENTER);
                    panel.requestFocusInWindow();//incase we lost focus
                }
            });
        } catch (InterruptedException ex) {
            Logger.getLogger(BinaryCalc.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(BinaryCalc.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void setKeyBindings(final JPanel panel) {
        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_0,0), "0");
        panel.getActionMap().put("0", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                String tmp = jtf.getText();
                jtf.setText(tmp + "0");
            }
        });
        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_1,0), "1");
        panel.getActionMap().put("1", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                String tmp = jtf.getText();
                jtf.setText(tmp + "1");
            }
        });
    }
}

@Robin92 不确定问题在哪里,已经编辑了我的代码片段。之前的观点仍然成立。 - David Kroukamp
1
+1 对于键绑定非常重要;在开发过程中使用混合模式可能更容易。 - trashgod
似乎问题是由IcedTea创建的。无论如何,感谢您的帮助 :) - Robin92
@Robin92:David的代码似乎可以使用JLabel,如此处所示。你得到了不同的结果吗? - trashgod
@trashgod,是的,我的代码(在问题中)在Windows上运行得非常完美,但在Linux上在浏览器中无法工作。然而,就这段代码而言,在Linux上它不起作用,并且尚未在Windows上进行测试。我想这可能与IcedTea有关... - Robin92
显示剩余2条评论

5
我已经自作主张将@David的有用示例改为使用标签和数字键盘。
更新:这个混合在Ubuntu/OpenJDK上工作,并且可以通过Java Web Start部署。
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.*;
import javax.swing.*;

/**
 * @see https://dev59.com/WWrXa4cB1Zd3GeqPC81X#13363349
 */
public class BinaryCalc extends JApplet {

    private static BinaryCalc bc = new BinaryCalc();
    private JLabel label = new JLabel("0000000000");

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setTitle("BinaryCalc");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                initContainer(frame);
                frame.pack();
                bc.label.setText("");
                frame.setVisible(true);
            }
        });
    }

    // Common initialization for either JApplet or JFrame
    private static void initContainer(Container container) {
        JPanel panel = new JPanel();
        bc.setKeyBindings(panel);
        panel.add(bc.label);
        container.add(panel, BorderLayout.CENTER);
        panel.requestFocusInWindow();
    }

    @Override
    public void init() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                initContainer(BinaryCalc.this);
            }
        });
    }

    private void setKeyBindings(final JPanel panel) {
        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
            .put(KeyStroke.getKeyStroke(KeyEvent.VK_0, 0), "0");
        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
            .put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "0");
        panel.getActionMap().put("0", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                String tmp = label.getText();
                label.setText(tmp + "0");
            }
        });
        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
            .put(KeyStroke.getKeyStroke(KeyEvent.VK_1, 0), "1");
        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
            .put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD1, 0), "1");
        panel.getActionMap().put("1", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                String tmp = label.getText();
                label.setText(tmp + "1");
            }
        });
    }
}

+1 大佬,我不知道为什么标签无法工作,只有 JTextField 可以,原来是因为我忘记调用 requestFocusInWindow()。谢谢你修复了它,这让我苦思冥想了几个小时 :) - David Kroukamp
1
JApplet 的重写方法 init() 应该将其代码包装在一个 invokeAndWait 块中。(请参见我的答案) - David Kroukamp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接