Yet another KeyListener question

I'm trying to implement KeyListeners for a simple Calculator program, to catch key typed events from the numeric keypad. So far I've done the following:

added implements KeyListener to the class header

added KeyListeners to each JButton

made the Panel that the JButton array resides in focusable

added the code for the Panel to request focus when the Calculator window is selected:

addWindowListener(new WindowAdapter(){

publicvoid windowActivated(WindowEvent e){

keypad.requestFocusInWindow();

}

});

added KeyTyped, KeyPressed, and KeyReleased methods to the class

the (incomplete) KeyTyped method, which uses code from the Sun tutorial, so far is:

publicvoid keyTyped(KeyEvent e)

{

int id = e.getID();

if (id == KeyEvent.KEY_TYPED){

char c = e.getKeyChar();

int keyNum = (int)c;

switch (keyNum)

{

case 48:case 96:

keys[0].doClick(100);

break;

case 49:case 97:

keys[1].doClick(100);

break;

case 50:case 98:

keys[2].doClick(100);

break;

}

}

}

So far, the implementation of the KeyListener doesn't appear to do anything.

What have I missed so far? Is the enclosing Panel of the JButton array the correct component to be requesting focus? Is there an error in the keyTyped method?

and here is the complete code for your enjoyment:

import java.awt.*;

import java.awt.event.*;

import java.awt.datatransfer.*;

import java.text.DecimalFormat;

import javax.swing.JOptionPane;

import javax.swing.JButton;//ADDED

publicclass Calculatorextends Frameimplements ActionListener, KeyListener

