JPanel KeyListener not responding.

I have a class extending JPanel and implementing a KeyListener. For some reason, the KeyListener does not respond to keys being pressed or released until I hit the TAB key once. I assume that the panel doesn’t have focus for some reason, which I thought setFocusable(true); and requestFocus(); would fix. What am I doing wrong?

EDIT: I placed requestFocus() in the updateGame() method and now it works. The updateGame() method is being called from an outside class that’s extending JFrame (where my game loop is). Is this solution “ok”, or should I make it some other way?

package tw;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

import javax.swing.JPanel;

/**
 * .
 */
public class GameBoard extends JPanel implements KeyListener {
	
	private Tank tank;
	
	public GameBoard() {
		
		// Setup panel
		setFocusable(true);
		requestFocus();
		setBackground(Color.WHITE);
		setDoubleBuffered(true);
		setSize(GameServer.W_WIDTH, GameServer.W_HEIGHT);

		addKeyListener(this);
		//setFocusTraversalKeysEnabled(false);
				
		// Initialize entities
		tank = new Tank(1, true, 200, 100, 1);
	}
	
	/**
	 * Update game logic
	 */
	public void updateGame() {
		//System.out.println("updating");
		tank.update();
	}
		
	/**
	 * Draw game objects
	 */
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		
		//System.out.println("drawing");
		tank.draw(g);
				
		Toolkit.getDefaultToolkit().sync();
		g.dispose();
	}
	
	
	// Handling key input
	/////////////////////////////////////////////
	/**
	 * Key input - when key is pressed.
	 */
	public void keyPressed(KeyEvent e) {
		System.out.println("KEY PRESSED!");
		tank.keyPressed(e);		
	}
	
	/**
	 * Key input - when key is released.
	 */
	public void keyReleased(KeyEvent e) {
		System.out.println("KEY RELEASED!");
		tank.keyReleased(e);			
	}
	
	public void keyTyped(KeyEvent e) {
		// do nothing
	}

}

Generally you want to avoid requesting focus repeatedly in a game loop. It causes the user to have a terrible user experience if they try to click anywhere different for focus, and it also performs differently on different OSes (Mac and PC).

Instead you should figure out who is stealing your focus and fix that. Or you can use something like JInput which doesn’t need focus to work.

Ugh. I support Eli’s statement.

Focus is one of the most frustrating systems I have ever come across in Gui programming. Especially with Swing’s. Ugh. Ugh. Double and triple ugh.

Instead of requesting focus like that (Which as Eli said will cause lots of fun system dependent), it also causes a spam of FocusGained and FocusLost events that will flood your system if anything else had focus. Especially since there is stuff like temporary focus loss (Through JOption Panes/window changes) and other such fun. If you can avoid having to deal with it in general, you should.

JInput works fairly well from what I’ve see. It’s simple, rather intuitive. The only difference is that it’s a Polling system, rather than an Listener system, which may/may-not work for what you need. If I’m wrong about that, please do correct me.

Hehe, had a feeling that was a bad idea. Anyhow, I solved it by not requesting focus in the GameBoard constructor but rather in the class that adds the GameBoard:


gameBoard = new GameBoard();
add(gameBoard);
gameBoard.requestFocusInWindow();

Now it seems to work the way it should!

Generally you should avoid extending JPanel at all, rather you should extend JComponent if you really need Swing.

Preferrably, it is best to use a Canvas + BufferStrategy.

Show us your JFrame class. I had the exact same problem today and I ended up fixing it by messing with the way things were ordered in there.

Also, I don’t think you need requestFocus() at all if you fix the underlying problem, setFocusable(true) should be enough.

@ra4king

Could you kinda briefly explain what the canvas/buffer strategy is?

The last paragraph and the code should explain it :slight_smile:

I see. That seems pretty cool.

What’s the point to having the double do-while loops though?

If I’m understanding this right, the pseudo-code for the loop would be something like:


As long as I'm running...
         update the game logic.

         I will...
                     I will...
                                  Borrow the buffer's graphics object.

                                  Do my drawing with it.

                                  Toss it, I'm done drawing.
                     If there is still things that need to be drawn on the buffer, I'll do it again.
         If the buffer's been flipped and drawn, I'll do it again.

         Take a nap.

I’m not seeing the need for 2 do-while loops for drawing…Oh…wait. Double buffering…right…

So, what exactly does contentsRestored() and contentsLost() check for? The javadoc doesn’t explain it well.

The JavaDocs says to read VolatileImage’s JavaDoc for a discussion on lost and restored buffers.