Java Changing Content Pane

1.8k views Asked by At

I have recently been making a game and came across a problem I could not solve. My problem is with removing the content pane of a JFrame and setting as something else. While this works, the KeyListener in the class of the content pane does not work unless I change the primary window on the computer to something else then back to the JFrame.

I replicated the problem in a smaller amount of code than what is was originally:

import java.awt.*;
import java.awt.event.*;
import javax.awt.swing.*;

public class TheFrame extends JFrame{

    private JButton play;
    private FirstPanel fp;
    private SecondPanel sp;

    public TheFrame(){

        setSize(800, 600);
        setLocationRelativeTo(null);
        setResizable(false);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        fp = new FirstPanel();
        setContentPane(fp);

        setVisible(true);
    }

    public static void main(String args[]){

        TheFrame tf = new TheFrame();
    }

    class FirstPanel() extends JPanel{

        private boolean test = false;

        public FirstPanel(){

            play = new JButton("play");
            play.addActionListener(new PlayListener());
            add(play);
        }

        public void paintComponent(Graphics g){

            if(test == true){

                sp = new SecondPanel();
                removeAll();
                revalidate();
                setContentPane(sp);
            }
        }

        class PlayListener implements ActionListener{

            public void actionPerformed(ActionEvent e){

                test = true;
                repaint();
            }
        }
    }
}

Here is also the code for the class SecondPanel:

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;

public class SecondPanel extends JPanel implements KeyListener{

    private int draw = 0;

    public SecondPanel(){

        addKeyListener(this);

    }

    public void paintComponent(Graphics g){

        super.paintComponent(g);

        g.drawString("press f to draw circles", 90, 40);

        if(draw > 0){

            for(int i = 0; i < draw; i++){

                g.drawOval((i*100)+100, (i*100)+100, 100, 100);
            }
        }
    }

    public void keyTyped(KeyEvent e){

        if(e.getKeyChar() == 'f' || e.getKeyChar() == 'F'){
            draw++;
            repaint();
        }
    }
}
2

There are 2 answers

0
MadProgrammer On BEST ANSWER

So before anything else, this...

public void paintComponent(Graphics g){
    if(test == true){
        sp = new SecondPanel();
        removeAll();
        revalidate();
        setContentPane(sp);
    }
}

This incredibly bad! First, you are breaking the paint chain (not calling super.paintComponent) and secondly, you are changing the state of the component from within the paint cycle, this will trigger a new repaint request and will call your paintComponent again and again and again and again ....

Painting is for painting the current state of the component, nothing more. NEVER change the state of any component from within ANY paint method EVER

Instead of trying to use remove/add, consider using a CardLayout instead, see How to Use CardLayout. This will allow you to switch between the first and second panels based on your needs, from a centralised control point.

KeyListener is a fickle mistress, it wants all the attention, all of the time. It will only raise key events if the component it is registered to is focusable AND has focus. A better solution is to use the key bindings API, which has been designed to overcome this limitation and provide you with a level of control over the level of focus required to trigger the associated actions.

See How to Use Key Bindings for more details

0
Hovercraft Full Of Eels On

To swap content of a container, be it a JFrame's contentPane or any JPanel, consider using a CardLayout since this tool was built specifically for this job.

Note that this code:

sp = new SecondPanel();
removeAll();
revalidate();
setContentPane(sp);

should never be found inside of a paintComponent method. This method is not under our direct control, and should be for painting and painting only. Also by not calling the super's method, you have broken the painting chain.

Also, instead of KeyListeners, use Key Bindings, and your functionality should work.

For instance, please have a look at the similar CardLayout code I created today for another similar question.