JButtons won't update on button click

Currently displays a GUI with an 8x8 grid of randomized colored buttons. The newButton is to reset the score display to 0 and reset the grid with a fresh like it does on startup after being clicked. I haven't been able to find many solutions other than that it's to do with the way Java displays it's buttons and the way layering works. Here are the 2 classes I'm using.

import javax.swing.JFrame; 
import javax.swing.*;
import java.awt.event.*;
import javax.swing.JPanel;
import java.awt.Color;

public class ShinyButtonsApp extends JFrame implements ActionListener {

    private static byte ROWS = 8;
    public int useThis; 
    ShinyButtons shiny = new ShinyButtons();
    public static ImageIcon[] icons = {new ImageIcon("RedButton.png"),
                                       new ImageIcon("OrangeButton.png"), 
                                       new ImageIcon("YellowButton.png"), 
                                       new ImageIcon("GreenButton.png"), 
                                       new ImageIcon("BlueButton.png"), 
                                       new ImageIcon("LightGrayButton.png"), 
                                       new ImageIcon("DarkGrayButton.png")}; 


    public ShinyButtonsApp(String title) { 
        super(title); // Set title of window 
        setDefaultCloseOperation(EXIT_ON_CLOSE); // allow window to close 
        setSize(578, 634); // Set size of window 
        setResizable(false);

        getContentPane().setLayout(null);   

        JLabel aLabel = new JLabel("Score: "); 
        aLabel.setLocation(10, 570); 
        aLabel.setSize(80,30); 
        getContentPane().add(aLabel);

        JTextField scoreField = new JTextField(); 
        scoreField.setText(Integer.toString(shiny.score));
        scoreField.setEditable(false);
        scoreField.setHorizontalAlignment(JTextField.RIGHT);
        scoreField.setLocation(60, 570); 
        scoreField.setSize(120,30);
        scoreField.setBackground(Color.WHITE); 
        getContentPane().add(scoreField);

        JButton newButton = new JButton("New Game");
        newButton.addActionListener(this);
        newButton.setLocation(348,570);
        newButton.setSize(110,30);
        getContentPane().add(newButton);

        JButton quitButton = new JButton("Quit");
        quitButton.setLocation(468,570);
        quitButton.setSize(80,30);
        getContentPane().add(quitButton);

        resetButtons2();
    } 

    public void actionPerformed(ActionEvent e) {
                shiny.score = 0;
                shiny.resetButtons();
                resetButtons2();
    }

    public void resetButtons2() {
        for (int r=0; r<ROWS; r++) {
            for (int c=0; c<ROWS; c++) {
                ImageIcon image1 = icons[(int)shiny.getButton(r,c)];
                JButton button = new JButton(image1);
                button.setLocation(10+(69*r),10+(69*c));
                button.setSize(69,69);
                button.setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
                getContentPane().add(button);
            }
        }
    }

    public static void main(String[] args) { 
        ShinyButtonsApp frame;      
        frame = new ShinyButtonsApp("Shiny Buttons"); // Create window 
        frame.setVisible(true); // Show window      
    }   
}

and

import javax.swing.JFrame; 
import javax.swing.*;
import java.awt.event.*;
import javax.swing.JPanel;
import java.awt.Color;

public class ShinyButtons extends JPanel{ 
    public static byte RED = 0; 
    public static byte ORANGE = 1; 
    public static byte YELLOW = 2; 
    public static byte GREEN = 3; 
    public static byte BLUE = 4; 
    public static byte LIGHT_GRAY = 5; 
    public static byte DARK_GRAY = 6; 

    public static byte ROWS = 8; 

    public byte[][] buttonTable;

    public int score = 0; 

    public ShinyButtons() { 
        buttonTable = new byte[ROWS][ROWS]; 
        resetButtons(); 
    } 

    public void resetButtons() { 
        for (int r=0; r<ROWS; r++) 
            for (int c=0; c<ROWS; c++) 
                buttonTable[r][c] = (byte)(Math.random()*7); 
    } 

    public byte getButton(int r, int c) { return buttonTable[r][c]; } 

    public int getScore() { return score; }
} 

Answers


Building on to what this answer points out (using layout managers instead of setting size, as you should be doing) you could reset the the images just by looping through the components (JLabels) of the JPanel and changing their icons.

