Tutorial 3

From Java Tutorials

Jump to: navigation, search

Handling keyboard input is somewhat similar to handling mouse input. You have to implement a Listener interface, and then add the listener.

Contents



[edit] Key events

We will implement the KeyListener interface in this example, i.e.,

public class Tutorial_2b extends Applet implements KeyListener {
  public void init() {
    addKeyListener(this);
  }
}

This time, there are only three methods to be implemented, they are:

void 	keyPressed(KeyEvent e)
         Invoked when a key has been pressed.
void 	keyReleased(KeyEvent e)
         Invoked when a key has been released.
void 	keyTyped(KeyEvent e)
         Invoked when a key has been typed.

We'll just look at keyPressed().

More information available here

[edit] Exercise 1

What we aim to do is have a basic applet that lets people type stuff into the applet, and have it displayed on the applet itself. We're not attempting to create a text editor, rather, just see how the KeyListener stuff works, and also get a bit of a feel for drawing text to the applet.

The getKeyChar() method in the KeyListener class returns the character that was pressed that caused the KeyEvent. For example, if someone presses the 'a' key, then getKeyChar() will return the char 'a'. If someone presses shift+'a', getKeyChar() returns 'A'. If the '9' key is pressed, then getKeyChar() returns '9'. You get the idea.

The first task is to capture the text that someone types on the keyboard, using getKeyChar() and the drawString() method of the Graphics class. If you don't remember how that works, to draw "Hello world" at coordinates (100,150) on the applet, you would use the command g.drawString("Hello world", 100, 150); (where g is a reference to a Graphics object). Just assume that the user is typing alphanumeric characters only (ie, a-z, A-Z, 0-9) and doesn't press the backspace key. After each key press, draw the previously typed text to the applet at the coordinates (10,10).

Hint __HIDER__

You'll need to have an instance variable (a String) to store the set of previously typed characters. Each time getKeyChar() is called, you need to update this String. Then, in getKeyChar(), draw the String at (10,10) every time a key is pressed.

Sample solution: __HIDER__
import java.awt.*;
import java.applet.*;
import java.awt.event.*; // for KeyListener

public class Tutorial_3 extends Applet implements KeyListener {

	private String currentLine; // holds the text of the string we are typing

	public void init() {
		currentLine = ""; // initialise the string to be empty
		addKeyListener(this); // so the following methods are called when
				      // a key is pressed
	}

	public void paint(Graphics g) {}
 	public void keyTyped(KeyEvent e) {}
	public void keyReleased(KeyEvent e) {}
 	public void keyPressed(KeyEvent e)
 	{
 		currentLine += e.getKeyChar(); // add the typed character to the string
 		Graphics g = getGraphics(); // so we can draw
		g.setColor(Color.black); // make the text black
		g.drawString(currentLine, 10, 10); // draw the typed string at (10,10)
 	} 	
}

[edit] Exercise 2

It would be nice if instead of using magic numbers like in the previous example, the initial drawing position of 10 pixels down could be replaced by a number dependent on the font being used to draw the text. What would happen if your text is more than 10 pixels tall? Conveniently, the java.awt.FontMetrics class can help us out. For a Graphics object g, you can use the command g.getFontMetrics() to get a reference to an instance of the FontMetrics class.

One of the useful methods in the FontMetrics class is the getHeight() method, which returns "the standard height of a line of text in this font. This is the distance between the baseline of adjacent lines of text" (from the API). So, by replacing one of the magic numbers with the number returned by getHeight(), the position of the String that is being drawn will depend on the size of the font we are drawing with.

Experiment with using different Fonts and font sizes. To create a new Font, try using g.setFont(new Font("SansSerif", Font.PLAIN, 24)) where 24 is the size of the font - if you change this number, does your code still draw the text such that the entire text is visible, and not going outside the borders of the applet?

Sample solution: __HIDER__
 	public void keyPressed(KeyEvent e)
 	{
 		currentLine += e.getKeyChar();
 		Graphics g = getGraphics();
		g.setFont(new Font("SansSerif", Font.PLAIN, 24)); // set the new font size
 		FontMetrics fm = g.getFontMetrics(); // get FontMetrics for the current font
		g.setColor(Color.black);

		// draw the string at (10, height_of_text)
		g.drawString(currentLine, 10, fm.getHeight());
 	}



[edit] Exercise 3

