Sign in to follow this  
RobAU78

[java] A Strange Problem with Keyboard Input and Focus

Recommended Posts

While working on my interface shell, I found a strange problem. If I don't include a setFocusable(true) statement in my game screen constructor, they won't be able to respond to keyboard input. However, they are already focusable beforehand! So I don't understand why I have to include that statement. Does anyone know? Here's my code to explain things better: InterfaceShell class:
package interfaceShell;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;

public class InterfaceShell{

	public static final int WIDTH = 800;
	public static final int HEIGHT = 600;

	private JFrame frame;

	// Constructor
	public InterfaceShell{
		frame = new JFrame("Interface Shell");
		frame.setContentPane(new SplashScreen());
		frame.setBounds(0, 0, WIDTH, HEIGHT);
		frame.setVisible(true);
		frame.addWindowListener(new WindowAdapter(){
			public void windowClosing(WindowEvent e){
				System.exit(0);
			}
		});
	}

	// Initializes an InterfaceShell object
	public static void main(String[] args){
		InterfaceShell ui = new InterfaceShell();
	}
}


GameScreen class:
package interfaceShell;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;

public class GameScreen extends JPanel implements ActionListener, KeyListener{

	private GameScreen parentScreen;
	private GameScreen childScreen;
	private boolean active;

	// Constructor
	public GameScreen(GameScreen parentScreen){
		this.parentScreen = parentScreen;
		childScreen = null;
		setPreferredSize(new Dimension(InterfaceShell.WIDTH, InterfaceShell.HEIGHT));
		setFocusable(true);  // This *MUST* be here for keyboard input to work correctly!!!
		setVisible(true);
		setActive(true);
		addKeyListener(this);
	}

	// Sets the child screen
	public void setChildScreen(GameScreen screen){
		if(screen != null){
			child = screen;
			add(child);
			setActive(false);
		}
		else{
			remove(child);
			child = null;
			setActive(true);
		}
		validate();
	}

	// Sets active properties (enabled, etc.)
	public void setActive(boolean b){
		active = b;
		setEnabled(b);
	}

	// Getter methods
	public GameScreen getParentScreen(){ return parentScreen; }
	public GameScreen getChildScreen(){ return childScreen; }
	public boolean isActive(){ return active; }

	// To be overridden by subclasses
	public void actionPerformed(ActionEvent e){ }
	public void keyPressed(KeyEvent e){ e.consume(); }
	public void keyReleased(KeyEvent e){ e.consume(); }
	public void keyTyped(KeyEvent e){ e.consume(); }
}


SplashScreen class:
package interfaceShell;
import java.awt.event.ActionEvent;
import javax.swing.JButton;

public class SplashScreen extends GameScreen{

	private JButton newGameButton;

	// Constructor
	public SplashScreen(){
		super(null);
		newGameButton = new JButton("New Game");
		newGameButton.addActionListener(this);
		add(newGameButton);
	}

	// Create and load new game screen when button is clicked
	public void actionPerformed(ActionEvent e){
		Object src = e.getSource();
		if(src == newGameButton) setChildScreen(new GalaxyScreen());
	}

	// Makes button invisible when not active
	public void setActive(boolean b){
		super.setActive(b);
		newGameButton.setVisible(b);
	}
}


GalaxyScreen class:
package interfaceShell;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JLabel;

public class GalaxyScreen extends GameScreen{

	private JButton nextTurnButton;
	private JLabel turnLabel;
	private int turnCount;

	// Constructor
	public GalaxyScreen(GameScreen parentScreen){
		super(parentScreen);
		turnCount = 0;
		turnLabel = new JLabel("Turn: " + turnCount);
		nextTurnButton = new JButton("Next Turn");
		nextTurnButton.addActionListener(this);
		add(turnLabel);
		add(nextTurnButton);
	}

	// Increments turn count when button is clicked
	public void actionPerformed(ActionEvent e){
		Object src = e.getSource();
		if(src == nextTurnButton){
			turnCount++;
			turnLabel.setText("Turn: " + turnCount);
		}
	}

	// Exits this screen when escape key is pressed
	public void keyPressed(KeyEvent e){
		int keyCode = e.getKeyCode();
		if(keyCode == KeyEvent.VK_ESC) parentScreen.setChildScreen(null);
		else e.consume();
	}
}


I hope the code above is clear enough. Feel free to let me know of any errors or improvements. Thanks, Rob

Share this post


Link to post
Share on other sites
to receive events, objects need not only to be FOCUSABLE butr also to be FOCUSED. maybe before they were focusable but callnig the command makes them focused? (i'm not sure) anyway if you want to receive key events for your whole application and not for a specific component you should use a keyeventdispatcher you'd get all events no matter what's focused

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this