{

private JButton keys[];//CHANGED Button to JButton

private Panel keypad;

private TextField lcd;

privatedouble op1;

privateboolean first;

privateboolean foundKey;

privateboolean clearText;

privateint lastOp;

private DecimalFormat calcPattern;

public Calculator()

{

//

MenuBar mnuBar =new MenuBar();

setMenuBar(mnuBar);

//

Menu mnuFile =new Menu("File",true);

mnuBar.add(mnuFile);

MenuItem mnuFileExit =new MenuItem("Exit");

mnuFile.add(mnuFileExit);

//

Menu mnuEdit =new Menu("Edit",true);

mnuBar.add(mnuEdit);

MenuItem mnuEditClear =new MenuItem("Clear");

mnuEdit.add(mnuEditClear);

mnuEdit.addSeparator();

MenuItem mnuEditCopy =new MenuItem("Copy");

mnuEdit.add(mnuEditCopy);

MenuItem mnuEditPaste =new MenuItem("Paste");

mnuEdit.add(mnuEditPaste);

//

Menu mnuAbout =new Menu("About",true);

mnuBar.add(mnuAbout);

MenuItem mnuAboutCalculator =new MenuItem("About Calculator");

mnuAbout.add(mnuAboutCalculator);

//

mnuFileExit.addActionListener(this);

mnuEditClear.addActionListener(this);

mnuEditCopy.addActionListener(this);

mnuEditPaste.addActionListener(this);

mnuAboutCalculator.addActionListener(this);

//

mnuFileExit.setActionCommand("Exit");

mnuEditClear.setActionCommand("Clear");

mnuEditCopy.setActionCommand("Copy");

mnuEditPaste.setActionCommand("Paste");

mnuAboutCalculator.setActionCommand("About");

//

lcd =new TextField(20);

lcd.setEditable(false);

keypad =new Panel();

keypad.setFocusable(true);//ADDED

keys =new JButton[24];

first =true;

op1 = 0.0;

clearText =true;

lastOp = 0;

calcPattern =new DecimalFormat("########.########");

//populate keys array with JButtons

for(int i = 0; i<=9; i++)

keys[i] =new JButton(String.valueOf(i));

keys[10] =new JButton("/");

keys[11] =new JButton("*");

keys[12] =new JButton("-");

keys[13] =new JButton("+");

keys[14] =new JButton("=");

keys[15] =new JButton(".");

keys[16] =new JButton("M+");

keys[17] =new JButton("M-");

keys[18] =new JButton("M*");

keys[19] =new JButton("M/");

keys[20] =new JButton("MR");

keys[21] =new JButton("MC");

keys[22] =new JButton("Back");

keys[23] =new JButton("Clear");

//

setLayout(new BorderLayout());

keypad.setLayout(new GridLayout(6,4,10,10));

for (int i=7; i<=10; i++)

keypad.add(keys[i]);

for (int i=4; i<=6; i++)

keypad.add(keys[i]);

keypad.add(keys[11]);

for(int i=1; i<=3; i++)

keypad.add(keys[i]);

keypad.add(keys[12]);

keypad.add(keys[0]);

for (int i=15; i>=13; i--)

keypad.add(keys[i]);

for (int i=16; i<24; i++)

keypad.add(keys[i]);

for(int i=0; i<keys.length; i++)

{

keys[i].addActionListener(this);

keys[i].addKeyListener(this);

}

add(lcd, BorderLayout.NORTH);

add(keypad, BorderLayout.CENTER);

//ADDED - keypad panel requests focus when window is active

// not sure this is the correct component to request focus

addWindowListener(new WindowAdapter(){

publicvoid windowActivated(WindowEvent e){

keypad.requestFocusInWindow();

}

});

addWindowListener(

new WindowAdapter()

{

publicvoid windowClosing(WindowEvent e)

{

System.exit(0);

}

}

);

}// end of constructor method

publicvoid actionPerformed(ActionEvent e)

{

//test for menu item clicks

String arg = e.getActionCommand();

if (arg =="Exit")

System.exit(0);

if (arg =="Clear")

{

clearText =true;

first =true;

op1 = 0.0;

lcd.setText("");

lcd.requestFocus();

}

if (arg =="Copy")

{

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

StringSelection contents =new StringSelection(lcd.getText());

cb.setContents(contents,null);

}

if (arg =="Paste")

{

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

Transferable content = cb.getContents(this);

try

{

String s = (String)content.getTransferData(DataFlavor.stringFlavor);

lcd.setText(calcPattern.format(Double.parseDouble(s)));

}

catch (Throwable exc)

{

lcd.setText("");

}

}

if (arg =="About")

{

String message ="Calculator ver. 1.0\nOpenExhibit Software\nCopyright 2004\nAll rights reserved";

JOptionPane.showMessageDialog(null, message,"About Calculator", JOptionPane.INFORMATION_MESSAGE);

}

//test for button clicks

foundKey =false;

//search for the clicked key

for (int i = 0; i><keys.length && !foundKey; i++)

{

if (e.getSource() == keys[i])

{

foundKey =true;

switch(i)

{

//number and decimal point buttons

case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:case 15:

if (clearText)

{

lcd.setText("");

clearText =false;

}

lcd.setText(lcd.getText()+ keys[i].getText());

break;

//operator buttons

case 10:case 11:case 12:case 13:case 14:

clearText =true;

if (first)//first operand

{

if(lcd.getText().length()==0) op1 = 0.0;

else op1 = Double.parseDouble(lcd.getText());

first =false;

//clearText = true;

lastOp = i;

}

else//second operand

{

switch(lastOp)

{

case 10:

op1 /= Double.parseDouble(lcd.getText());

break;

case 11:

op1 *= Double.parseDouble(lcd.getText());

break;

case 12:

op1 -= Double.parseDouble(lcd.getText());

break;

case 13:

op1 += Double.parseDouble(lcd.getText());

break;

}//end of switch(lastOp)

lcd.setText(calcPattern.format(op1));

//clearText = true;

if (i==14) first =true;

else lastOp = i;

}// end else

break;

//Add to memory button

case 16:

{

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

Transferable content = cb.getContents(this);

try

{

//get contents of clipboard

String s = (String)content.getTransferData(DataFlavor.stringFlavor);

//parse contents of clipboard into Double

Double d = Double.parseDouble(s);

//get contents of lcd, parse into Double and add to clipboard value

d += Double.parseDouble(lcd.getText());

//convert Double value into StringSelection

StringSelection contents =new StringSelection(d.toString());

//set clipboard contents

cb.setContents(contents,null);

clearText =true;

}

catch (Throwable exc)

{

lcd.setText("");

}

break;

}

//Subtract from memory button

case 17:

{

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

Transferable content = cb.getContents(this);

try

{

//get contents of clipboard

String s = (String)content.getTransferData(DataFlavor.stringFlavor);

//parse contents of clipboard into Double

Double d = Double.parseDouble(s);

//get contents of lcd, parse into Double and subtract from clipboard value

d -= Double.parseDouble(lcd.getText());

//convert Double value into StringSelection

StringSelection contents =new StringSelection(d.toString());

//set clipboard contents

cb.setContents(contents,null);

clearText =true;

}

catch (Throwable exc)

{

lcd.setText("");

}

break;

}

//Multiply with memory contents button

case 18:

{

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

Transferable content = cb.getContents(this);

try

{

//get contents of clipboard

String s = (String)content.getTransferData(DataFlavor.stringFlavor);

//parse contents of clipboard into Double

Double d = Double.parseDouble(s);

//get contents of lcd, parse into Double and multiply with clipboard value

d *= Double.parseDouble(lcd.getText());

//convert Double value into StringSelection

StringSelection contents =new StringSelection(d.toString());

//set clipboard contents

cb.setContents(contents,null);

clearText =true;

}

catch (Throwable exc)

{

lcd.setText("");

}

break;

}

//Divide by memory contents button

case 19:

{

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

Transferable content = cb.getContents(this);

try

{

//get contents of clipboard

String s = (String)content.getTransferData(DataFlavor.stringFlavor);

//parse contents of clipboard into Double

Double d = Double.parseDouble(s);

//get contents of lcd, parse into Double and divide by clipboard value

d /= Double.parseDouble(lcd.getText());

//convert Double value into StringSelection

StringSelection contents =new StringSelection(d.toString());

//set clipboard contents

cb.setContents(contents,null);

clearText =true;

}

catch (Throwable exc)

{

lcd.setText("");

}

break;

}

//Paste from memory button

case 20:

{

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

Transferable content = cb.getContents(this);

try

{

String s = (String)content.getTransferData(DataFlavor.stringFlavor);

lcd.setText(calcPattern.format(Double.parseDouble(s)));

}

catch (Throwable exc)

{

lcd.setText("");

}

break;

}

//Clear memory button

case 21:

{

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

Double d = 0.0;

StringSelection contents =new StringSelection(d.toString());

cb.setContents(contents,null);

break;

}

//Backspace button

case 22:

{

lcd.setText(lcd.getText().substring(0, lcd.getText().length()-1));

break;

}

//Clear button

case 23:

{

clearText =true;

first =true;

op1 = 0.0;

lcd.setText("");

lcd.requestFocus();

break;

}

}// end of switch(i)

}// end of if

}// end of for

}// end of actionPerformed() method

//ADDED - and not working

publicvoid keyTyped(KeyEvent e)

{

int id = e.getID();

if (id == KeyEvent.KEY_TYPED){

char c = e.getKeyChar();

int keyNum = (int)c;

switch (keyNum)

{

case 48:case 96:

keys[0].doClick(100);

break;

case 49:case 97:

keys[1].doClick(100);

break;

case 50:case 98:

keys[2].doClick(100);

break;

}

}

}

publicvoid keyPressed(KeyEvent e)

{

}

publicvoid keyReleased(KeyEvent e)

{

}

publicstaticvoid main(String args[])

{

//set frame properties

Calculator f =new Calculator();

f.setTitle("Calculator application");

f.setBounds(200, 200, 300, 300);

f.setVisible(true);

//set image properties and add to Frame

Image icon = Toolkit.getDefaultToolkit().getImage("calcImage.gif");

f.setIconImage(icon);

}// end of main

}// end of class

