Search code examples
javaswingeventsjlabelmouse-listeners

Change JLabel Background on MouseRelease Event


I am trying to write a Java program that when you click on a certain element in the JFrame, the background of a JLabel is changed. The function that the event handling method calls to change the background does not work when called from the event, however it does work whenever you call the method somewhere independent of any event. Does anyone know how you would go about changing the background on MouseRelease event, since using an event handling method like so doesn't work?

private void MouseReleaseEvent(java.awt.event.MouseEvent evt) {                                             
    // Do some random stuff, like a println.
    // This println works as a normal println statement should.
    System.out.println("Hello World!");
    // Now let's try the method that changes the background.
    // This method will not work within this event handler, but trying it outside
    // of it does work.
    changeBackground();
}   
  • Note: The above code is solely for showing my problem. It is not actually part of my code. I am not releasing the code because I am working on the program with a friend, and he does not want to release the code, even if it is just a small part of it with which nothing valuable can be done.

Edit: As per the request of all of those trying to help me, I have uploaded the NetBeans project workspace to FileDropper in a zip file, which you can download here. You will notice that when I call the method from the main class, it does flash another image and return to the current one as I want, but when you release the mouse from clicking on "Play", it does not call it, even though it should. Feel free to experiment with the code as you wish.

Edit 2: Since it has also been requested that I post the code for those that do not want to download the workspace, here it is:

Simon.java

package simon;

public class Simon {
    public static void main(String[] args) {
        // Load all of the other class files into this main class.
        SimonGUI SimonGUI = new SimonGUI();
        // For some reason, setting the background color doesn't work when we
        // try doing it within the GUI development environment, but it works
        // here, so that's what we are going to do.
        SimonGUI.getContentPane().setBackground(new java.awt.Color(0, 0, 0));
        // Make the form visible to the user. It is now ready for use.
        SimonGUI.setVisible(true);
        SimonGUI.playBackPattern();
    }
}

SimonGUI.java*

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simon;

import java.awt.Component;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 *
 * @author Administrator
 */
public class SimonGUI extends javax.swing.JFrame {
    // Declare class level variables.
    boolean aboutTextVisible = false;
    boolean mainMenuVisible = true;
    boolean userResponseAllowed = false;
    private Component frame;

    /**
     * Creates new form SimonGUI
     */
    public SimonGUI() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        lblPlayButton1 = new javax.swing.JLabel();
        lblPlayButton = new javax.swing.JLabel();
        lblSimonBoard = new javax.swing.JLabel();

        lblPlayButton1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/MenuPlay.png"))); // NOI18N
        lblPlayButton1.setName("lblPlayButton"); // NOI18N

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Simon");
        setBackground(new java.awt.Color(0, 0, 0));
        setMaximumSize(new java.awt.Dimension(600, 600));
        setMinimumSize(new java.awt.Dimension(600, 600));
        setName("frmSimon"); // NOI18N
        setPreferredSize(new java.awt.Dimension(600, 600));
        setResizable(false);
        getContentPane().setLayout(null);

        lblPlayButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/MenuPlay.png"))); // NOI18N
        lblPlayButton.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
        lblPlayButton.setName("lblPlayButton"); // NOI18N
        lblPlayButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                lblPlayButtonMouseReleased(evt);
            }
        });
        getContentPane().add(lblPlayButton);
        lblPlayButton.setBounds(150, 20, 317, 58);
        lblPlayButton.getAccessibleContext().setAccessibleName("lblPlayerButton");

        lblSimonBoard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/Original.PNG"))); // NOI18N
        lblSimonBoard.setToolTipText("");
        lblSimonBoard.setFocusable(false);
        lblSimonBoard.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        lblSimonBoard.setName("lblSimon"); // NOI18N
        getContentPane().add(lblSimonBoard);
        lblSimonBoard.setBounds(-1, 93, 600, 497);
        lblSimonBoard.getAccessibleContext().setAccessibleName("lblSimon");

        getAccessibleContext().setAccessibleName("frmSimon");
        getAccessibleContext().setAccessibleDescription("");

        pack();
    }// </editor-fold>                        

    private void lblPlayButtonMouseReleased(java.awt.event.MouseEvent evt) {                                            
        // This handles the start of Simon gameplay.
        toggleBoard();
        playBackPattern();
    }                                           

    public void toggleBoard() {
        // Handles toggling between the main menu and the simon board.
        if (mainMenuVisible == true)
        {
            // Board is visible, let's toggle it off.
            lblPlayButton.setBounds(700, 700, 317, 58);
            mainMenuVisible = false;
            lblSimonBoard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/Original.PNG")));
        }
        else
        {
            // Board is not visible, let's toggle it on.
            lblPlayButton.setBounds(120, 140, 317, 58);
            mainMenuVisible = true;
            lblSimonBoard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/SimonBackground.PNG")));
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /*
         * Set the Nimbus look and feel
         */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /*
         * If Nimbus (introduced in Java SE 6) is not available, stay with the
         * default look and feel. For details see
         * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(SimonGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(SimonGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(SimonGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(SimonGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /*
         * Create and display the form
         */
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new SimonGUI().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify                     
    private javax.swing.JLabel lblPlayButton;
    private javax.swing.JLabel lblPlayButton1;
    public javax.swing.JLabel lblSimonBoard;
    // End of variables declaration                   

    public void flashColor(int colorID) {
        // Flash one of the colors. The value of the color that we are going to
        // flash is passed here, and the color is flashed.
        // Pause for a fraction of a second before blinking the color. In case
        // something goes wrong during the pause, we have an error which we hope
        // to never use that will catch the program's weakness.
        try {
            Thread.sleep(250);
        } catch (InterruptedException ex) {
            JOptionPane.showMessageDialog(frame,"A fatal error has caused Simon to stop working.","Simon • Fatal Error",JOptionPane.ERROR_MESSAGE);
            System.exit(0);
        }
        // Flash the respective color.
        if(colorID == 1) {
            // Flash Red
            lblSimonBoard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/Red.PNG")));
        }
        else if(colorID == 2){
            // Flash Green
            lblSimonBoard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/Green.PNG")));
        }
        else if (colorID == 3) {
            // Flash Yellow
            lblSimonBoard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/Yellow.PNG")));
        }
        else {
            // Flash Blue
            lblSimonBoard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/Blue.PNG")));
        }
        // Wait for a fraction of a second before we return to the regular
        // board. In case something bad happens here while we pause, we have an
        // error message that we hope will never be used.
        try {
            Thread.sleep(250);
            lblSimonBoard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/simon/resources/Original.PNG")));
        } catch (InterruptedException ex) {
            JOptionPane.showMessageDialog(frame,"A fatal error has caused Simon to stop working.","Simon • Fatal Error",JOptionPane.ERROR_MESSAGE);
            System.exit(0);
        }
    }
    public void userResponseEnabled(boolean responsePermitted) {
        // Toggles the class-level variable "userResponseAllowed", which
        // controls if the user can click on the buttons on the Simon board and
        // give a response.

        // Pass the value to the class-level variable.
        userResponseAllowed = responsePermitted;
    }

    // "Actual Game Code"
    int score = 0;
    ArrayList pattern = new ArrayList();
    int playerColor = 0;
    int y = 0;
    boolean correct = true;
    private SimonGUI SimonGUI;

    int certainColor = 1;
    int numberOfColors = 0;

    public void playBackPattern() {
        // Normally, the playBackPattern() method has more than just this line,
        // but for demo purposes, this line is all that is needed.
        flashColor(4);
    }
}

*Some of this code is automatically generated using the Swing GUI designer add-on for NetBeans.


Solution

  • Based on the example code, there are, at least, two things that I can that are worrisome

    1. The use of null layouts. Every system is unique, with it's own font, screen resolution and DPI properties. To overcome this issue, and to make it easier to write sophisticated user interfaces that could work across not only different computers, but different operating systems, the layout management API was developed. It strongly recommend that you take a look at Laying Out Components Within a Container
    2. The use of Thread.sleep within the Event Dispatching Thread.

    Swing is a single threaded framework. That is, it is required that all interactions and modifications to the UI take place within the context of the Event Dispatching Thread. Any actions which blocks this thread will prevent it from processing, amongst other things, paint requests.

    This mean that when you execute Thread.sleep, you are prevent the EDT from updating the UI until AFTER the method you are in exits...

    The simplest solution I can think of for you is to use a javax.swing.Timer. The following is a simple example which demonstrates this idea.

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Image;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class SimpleSimon {
    
        public static void main(String[] args) {
            new SimpleSimon();
        }
    
        public SimpleSimon() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private JLabel board;
            private Timer timer;
            private int flashCount;
    
            private int flashColor;
            private Image[] colors;
    
            private Image original;
    
            public TestPane() {
    
                colors = new Image[4];
                try {
                    colors[0] = ImageIO.read(getClass().getResource("/resources/Blue.PNG"));
                    colors[1] = ImageIO.read(getClass().getResource("/resources/Green.PNG"));
                    colors[2] = ImageIO.read(getClass().getResource("/resources/Red.PNG"));
                    colors[3] = ImageIO.read(getClass().getResource("/resources/Yellow.PNG"));
                    original = ImageIO.read(getClass().getResource("/resources/Original.PNG"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
    
                setLayout(new BorderLayout());
                JButton btn = new JButton("Play");
                board = new JLabel();
                board.setHorizontalAlignment(JLabel.CENTER);
                board.setVerticalAlignment(JLabel.CENTER);
                add(board);
    
                timer = new Timer(250, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (flashCount == 0) {
                            board.setIcon(new ImageIcon(colors[flashColor]));
                        } else {
                            board.setIcon(new ImageIcon(original));
                            timer.stop();
                        }
                        flashCount++;
                    }
                });
                timer.setRepeats(true);
    
                add(btn, BorderLayout.SOUTH);
                btn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        flashCount = 0;
                        flashColor = (int)Math.round(Math.random() * 3);
                        timer.restart();
                    }
                });
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(650, 400);
            }
        }
    }
    

    Basically, I would change your playBackPattern method to start a javax.swing.Timer which could, if you wanted it to, call your flashColor method, for example...

    See Concurrency in Swing for more details.

    Updated with a "call back" feature

    This provides a very basic example of providing a call back feature, so that when the animation stops, it will make a call back to the specified object.

    Personally, I would have preferred to do it slightly different, but it was over complicating the example...

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.Image;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class SimpleSimon {
    
        public static void main(String[] args) {
            new SimpleSimon();
        }
    
        public SimpleSimon() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private JLabel board;
            private Timer timer;
            private int flashCount;
            private int flashTimes;
    
            private int flashColor;
            private Image[] colors;
    
            private Image original;
    
            private AnimationCallBack callBack;
    
            public TestPane() {
    
                colors = new Image[4];
                try {
                    colors[0] = createImage(Color.BLUE);
                    colors[1] = createImage(Color.GREEN);
                    colors[2] = createImage(Color.RED);
                    colors[3] = createImage(Color.YELLOW);
                    original = createImage(Color.WHITE);
                } catch (Exception e) {
                    e.printStackTrace();
                }
    
                setLayout(new BorderLayout());
                board = new JLabel();
                board.setHorizontalAlignment(JLabel.CENTER);
                board.setVerticalAlignment(JLabel.CENTER);
                add(board);
    
                timer = new Timer(250, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (flashCount < flashTimes) {
                            if (flashCount % 2 == 0) {
                                board.setIcon(new ImageIcon(colors[flashColor]));
                            } else {
                                board.setIcon(new ImageIcon(original));
                            }
                        } else {
                            board.setIcon(new ImageIcon(original));
                            timer.stop();
                            // Animation has stopped, make call back...
                            if (callBack != null) {
                                callBack.animationDone();
                            }
                        }
                        flashCount++;
                    }
                });
                timer.setRepeats(true);
    
                JPanel buttons = new JPanel(new GridLayout(0, 2));
    
                final JButton btnOne = new JButton("#1");
                buttons.add(btnOne);
                btnOne.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        flashCount = 0;
                        flashTimes = 1;
                        flashColor = (int) Math.round(Math.random() * 3);
                        timer.restart();
    
                        btnOne.setEnabled(false);
                        // Set the call back so we know when the
                        // animation has stopped...
                        callBack = new AnimationCallBack() {
                            @Override
                            public void animationDone() {
                                btnOne.setEnabled(true);
                            }
                        };
                    }
                });
    
                final JButton btnTwo = new JButton("#2");
                buttons.add(btnTwo);
                btnTwo.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        flashCount = 0;
                        flashTimes = 3;
                        flashColor = (int) Math.round(Math.random() * 3);
                        timer.restart();
                        btnTwo.setEnabled(false);
                        // Set the call back so we know when the
                        // animation has stopped...
                        callBack = new AnimationCallBack() {
                            @Override
                            public void animationDone() {
                                btnTwo.setEnabled(true);
                            }
                        };
                    }
                });
                add(buttons, BorderLayout.SOUTH);
            }
    
            protected Image createImage(Color color) {
    
                BufferedImage img = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = img.createGraphics();
                g2d.setColor(color);
                g2d.fillRect(0, 0, 400, 400);
    
                return img;
    
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 400);
            }
        }
    
        public interface AnimationCallBack {
            public void animationDone();
        }
    }