2

I'm creating a "Snake" game as homework and I've passed hours searching around all the possible methods to close a JFrame, without conclusion. I started the program creating a JFrame with a background image and a menu, nothing more. When I click on the "Start game" button (JMenuItem) it opens a new JFrame with a thread to play the game. My problem is that the first JFrame doesn't close anyway I try to. I tried using these solutions and these and these but that JFrame's still there. It looks like the only command he listens to is the "EXIT_ON_CLOSE" as DefaultCloseOperation, not even the "DISPOSE_ON_CLOSE" is working. This is my SnakeFrame class:

package snake;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;

/**
 *
 * @author Giacomo
 */
public class SnakeFrame extends Snake{

    protected JMenuItem start = new JMenuItem("Start game");

    public void pullThePlug(final SnakeFrame frame) {

  /*// this will make sure WindowListener.windowClosing() et al. will be called.
        WindowEvent wev = new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
        Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev);
       // this will hide and dispose the frame, so that the application quits by
        // itself if there is nothing else around. 
        frame.setVisible(false);
        frame.dispose();*/


        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentHidden(ComponentEvent e) {

                ((JFrame)(e.getComponent())).dispose();
            }
        });
    }

    public int game(final SnakeFrame frame,final JFrame gameFrame){

        short game_ok=0;

        try{
            start.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    GameThread gamethread = new GameThread(frame,gameFrame);
                    gamethread.start();

                    pullThePlug(frame);

                }
            });
        }
        catch(Exception e){
            game_ok=1;
        }


        return game_ok;
    }

//-----------Frame initialization with relative Listeners and Events.---------\\
    public SnakeFrame(){

            JFrame frame= new JFrame("Le Snake");
            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
            double larghezza = screenSize.getWidth();
            double altezza = screenSize.getHeight();
            frame.setBounds(((int)larghezza/4),((int)altezza/4),800, 600);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            ImageIcon icon = new ImageIcon("C:\\Users\\Giacomo\\Documents"
            + "\\NetBeansProjects\\Snake\\src\\res\\Snake-icon.png");
            frame.setIconImage(icon.getImage());
            frame.setResizable(false);
            frame.setLayout(new GridLayout());

            JMenuBar menuBar = new JMenuBar();
            JMenu menu = new JMenu("Menu");
            menuBar.add(menu);
            menuBar.setBounds(1, 1, frame.getWidth(),frame.getHeight()/25);
            JMenuItem close = new JMenuItem("Exit");

            close.addActionListener(new ActionListener(){

                @Override
                public void actionPerformed(ActionEvent e) {

                    System.exit(0);
                }
            });

            menu.add(start);
            menu.add(close);
            frame.setJMenuBar(menuBar);

            try {
            frame.setContentPane(new JLabel(new ImageIcon(ImageIO.read
                (new File("C:\\Users\\Giacomo\\Documents\\NetBeansProjects\\"
                        + "Snake\\src\\res\\default.png")))));
            } catch (IOException e) {
                System.out.println("Exception with the background image.");
            }

            frame.pack();

        }
//-------------------------Frame initialization ended.-----------------------\\

}

Here's the game thread's code instead (for now):

package snake;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;

/**
 *
 * @author Giacomo
 */
public class GameThread extends Thread implements Runnable{

    private final SnakeFrame frame;
    private final JFrame gameFrame;

    public GameThread(final SnakeFrame f,final JFrame gF){
        this.frame = f;
        this.gameFrame = gF;
    }

    @Override
    public void run(){

        System.out.println("Game Thread started.");

        final Label[][] griglia = new Label[50][50];
        final Container container = new Container();

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        double larghezza = screenSize.getWidth();
        double altezza = screenSize.getHeight();

        gameFrame.setVisible(true);
        gameFrame.setBounds(((int)larghezza/4),((int)altezza/4),
                800, 600);
        gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ImageIcon icon = new ImageIcon("C:\\Users\\Giacomo\\Documents"
            + "\\NetBeansProjects\\Snake\\src\\res\\Snake-icon.png");
            gameFrame.setIconImage(icon.getImage());
        gameFrame.setResizable(false);

        gameFrame.addWindowListener(new WindowAdapter(){

        @Override
        public void windowClosing(WindowEvent e){

                //Just to be sure he dies.       
                gameFrame.dispose();
                frame.dispose();
                System.exit(0); 

            }
        });

        gameFrame.setLayout(new GridLayout(50,50));

        for(int i=0;i<50;i++){
            for(int j=0;j<50;j++){
                griglia[i][j] = new Label();
                griglia[i][j].setBackground(Color.BLACK);
                container.add(griglia[i][j]);
            }
        } 
        gameFrame.add(container);
    }

}

Did I forget something? Does anyone have an idea? Sorry if my code's bad but I'm at school to learn :P I'm using NetBeans 8.0.2. Thanks everyone. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ EDIT:

I have now solved most of the problems I had, and here is the main:

public static void main(String[] args) {

    final SnakeFrame frame = new SnakeFrame();
    short isGameOk;

    isGameOk =(short)frame.game(frame);

    if(isGameOk==1)
        System.err.println("Game Error!");

}

while here's the SnakeFrame class ("fixed"):

public class SnakeFrame extends Snake{

    private final JMenuItem start = new JMenuItem("Start game");
    private final BackgroundPanel defaultpanel;

    public int game(final SnakeFrame frame){

        short game_ok=0;

        try{
            start.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {

                    defaultpanel.setVisible(false);
                    JPanel panel = new JPanel();
                    JLabel[][] griglia = new JLabel[50][50];
                    panel.setLayout(new GridLayout(50,50));

                for(int i=0;i<50;i++){
                    for(int j=0;j<50;j++){
                        griglia[i][j] = new JLabel();
                        griglia[i][j].setBackground(Color.BLACK);
                        panel.add(griglia[i][j]);
                    }
                } 
                frame.add(panel);
                griglia[45][30].setIcon(new ImageIcon
                        ("src\\res\\sneikhead.png"));
                panel.setVisible(true);
                }
            });
        }
        catch(Exception e){
            game_ok=1;
        }

        return game_ok;
    }

