Swing Wie bekomme ich den Fokus für einen JPanel

icarus2

Top Contributor
Hi

Nachdem ich mich etwas mit anderen Programmiersprachen beschäftigt habe möchte ich nun wieder einmal ein 2D Spiel in Java programmieren. Ich habe dabei einen JFrame, den ich maximiere. Dieser hat dann by default den Fokus. Zu Beginn wird ein Menu (in einem JPanel, der mit maximaler Grösse auf dem JFrame liegt) angezeigt mit einem Start JButton. Wenn dieser angeklickt wird soll der JPanel "GamePanel" mit maximaler Grösse erscheinen. Dem GamePanel möchte ich nun einen KeyListener hinzufügen, was jedoch nicht so läuft wie ich möchte, da der GamePanel nie den Fokus bekommt.

Da es bereits einiges an Code ist kann ich schlecht den ganzen Code zeigen. Ich kann euch aber ein par Ausschnitte geben. Wenn ihr mehr braucht kann ich das dann gerne nachliefern:

Der Konstruktor der Klasse Main (Main ist von JFrame abgeleitet)
[Java]
private Main() {

container = getContentPane();

showMenu();
initializeMainWindow();

}
[/Java]

und noch zwei Methoden aus der Klasse Main:
[Java]
public void showMenu(){

container.removeAll();

if( gameMenu == null )
gameMenu = new GameMenu( this );

container.add( gameMenu );
container.repaint();

}

private void initializeMainWindow() {

this.setExtendedState( JFrame.MAXIMIZED_BOTH );
this.setUndecorated( true );
this.setVisible( true );
this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );


}
[/Java]

Um nun den GamePanel anzuzeigen habe ich folgende Methode in der Klasse Main:
[Java]
public void showGamePanel(){

container.removeAll();

gamePanel = new GamePanel( this );

container.add( gamePanel );
container.repaint();
}
[/Java]

Der Konstruktor von GamePanel sieht so aus:
[Java]
protected GamePanel( Main main ) {

this.main = main;
this.setSize( Main.getScreenWidth(), Main.getScreenHeight() );
this.addKeyListener( new GamePanelListener( this ) );
initializeLists(); // creates lists and adds the levels


}
[/Java]

Ich hatte schon früher Probleme damit, dass die gewünschte Komponente nicht den Fokus hat. Wenn ich den KeyListener auf den JFrame lege, dann funktioniert er. Ich möchte den KeyListener aber lieber nicht auf dem JFrame.
Hat jemand eine Idee wie ich dieses Problem lösen kann? Ich habe schon diverse Dinge ausprobiert wie 'requestFocusInWindow()' und solches. Hat leider nichts gebracht.

PS:
Ich hoffe, dass ich mich verständlich ausdrücken konnte ^^
 
wann und wo rufst du das auf?, vor oder nach setVisible(true) kann schon einen Unterschied machen,

ein vollständiges Beispielprogramm wäre günstig, so wie es hier funktioniert:
Java:
public class Test {
    public static void main(String[] args)  {
        JFrame f = new JFrame();
        JPanel p = new JPanel();
        f.add(p);
        p.addKeyListener(new KeyAdapter() {
                public void keyTyped(KeyEvent e)  {
                    System.out.println("key: " + e.getKeyChar());
                }
            });
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(200, 200);
        f.setVisible(true);
        p.requestFocusInWindow();
    }
}

generell sind KeyBindings vielleicht die bessere Idee, hier KeyListener mit Links zu Bindings + Focus:
How to Write a Key Listener (The Java™ Tutorials > Creating a GUI With JFC/Swing > Writing Event Listeners)
 
Ich kenne mich mit KeyBindings nicht aus... aber ich werde mir das ganze mal durchlesen und beibringen.

Der folgende Code ist kompilierbar. Ich habe so viel rausgenommen wie nur möglich.

Klasse Main:
[Java]

package surface;

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Toolkit;

import javax.swing.JFrame;

public class Main extends JFrame {


private static final long serialVersionUID = 1L;
private static final int SCREEN_WIDTH = Toolkit.getDefaultToolkit().getScreenSize().width;
private static final int SCREEN_HEIGHT = Toolkit.getDefaultToolkit().getScreenSize().height;

private GameMenu gameMenu;
private GamePanel gamePanel;

private Container container;

private Main() {

container = getContentPane();

showMenu();
initializeMainWindow();

}

private void initializeMainWindow() {

this.setExtendedState( JFrame.MAXIMIZED_BOTH );
this.setUndecorated( true );
this.setVisible( true );
this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

}

public static int getScreenWidth(){
return SCREEN_WIDTH;
}

public static int getScreenHeight(){
return SCREEN_HEIGHT;
}

/**
* Removes all components from the Container and adds the game menu to it. It automatically creates a game menu
* at first usage
*/
public void showMenu(){

container.removeAll();

if( gameMenu == null )
gameMenu = new GameMenu( this );

container.add( gameMenu );
container.repaint();

}

public void showGamePanel(){

container.removeAll();

gamePanel = new GamePanel( this );

container.add( gamePanel );
container.repaint();
}

public static void main( String[] args ) {

EventQueue.invokeLater( new Runnable() {
@Override
public void run(){
new Main();
}
});

}

}
[/Java]