Thanks in advance!>

[27169 byte] By [earachefl2a] at [2007-11-27 2:53:07]
# 1
I find there usually an easier solution to most problems that people think KeyListener is the answer.In your case, why not use ActionListeners? Isn't that the first choice with buttons?
DrLaszloJamfa at 2007-7-12 3:27:46 > top of Java-index,Java Essentials,Java Programming...
# 2
D'oh!! I just answered my own question.... since the Panel is requesting focus, the Panel needs to have the KeyListener... added, solved. Thanks anyway!!
earachefl2a at 2007-7-12 3:27:46 > top of Java-index,Java Essentials,Java Programming...
# 3
If you think you need a KeyListener on a JPanel, you may be better offusing keyboard bindings: http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.htmlAgain, KeyListeners are usually not the way to go.
DrLaszloJamfa at 2007-7-12 3:27:46 > top of Java-index,Java Essentials,Java Programming...
# 4

Thanks for the answers. I'm fairly new at this, and I found the linked page tough to swallow.

I have the keypad working, with the exception of the "Clear", "/", "*", "-", "+", and "." buttons.

All numeric buttons, Equals, and Backspace (delete) work. Here is the keyTyped method code:

public void keyTyped(KeyEvent e)

{

int id = e.getID();

if (id == KeyEvent.KEY_TYPED) {

char c = e.getKeyChar();

int keyNum = (int)c;

switch (keyNum)

{

case 48: case 96:

keys[0].doClick(100);

break;

case 49: case 97:

keys[1].doClick(100);

break;

case 50: case 98:

keys[2].doClick(100);

break;

case 51: case 99:

keys[3].doClick(100);

break;

case 52: case 100:

keys[4].doClick(100);

break;

case 53: case 101:

keys[5].doClick(100);

break;

case 54: case 102:

keys[6].doClick(100);

break;

case 55: case 103:

keys[7].doClick(100);

break;

case 56: case 104:

keys[8].doClick(100);

break;

case 57: case 105:

keys[9].doClick(100);

break;

case 111://Divide

keys[10].doClick(100);

break;

case 106://Multiply

keys[11].doClick(100);

break;

case 109://Minus

keys[12].doClick(100);

break;

case 107://Plus

keys[13].doClick(100);

break;

case 61://Equals

keys[14].doClick(100);

break;

case 110://Dot

keys[15].doClick(100);

break;

case 157://Clear

keys[23].doClick(100);

break;

case 8://Backspace(Delete)

keys[22].doClick(100);

break;

}

}

}

