3

I'm developing an application for my computer science classes. The task is to write a calculator but without using JTextFields or JTextAreas. I've come up with an idea of implementing KeyListener which works nice in both appletviewer and JFrame but doesn't work at all in Google Chrome (and probably other browsers).

Here're my code snippets.

//- 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();
    }

}

And HTML file containing my applet.

<!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>

Any ideas?

Roman C
  • 49,761
  • 33
  • 66
  • 176
Robin92
  • 533
  • 1
  • 6
  • 20
  • @DavidKroukamp is it just "should" or "must"? BTW, my snippets are SSCCE :) – Robin92 Nov 13 '12 at 15:11
  • 1
    must! `KeyListener` is old and has problems with focus etc. I would suggest only using a `KeyListener` when you are expecting an event from any key on the keyboard, and even than always make sure you call for focus on the component the keylistener is attached to to avoid more problems – David Kroukamp Nov 13 '12 at 15:12
  • @DavidKroukamp Your idea works the same as my KeyListener, means it doesn't work in a browser. – Robin92 Nov 13 '12 at 15:34
  • Have you seen my answer? there is many things wrong with the code contributing to your snippets anamolies – David Kroukamp Nov 13 '12 at 15:35
  • It would be better if you replace your `JPanel` with `JFrame` and see how it works. – Roman C Nov 13 '12 at 15:37

2 Answers2

5
  • Use KeyBindings and not KeyListener

  • Do not create your own JFrame for a JApplet simply call getContentPane() on applet instance and add all your components there.

  • All components should be created in JApplet overriden init() method wrapped in a SwingUtilities#invokeAndWait(..) block

  • JApplets and Applets do not have a main(..) method (besides testing purposes)

  • Use requestFocusInWindow() instead of requestFocus()

I would highly suggest you read:

Here is an example works for me. It simply adds an un-editable JTextField to the JPanel and then adds KeyBindings for KeyEvent.VK_0 and KeyEvent.VK_1 to the JPanel. If the user types either 0 or 1 it will be displayed in un-edtiable 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");
            }
        });
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • if you change JTextField (which I CAN"T use) to JLabel the above code seems not to work. Moreover, even with JTextField it doesn't work. – Robin92 Nov 13 '12 at 17:24
  • @Robin92 not sure what the problem is there, edited my code snippet out. Previous points stand though – David Kroukamp Nov 13 '12 at 17:52
  • 1
    +1 for key bindings; a [hybrid](http://stackoverflow.com/a/12449949/230513) may be easier during development. – trashgod Nov 13 '12 at 18:05
  • it's seems like the issue is created by IcedTea. Thanks for help anyway :) – Robin92 Nov 13 '12 at 18:15
  • @Robin92: David's code seemed to work with `JLabel`, as shown [here](http://stackoverflow.com/a/13366367/230513). Are you getting a different result? – trashgod Nov 13 '12 at 18:20
  • @trashgod Yeah, my code (in the Question) works perfectly on Windows, but on Linux it doesn't work in browser. However, as far as this piece of code is concern it doesn't work on Linux and has not been tested on Windows. I suppose there's an issue with IcedTea... – Robin92 Nov 13 '12 at 23:17
  • @Robin92: I've abandoned applet in favor of application and [tag:javawebstart], as suggested in this [variation](http://stackoverflow.com/a/13366367/230513). – trashgod Nov 14 '12 at 01:22
5

I took the liberty of converting @David's helpful example to use a label and the numeric keypad.

Update: This hybrid works on Ubuntu/OpenJDK, and it can be deployed via Java Web Start.

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.*;
import javax.swing.*;

/**
 * @see https://stackoverflow.com/a/13363349/230513
 */
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");
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • +1 great one I somehow couldnt get the label thing to work, only JTextField, and all becuase I forgot to call `requestFocusInWindow()`. Thank you for fixing it it has been racking my brain now for hours :) – David Kroukamp Nov 13 '12 at 18:30
  • 1
    the overriden method of `JApplet` - `init()` should have its code wrapped in an `invokeAndWait` block. (see my answer for me) – David Kroukamp Nov 15 '12 at 10:45