Now that we know the height of a line of text, it's possible to draw several lines of text such that they are correctly spaced. Your task now is to allow someone to enter several lines of text. You can register when the return or enter keys are pressed by detecting if getKeyChar() returns a newline character '\n'. If this occurs, you should tell your program to increase the y position at which your String is drawn, and empty the String too (so each new line is empty to start with).

Hint __HIDER__

Use another instance variable to keep track of how many lines have been drawn already - when getKeyChar() returns '\n', increment this number. Then, when drawing the String, multiply the number of lines by the height of a line of text to determine the y position at which to draw the text.

Sample solution: __HIDER__
import java.awt.*;
import java.applet.*;
import java.awt.event.*; // for KeyListener

public class Tutorial_3 extends Applet implements KeyListener {

	private String currentLine; // holds the text of the string we are typing
	private int nLines; // the number of lines of text we have drawn

	public void init() {
		nLines = 1; // initially, we've just got one line of text
		currentLine = ""; // initialise the string to be empty
		addKeyListener(this); // so the following methods are called when
				      // a key is pressed
	}

	public void paint(Graphics g) {}
 	public void keyTyped(KeyEvent e) {}
	public void keyReleased(KeyEvent e) {}
 	public void keyPressed(KeyEvent e)
 	{
 		char keyCode = e.getKeyChar();
 		if (keyCode == '\n') // if it's a new line, then update our variables
 		{
 			currentLine = ""; // clear the stored text for this line
 			++nLines; // and increment the number of lines we have drawn
 					  // so we don't draw over existing text!
 		} else 

 		currentLine += keyCode;
 		Graphics g = getGraphics();
		FontMetrics fm = g.getFontMetrics();
		g.setColor(Color.black); // now draw the text
		g.drawString(currentLine, 10, fm.getHeight()*nLines);
 	} 	
}

[edit] Exercise 4

Another convenient method that the FontMetrics class provides is stringWidth(s) which returns the width, in pixels, of the String s. We can use this to centre the text that is being drawn. Modify your code from Exercise 1 such that it now centres your one line of text.

You'll need to clear the applet each time you draw the text, otherwise your text will be drawn on top of previous text and it will look yucky. This wasn't a problem before because the x location used to draw the text was always the same, but when the text is centred, it moves as the width of the text changes.

Sample code: __HIDER__
 g.setColor(Color.white); // set the colour to the background colour
 g.fillRect(0, 0, getWidth(), getHeight()); // fill the entire applet
 g.setColor(Color.black); // set the colour back to the text colour
 g.drawString(...); // draw the string!

Hint __HIDER__

Use (getWidth()-fm.stringWidth("centre this text"))/2 to get the x location at which to draw the text, where fm is your FontMetrics object, and getWidth() returns the width of the applet in pixels. You can expand this into two parts, getWidth()/2, which is half of the width of the applet in pixels (the number of pixels from the side of the applet to the centre!), and -fm.stringWidth("centre this text")/2, which is half of the width of the text in pixels. This works because if we go to the centre of the applet, and then take off half of the width of the text, and then draw the text, that will cause half of the text to be on the left of the centre, and half on the right of the centre, ie, the text is centred!

Sample solution: __HIDER__
 	public void keyPressed(KeyEvent e)
 	{
 		currentLine += e.getKeyChar();
 		Graphics g = getGraphics();
		g.setFont(new Font("SansSerif", Font.PLAIN, 24));
 		FontMetrics fm = g.getFontMetrics();
		g.setColor(Color.white); // change the colour to white
		g.fillRect(0, 0, getWidth(), getHeight()); // so we clear the applet
		g.setColor(Color.black); // now draw the text in black
		// centre the text now
		g.drawString(currentLine, (getWidth()-fm.stringWidth(currentLine))/2,
			fm.getHeight());
 	}

You'll notice that the applet seems to flicker when you type now. This is because initially, there is text drawn on the screen, then you clear it to remove the text, and then you draw the text again. The flickering occurs because of the clearing process, and is the same as the flickering that you see in the Slime games. There is a way around this which will be covered in a future tutorial.


Java Games Programming Tutorials

Getting started
Tutorial 1 - Hello World | Tutorial 2 - Handling mouse input
Tutorial 3 - Handling Key Input | Tutorial 4 - Threads and Animations
to be continued...
oneslime.net programming forum

Personal tools