Showing posts with label LISTENERS. Show all posts
Showing posts with label LISTENERS. Show all posts

Saturday, 15 September 2012

Interactive Keyboard Input In Java: KeyListeners

n a console application, you can get keyboard input using the Scanner class, as described in Keyboard Input for Console Apps. In an graphical app, though, you can use one of the classes built to accept text input (e.g. TextArea or JTextField) or add code to your application to respond directly to the keyboard.

Keyboard input in the Java GUI made simple.
Playing with Today's Program

There are two basic ways of doing this. One is to set up Key Bindings, which maps keystrokes to actions in your application similar to accelerator keys or menu keyboard equivalents. The other is to use a Key Listener, similar to the Mouse Listener, which I detailed in Simple Mouse Interaction.

In this example we're going to use Key Listeners. There is less overhead to setting up a KeyListener when you just need to use a few keys. Key Bindings require more overhead to set up, but when you want to bind actions to a lot of different keystrokes, and manage the actions bound to particular keystrokes at a higher level, Key Bindings are better to use than a simple KeyListener.

As its name implies, a KeyListener is an Event Listener. If you're not sure what that is, read my article on Listeners or follow the prior link to Oracle/Sun's description.

Here's a program that demonstrates simple keyboard interaction. It's based on the MousePanel program I presented in Simple Mouse Interaction. It acts as a sort of "Etch-a-Sketch". You can download the KeyPanel program source from my Java code site.

// Import the basic necessary classes.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class KeyPanel extends JPanel implements KeyListener{

public KeyPanel(){
super();
pointX=0;
pointY=0;
oldX=0;
oldY=0;
addKeyListener(this);
}

int pointX, pointY, oldX, oldY;
boolean erase;

public void paintComponent(Graphics g){
// Erase the board if it's been requested.
if (erase) {
g.clearRect(0, 0 , getBounds().width, getBounds().height);
erase = false; // We're done, turn off this flag now.
}

// Draw gray where the pointer was..
g.setColor(Color.GRAY);
g.fillRect(oldX-2, oldY-2, 4, 4);
// Draw "Cursor" at current location in black.
g.setColor(Color.BLACK);
g.fillRect(pointX-2,pointY-2, 4, 4);
}

public void keyPressed(KeyEvent key){

// Copy the last clicked location into the 'old' variables.
oldX=pointX;
oldY=pointY;
// Move the current point depending on which key was pressed.
if (key.getKeyCode() == key.VK_DOWN){
pointY=pointY+5;
if (pointY > getBounds().height){
pointY=getBounds().height;
}
}
if (key.getKeyCode() == key.VK_UP){
pointY=pointY-5;
if (pointY < 0){pointY=0;}
}
if (key.getKeyCode() == key.VK_LEFT){
pointX=pointX-5;
if (pointX < 0){pointX=0;}
}
if (key.getKeyCode() == key.VK_RIGHT){
pointX=pointX+5;
if (pointX > getBounds().width){
pointX=getBounds().width;
}
}

// Set a flag to erase the screen if Space is pressed.
if (key.getKeyCode() == key.VK_SPACE){
erase = true;
}


// Tell the panel that we need to redraw things.
repaint();
}

/* The following methods have to be here to comply
with the MouseListener interface, but we don't
use them, so their code blocks are empty. */
public void keyTyped(KeyEvent key){ }
public void keyReleased(KeyEvent key){ }

public static void main(String arg[]){
JFrame frame = new JFrame("Use Arrows to Draw, Space to Erase.");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(640,480);

KeyPanel panel = new KeyPanel();
frame.setContentPane(panel);
frame.setVisible(true);

// We *must* do this to see KeyEvents.
panel.setFocusable(true);


// Initialize the drawing pointer.
panel.oldX=panel.getBounds().width/2;
panel.oldY=panel.getBounds().height/2;
panel.pointX=panel.oldX;
panel.pointY=panel.oldY;

}
}

Using this technique with the Simple Video Game Kernel would be similar. The VGKernel would extend KeyListener, register itself, and implement the KeyListener methods. But in those methods, rather than performing the operations that result from the keypress, as in this program, you would want to simply set a flag to show that the key has been pressed. Then, in your core game logic you would test to see whether the key has been pressed, and perform the appropriate actions.

That way the actions are performed at the appropriate time in your game, and not just whenever the key happens to get pressed. Reacting to a key when it is pressed is appropriate for a turn-based game, but not for a real-time game. In a real-time game the action happens according to the timing of the TimerTask that drives the game, which is why we just note that a key has been pressed, and wait until the TimerTask occurs to actually conduct the action related to that key. This would be similar to what we do with the space key here, which sets a flag to tell paintComponent() to erase the screen.

Give this program a try, see if you can extend it to allow the user to select colors to draw with or change the size of the drawing pen.

Friday, 14 September 2012

Listeners: Can Java Hear You?

In the program given in Simple Mouse Interaction in Java we used a Listener to connect our program to the mouse. We were able to use the Listener to find out when and where the mouse had been clicked within our program's window. We used this information to draw lines in the window, in a similar fashion to our earlier programs but controlled by the mouse, rather than from specific drawing instructions in the program that do the same thing every time you run the program.

By implementing the MouseListener interface, we were able to talk to the mouse very easily. However, a MouseListener is just one of the many types of Listener available in Java.