I got the Unicode integer equivalents through Sun's KeyEventDemo code. For instance, the period "."

key shows a value of 110. Yet this key does nothing in my Calculator program.

Are you going to tell me again that I should use KeyBindings instead? Because it's going to take me a while to comprehend it...

Message was edited by:

earachefl2

earachefl2a at 2007-7-12 3:27:46 > top of Java-index,Java Essentials,Java Programming...
# 5

> Are you going to tell me again that I should use

> KeyBindings instead? Because it's going to take me a

> while to comprehend it...

It would take somebody else just as long to comprehend the code you wrote there. It's full of magic numbers. Not only did you convert the KeyEvent constants (such as KeyEvent.VK_1) into magic numbers (49), you used more magic numbers in the form of indexes into your "keys" array.

So yes, it might be a good idea for you to read that document.

DrClapa at 2007-7-12 3:27:46 > top of Java-index,Java Essentials,Java Programming...
# 6

Here's a quick demo. Perhaps that article makes it sound trickier than it

really is, in practice.

import java.awt.event.*;

import javax.swing.*;

public class MnemonicExample implements Runnable {

public void run() {

JPanel panel = new JPanel();

addButton(panel, "+", "ADD", new AddAction());

addButton(panel, "-", "SUBTRACT", new SubtractAction());

addButton(panel, "*", "MULTIPLY", new MultiplyAction());

addButton(panel, "/", "DIVIDE", new DivideAction());

JFrame f = new JFrame("MnemonicExample");

f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

f.setContentPane(panel);

f.pack();

f.setLocationRelativeTo(null);

f.setVisible(true);

}

void addButton(JPanel panel, String text, String keystrokeDescription, Action action) {

InputMap im = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);

ActionMap am = panel.getActionMap();

Object key = action.getValue(Action.NAME);

im.put(KeyStroke.getKeyStroke(keystrokeDescription), key);

am.put(key, action);

JButton btn = new JButton(text);

btn.addActionListener(action);

panel.add(btn);

}

public static void main(String[] args) {

SwingUtilities.invokeLater(new MnemonicExample());

}

}

class AddAction extends AbstractAction {

AddAction() {super("add");}

public void actionPerformed(ActionEvent evt) {

System.out.println("ADD");

}

}

class SubtractAction extends AbstractAction {

SubtractAction() {super("subtract");}

public void actionPerformed(ActionEvent evt) {

System.out.println("SUB");

}

}

class MultiplyAction extends AbstractAction {

MultiplyAction() {super("multiply");}

public void actionPerformed(ActionEvent evt) {

System.out.println("MUL");

}

}

class DivideAction extends AbstractAction {

DivideAction() {super("divide");};

public void actionPerformed(ActionEvent evt) {

System.out.println("DIV");

}

}

Note that I bound the +, -, * and / keys of the numpad, not the main keyboard.

DrLaszloJamfa at 2007-7-12 3:27:46 > top of Java-index,Java Essentials,Java Programming...
# 7

Swing related questions should be posted in the Swing forum.

Swing applications should only use Swing components. You are mixing in AWT components which will lead to trouble. In case you don't know the difference, Swing components start with a "J" (JTextField...), AWT components don't (TextField...).

Maybe the Swing tutorial on [url http://java.sun.com/docs/books/tutorial/uiswing/misc/keybinding.html]How to Use Key Bindings[/url] is an easier read.

camickra at 2007-7-12 3:27:46 > top of Java-index,Java Essentials,Java Programming...
# 8
> You are mixing in AWT componentsI didn't notice that in the OP's code. That is bad!
DrLaszloJamfa at 2007-7-12 3:27:46 > top of Java-index,Java Essentials,Java Programming...