//-----------Frame initialization with relative Listeners and Events.---------\\
    public SnakeFrame(){

            JFrame frame= new JFrame("Le Snake");
            ImageIcon imm = new ImageIcon(getClass().getResource
        ("/res/default.png"));
            Image background = imm.getImage();
            defaultpanel = new BackgroundPanel(background, BackgroundPanel.ACTUAL,
                    1.0f, 0.5f);
            frame.add(defaultpanel);
            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
            double larghezza = screenSize.getWidth();
            double altezza = screenSize.getHeight();
            frame.setBounds(((int)larghezza/4),((int)altezza/4),800, 600);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            ImageIcon imageIcon = new ImageIcon(getClass().getResource("/res/Snake-icon.png"));
            Image icon = imageIcon.getImage();
            frame.setIconImage(icon);
            frame.setResizable(false);
            frame.setLayout(new GridLayout());
            JMenuBar menuBar = new JMenuBar();
            JMenu menu = new JMenu("Menu");
            menuBar.add(menu);
            menuBar.setBounds(1, 1, frame.getWidth(),frame.getHeight()/25);
            JMenuItem close = new JMenuItem("Exit");

            close.addActionListener(new ActionListener(){

                @Override
                public void actionPerformed(ActionEvent e) {

                    System.exit(0);
                }
            });

            menu.add(start);
            menu.add(close);
            frame.setJMenuBar(menuBar);

            frame.pack();

        }
//-------------------------Frame initialization ended.-----------------------\\

}

I am now able to change the SnakeFrame's background with a defaultpanel.setVisible(false); but the panel full of black colored labels that should show isn't visible even if I set it true. How can I get it visible? Thank you.

Community
  • 1
  • 1
ServantGrunt
  • 35
  • 11

1 Answers1

3

Some general comments:

  1. Why are you creating a second frame? Usually a game is played in its own frame? You have a menu that allows you to change options of the game and this is done by display a modal JDialog. Then you have a "Start" button that starts the game by adding a panel to the existing frame.

  2. Don't use a ComponentListener on the frame. There is no need to handle componentHidden.

  3. Why do you have the game() method? I don't even see in your code where you invoke the method. Just add the actionListener to the Start menu item in the constructor of your class. Keep the creation of the menu item and the assignment of the actionListener in the same block of code so we don't need to look all over the class to find all the related properties for the component.

When I click on the "Start game" button (JMenuItem) it opens a new JFrame with a thread to play the game.

This is wrong. You should NOT be starting a separate Thread. All Swing components should be created on the Event Dispatch Thread (EDT). So basically the code should just be invoked from within your ActionListener since all event code does execute on the EDT.

It looks like the only command he listens to is the "EXIT_ON_CLOSE" as DefaultCloseOperation, not even the "DISPOSE_ON_CLOSE" is working.

Those are properties that you set for the frame. Then when the user clicks on the "X" button at the top/right of the frame the frame will close depending on the property. These properties have no effect when you invoke frame.dispose() or System.exit() directly.

GameThread gamethread = new GameThread(frame,gameFrame);

Why are you passing the frame to your other class? If you goal is to close the original frame. Then close the frame here after creating the other frame. In fact why do you even have a reference to the "gameFrame" here. If you have another class that creates the components of the frame, it should create the frame as well.

pullThePlug(frame);

This code does nothing. You are invoking this method AFTER you attempt to close the frame.

In general, I can't follow your logic since the code is not complete. However as a general rule an application should only contain a single JFrame. Hiding/showing multiple frames is not an experience uses want to see. When a new game starts you should just refresh the content pane of the existing frame.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • I decided to create a new JFrame because I couldn't in any way change that JFrame's background. Everything I tried had no effect, so I just thought: "Since I can't change the background, I'll create a new one with no background". Anyway, thank you for your help, I'll try fixing everything you highlighted above. – ServantGrunt Jan 02 '15 at 17:03
  • @ServantGrunt, `I couldn't in any way change that JFrame's background` - sure you can change the background. Check out [Background Panel](http://tips4java.wordpress.com/2008/10/12/background-panel/) for a couple of approaches. – camickr Jan 02 '15 at 17:08
  • I just realized that maybe I couldn't change it because I set that background not as a panel's background but as the frame's background? – ServantGrunt Jan 02 '15 at 17:12
  • The "content pane" of a JFrame is a JPanel, so if you want to change the background color you need to change the color of the "content pane", not the frame. Same for an image. You add the image to the content pane. – camickr Jan 02 '15 at 17:21
  • Thanks, that wasn't totally clear in my mind before. My problem now is that it says it can't read the file. I did this: BackgroundPanel defaultpanel; Image background = null; try { background = ImageIO.read(new File("/src/res/default.png")); } catch (IOException ex) {Logger.getLogger(SnakeFrame.class.getName()).log(Level.SEVERE, null, ex); } defaultpanel = new BackgroundPanel(background, BackgroundPanel.ACTUAL, 1.0f, 0.5f); Do you know why it can't read it? Even using "\\" instead of "/" isn't working. – ServantGrunt Jan 02 '15 at 17:33
  • You should use paths relative to your drive because the code may run on different computers. Check out the section from the Swing tutorial on [How to Use Icons](http://docs.oracle.com/javase/tutorial/uiswing/components/icon.html) for more examples of reading files. – camickr Jan 02 '15 at 18:16