What Sort of Listener Are You?


If we tried to implement a full graphical interface in a program by using MouseListener, we'd have a lot of work to do. We'd have to draw all the buttons using drawing instructions in our program, then wait for mouse clicks. Once we got a click on something we'd have to figure out what had been clicked on by the location of the click in our window. Then we'd have to redraw the button (or slider, or whatever) to show that it had been clicked. Then we'd have to continue responding to the mouse by figuring out what the user is doing (clicking and releasing, clicking and dragging, or what) and make our program and the picture on the screen respond appropriately.

Whew! What a lot of work that would be! If we had to do that it'd almost be enough to drive you back to the command line. (Once upon a time you did have to do all that, believe it or not. Thank goodness those days are gone.)

Fortunately, Java has a number of different types of Listener suited for different tasks that take care of the complexity for you, and let your program do what you want it to do without you having to spell out every little detail. There are listeners for buttons, sliders and other controls such as trees, lists, focus changes, and so on. You can also write your own Listeners, so that you can create a new type of control and only ever have to deal with the inner complexities of how it works once.

What is a Listener?

A Listener is a connection between your program and something that creates an Event. An Event is a communication from a device on the computer or a program to something outside itself on the computer, like another program or device (through a program associated with that device, like its device driver.) In the case of our mouse program, the Listener is the connection between the mouse's button and our program. Whenever the left button is clicked, an Event is generated that contains what happened (a left-click) and the location in our window where it happened.

If the click happens outside our program's window, an Event is generated, but our program isn't included in the recipients of the message. If our program doesn't have a Listener, the mouse click Event gets passed to our program but is ignored.

If we have a Listener appropriate to an Event that's passed to our program, then something happens with it. So we have to create a Listener, tell our program to add it to its list of Listeners (which also tells the program what kind of events it's listening for.) Then our program will tell that Listener whenever one of those events happens, and pass along the details.

A MouseListener is a very generalized sort of Listener. An ActionListener, however, can be set up to listen for a specific event, like a click on a specific button. Take a look in the Java API at ActionListener. By associating an ActionListener with a specific button, you can hear only about when that button is clicked, and know that that's precisely what happened. That ActionListener won't hear about clicks elsewhere, you don't need to check the location of the click, and it won't tell you about anything else you don't want to hear about.

That saves you all the trouble that I talked about above. You can set up your ActionListener to just exeecute the part of your code you want it to run when someone clicks the button. Or slides the slider, or whatever. No fuss, no muss.

I'll be posting a sample program soon, until then have a look at the Sun Java Tutorials on using buttons and other controls from Swing (Java's best set of graphical user interface objects.)

Simple Mouse Interaction in Java

Building on the basic graphics we've done in Very Basic Java Graphics--3 Examples and A Most Basic Graphics App we'll be adding mouse interaction in this example.

/* MousePanel.java

    This program adds the ability to respond to
    mouse events to the BasicPanel program.

    The mouse clicks are used to draw on the
    panel area using drawline instructions.

    mag-30Apr2008
*/

// Import the basic necessary classes.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class MousePanel extends JPanel implements MouseListener{

    public MousePanel(){
        super();
        pointX=20;
        pointY=20;
        oldX=0;
        oldY=0;
        addMouseListener(this);   
    }

    int pointX, pointY, oldX, oldY;

    public void paintComponent(Graphics g){
    // Draw a line from the prior mouse click to new one.
        g.drawLine(oldX,oldY,pointX,pointY);
    }

    public void mouseClicked(MouseEvent mouse){
    // Copy the last clicked location into the 'old' variables.
        oldX=pointX;
        oldY=pointY;
    // Get the location of the current mouse click.
        pointX = mouse.getX();
        pointY = mouse.getY();
    // Tell the panel that we need to redraw things.
        repaint();
    }

/* The following methods have to be here to comply
   with the MouseListener interface, but we don't
   use them, so their code blocks are empty. */
    public void mouseEntered(MouseEvent mouse){ }   
    public void mouseExited(MouseEvent mouse){ }
    public void mousePressed(MouseEvent mouse){ }
    public void mouseReleased(MouseEvent mouse){ }

    public static void main(String arg[]){
        JFrame frame = new JFrame("MousePanel");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(250,250);

        MousePanel panel = new MousePanel();
        frame.setContentPane(panel);
        frame.setVisible(true);
    }
}

To add mouse interaction, we're taking advantage of an interface from the java.awt.events package called MouseListener. When we implement MouseListener as part of our JPanel class, then call addMouseListener(this) as part of our MousePanel() constructor, our program is "wired up" to the mouse, so that our MousePanel gets mouse events from the JVM.

Implementing the MouseListener interface requires that we implement all the methods called for by the interface. We only use MouseClicked() in this example, however, so it's the only one that has code in its code block.

This program lets you draw by clicking the mouse at various places in the MousePanel's drawing area, drawing a line from the prior click location (or from top left for the first click) to the location of the current click.

Here's a little challenge for you:

Change the program so that it doesn't draw a line from upper left on the first click, but instead draws a dot in the location of the first click. Later clicks will then draw lines from the prior click's location.

Hint: If you set the initial oldX and oldY values to a value that isn't in the visible MousePanel area, you can tell if there haven't been any prior clicks.