pmurph
pmurph

Reputation: 51

JPanel within a JFrame drawing over top of the JFrame's Menu

So I have a JPanel object as a component of a JFrame, and I am periodically redrawing the contents of the JPanel with a Timer object. Everything is working fine except for the JPanel being redrawn over top of the JFrame's menu when therefore making the menu items unreadable. Is there a way around this problem without having to pause the timer every time the user goes to access the menu?

Control Frame Class

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

public class ControlFrame extends JFrame implements ActionListener{
    /*======Public Constants======*/
    public static int DEFAULT_HEIGHT = 400;
    public static int DEFAULT_WIDTH = 400;

    /*======Private Instance Variables======*/
    private AnimationPanel animPane;
    private JMenu menu;
    private JMenuItem menuExit;
    private JMenuBar menuBar;

    /*======Constructors======*/
    public ControlFrame(){
        initialize();
    }

    /*======Public Instance Methods======*/
    public void actionPerformed(ActionEvent ae) {
        if(ae.getActionCommand().equals("exit")){
                System.exit(0);
        }
    }

    /*======Private Instance Methods======*/
    private void initialize(){
        this.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        this.setLayout(new GridLayout(0,2));

        this.animPane = new AnimationPanel(this.getWidth(), this.getHeight());

        this.add(animPane);

        createCFMenu();

        this.setVisible(true);
    }

    private void createCFMenu(){
        this.menuBar = new JMenuBar();
        this.menu = new JMenu("File");
        this.menu.setMnemonic(KeyEvent.VK_F);
        this.menuBar.add(this.menu);

        this.menuExit = new JMenuItem("Exit", KeyEvent.VK_X);
        this.menuExit.addActionListener(this);
        this.menuExit.setActionCommand("exit");
        this.menu.add(menuExit);

        this.setJMenuBar(this.menuBar);
    }

    /*======Main Method======*/
    public static void main(String[] args){
        ControlFrame cf = new ControlFrame();


    }

}

AnimationPanel Class

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

public class AnimationPanel extends JPanel implements ActionListener{



    /*======Private Instance Variables======*/
    private int timeInterval;
    private Timer animTimer;

    /*======Constructor======*/
    public AnimationPanel(int width, int height){
        timeInterval = 50;

        this.setSize(width, height);

        this.animTimer = new Timer(timeInterval, this);

        animTimer.start();
    }


    public void actionPerformed(ActionEvent arg0) {

        paint();
    }

    /*======Private Instance Variables======*/
    private void paint(){
        BufferedImage bImage = new BufferedImage(this.getWidth(), 
            this.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics bg = bImage.getGraphics();

        bg.setColor(Color.WHITE);
        bg.fillRect(0, 0, bImage.getWidth(), bImage.getHeight());

        this.getGraphics().drawImage(bImage, 0, 0, this);
    }
} 

The problem is the Animation Panel is drawing over top of the ControlFrames Menu

Upvotes: 3

Views: 2534

Answers (2)

Andrew Thompson
Andrew Thompson

Reputation: 168825

Don't call getGraphics() in Java code. A Java GUI must repaint when it is told to do so, and should do so using either paint(Graphics) or paintComponent(Graphics). That is why the menu was vanishing.

The bug is solved in this version of AnimationPanel.

class AnimationPanel extends JPanel implements ActionListener{
    /*======Private Instance Variables======*/
    private int timeInterval;
    private Timer animTimer;

    /*======Constructor======*/
    public AnimationPanel(int width, int height){
        timeInterval = 50;
        this.setSize(width, height);
        this.animTimer = new Timer(timeInterval, this);
        animTimer.start();
    }

    public void actionPerformed(ActionEvent arg0) {
        repaint();
    }

    /*======Private Instance Variables======*/
    public void paintComponent(Graphics g){
        // important to get the component to paint itself & borders etc.
        super.paintComponent(g); 
        BufferedImage bImage = new BufferedImage(this.getWidth(),
            this.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics bg = bImage.getGraphics();

        bg.setColor(Color.WHITE);
        bg.fillRect(0, 0, bImage.getWidth(), bImage.getHeight());
        bg.dispose();  // Assist the garbage collector!

        g.drawImage(bImage, 0, 0, this);
    }
}

Upvotes: 4

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

One problem with your code is you're drawing all wrong. You almost never use getGraphics of a component to get its Graphics object since this object won't persist if there's any repaints. Instead drawing should be passively performed in the paintComponent method of your JPanel.

Edit: as Andrew shows in his faster post! 1+ to him!

But if you get anything out of this exercise, it's that you will need to go through the Java Swing graphics tutorials to learn how to draw in Swing as you will need to throw out some faulty assumptions that you (and all of us) have when starting to do this kind of coding.

Upvotes: 4

Related Questions