Klasse GameMenu
[Java]

package surface;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.JButton;
import javax.swing.JPanel;

public class GameMenu extends JPanel implements ActionListener {

private static final long serialVersionUID = 1L;

private JButton startButton, controlsButton, exitButton;

private Main main;

protected GameMenu( Main main ){

this.main = main;

this.setSize( Main.getScreenWidth(), Main.getScreenHeight() );
this.setLayout( null );

initializeButtons();
}

private void initializeButtons(){

// The button width and height is adjusted relative to the screen dimensions
final int buttonWidth = Main.getScreenWidth() / 4;
final int buttonHeight = Main.getScreenHeight() / 10;
final int xLocationCentered = Main.getScreenWidth() / 2 - buttonWidth / 2;
int yLocation = Main.getScreenHeight() / 3; // y location of the start button
final int yButtonDistance = 40; // vertical distance between the buttons

Font font = new Font( "Arial", Font.BOLD, 35); // new font is created for the buttons

startButton = new JButton("Start Game!");
startButton.setSize( buttonWidth, buttonHeight );
startButton.setLocation( xLocationCentered, yLocation );
startButton.setFont( font );
startButton.setFocusable( false );
startButton.addActionListener( this );
yLocation += yButtonDistance + buttonHeight; // y coordinate is adjusted

controlsButton = new JButton("Controls");
controlsButton.setSize( buttonWidth, buttonHeight );
controlsButton.setLocation( xLocationCentered, yLocation );
controlsButton.setFont( font );
controlsButton.setFocusable( false );
controlsButton.addActionListener( this );
yLocation += yButtonDistance + buttonHeight; // y coordinate is adjusted

exitButton = new JButton("Exit");
exitButton.setSize( buttonWidth, buttonHeight );
exitButton.setLocation( xLocationCentered, yLocation );
exitButton.setFont( font );
exitButton.setFocusable( false );
exitButton.addActionListener( this );

add( startButton );
add( controlsButton );
add( exitButton );

}

@Override
protected void paintComponent( Graphics g ) {
super.paintComponent( g );

g.drawString("GameMenu", 400, 400);


}

@Override
public void actionPerformed( ActionEvent e ) {

if( e.getSource().equals( startButton )){
main.showGamePanel();
}

else if( e.getSource().equals( controlsButton ) ) {

}

else if( e.getSource().equals( exitButton ) ) {
System.exit( 0 );
}

}


}
[/Java]

GamePanel:
[Java]

package surface;



import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JPanel;


public class GamePanel extends JPanel {

private Main main;

protected GamePanel( Main main ) {

this.main = main;
this.setSize( Main.getScreenWidth(), Main.getScreenHeight() );
this.requestFocusInWindow();
this.addKeyListener( new GamePanelListener( this ) );

}



@Override
public void paintComponent( Graphics g ){
super.paintComponent( g );

g.drawString("GamePanel", 400, 400);

}

}
[/Java]

GamePanelListener
[Java]

package surface;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class GamePanelListener extends KeyAdapter {

private GamePanel gamePanel;

protected GamePanelListener( GamePanel gamePanel ){
this.gamePanel = gamePanel;
}

@Override
public void keyReleased( KeyEvent e ){

System.out.println("GamePanelListener: Key Released");

}

}

[/Java]
 
Zuletzt bearbeitet:
hast du auch dein Denken rausgenommen? sorry 😉

requestFocusInWindow(); steht hier doch im GamePanel-Konstruktor, also VOR setVisible(true),
genau wie ich es in meiner ersten Antwort schon als mögliche Problem-Ursache angemerkt hatte
 
slaterb hat gesagt.:
hast du auch dein denken rausgenommen? Sorry 😉
Nicht mit Absicht aber es sieht ganz danach aus ^^

slaterb hat gesagt.:
requestfocusinwindow(); steht hier doch im gamepanel-konstruktor, also vor setvisible(true),
genau wie ich es in meiner ersten antwort schon als mögliche problem-ursache angemerkt hatte

Ich rufe gamePanel.requestFocusInWindow() jetzt in der Methode showGamePanel() nach dem container.repaint() auf und es klappt. Mir war irgendwie nicht klar, dass da ja eigentlich ein setVisible vorkommt, weil es nicht explizit da steht.

Vielen Dank für die Hilfe!

PS:
Werde mir die KeyBindings trotzdem mal anschauen.
 

Zurück
Oben