This particular example uses JLabels with MouseListeners but it could easily be switched to JButtons with ActionListeners

newGame.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
         reset(iconPanel, icons);               <--- call reset method from below
         score = 0;
         scoreField.setText(String.valueOf(score));
    }
});
....

private void reset(JPanel panel, ImageIcon[] icons) {
    Component[] comps = panel.getComponents();
    Random random = new Random();
    for(Component c : comps) {
        if (c instanceof JLabel) {
            JLabel button = (JLabel)c;
            int index = random.nextInt(icons.length);
            button.setIcon(icons[index]);
        }
    }
}

Here's the complete running code. You just need to replace the image paths.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

public class CircleImages {

    private int score = 0;
    private JTextField scoreField = new JTextField(10);

    public CircleImages() {
        scoreField.setEditable(false);

        final ImageIcon[] icons = createImageIcons();
        final JPanel iconPanel = createPanel(icons, 8);

        JPanel bottomLeftPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
        bottomLeftPanel.add(new JLabel("Score: "));
        bottomLeftPanel.add(scoreField);

        JPanel bottomRightPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
        JButton newGame = new JButton("New Game");
        bottomRightPanel.add(newGame);
        JButton quit = new JButton("Quit");
        bottomRightPanel.add(quit);

        JPanel bottomPanel = new JPanel(new GridLayout(1, 2));
        bottomPanel.add(bottomLeftPanel);
        bottomPanel.add(bottomRightPanel);

        newGame.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                reset(iconPanel, icons);
                score = 0;
                scoreField.setText(String.valueOf(score));
            }
        });

        JFrame frame = new JFrame();
        frame.add(iconPanel);
        frame.add(bottomPanel, BorderLayout.PAGE_END);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void reset(JPanel panel, ImageIcon[] icons) {
        Component[] comps = panel.getComponents();
        Random random = new Random();
        for(Component c : comps) {
            if (c instanceof JLabel) {
                JLabel button = (JLabel)c;
                int index = random.nextInt(icons.length);
                button.setIcon(icons[index]);
            }
        }
    }

    private JPanel createPanel(ImageIcon[] icons, int gridSize) {
        Random random = new Random();
        JPanel panel = new JPanel(new GridLayout(gridSize, gridSize));
        for (int i = 0; i < gridSize * gridSize; i++) {
            int index = random.nextInt(icons.length);
            JLabel label = new JLabel(icons[index]);
            label.addMouseListener(new MouseAdapter(){
                public void mouseClicked(MouseEvent e) {
                    score += 1;
                    scoreField.setText(String.valueOf(score));
                }
            });
            label.setBorder(new LineBorder(Color.GRAY, 2));
            panel.add(label);
        }
        return panel;
    }

    private ImageIcon[] createImageIcons() {
        String[] files = {"blackcircle.png",
            "bluecircle.png",
            "greencircle.png",
            "greycircle.png",
            "orangecircle.png",
            "redcircle.png",
            "yellowcircle.png"
        };
        ImageIcon[] icons = new ImageIcon[files.length];
        for (int i = 0; i < files.length; i++) {
            icons[i] = new ImageIcon(getClass().getResource("/circles/" + files[i]));
        }
        return icons;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new CircleImages();
            }
        });
    }
}

You are creating a new set of buttons in resetButtons2() and placing them on top (or rather under) of the already existing set of buttons in the same locations. You should create the set of buttons once and only update their icons upon reset.

You should do a number of things:

  • Use a proper layout manager, e.g., GridLayout
  • Create the 8x8 grid of buttons only once and replace their icons when needed
  • Call invalidate/repaint to refresh the content pane

And for a JFrame you don't need getContentPane().add(...), you can directly do add(...)


Two things.

First, make sure you remove the existing buttons first. Alternatively, you could simply update the state of the buttons. This would require you to create an array or List of buttons first, which your reset method would then iterate over and update their properties as required.

Second, make use of an appropriate layout manager. A null layout ias a very bad idea. You do not control the font metrics of the underlying platform and this will change how your buttons look on different systems, making your UI less dynamic then it should be


Need Your Help

Extending border to the end of the page

html css border webpage

Just wondering if there is a way to extend a border underneath a heading to reach the end of the page.

Active admin not finding partial in show

ruby-on-rails ruby-on-rails-4 activeadmin

This should be pretty simple, trying to render a partial on active admin in show action:

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.