Search code examples
javadynamicjframejpaneljbutton

Dynamically initializing JButtons with unique ImageIcons


This is my first attempt at using a GUI layout in Java. I am trying to create a simple memory card game, where the user flips over two cards, and tries to get a match, and if there's no match they flip back over, if there's a match they stay flipped until you get all the matches. I might have made it hard on myself though in that I made the whole game dynamic to the constructor variables that define the number of columns and rows of cards. I thought this was better than hard-coding a certain value in, but now I'm having trouble putting the images in my img folder onto my cards.

I understand that variables of variables are not permitted in Java & this is really hard for me to adapt to as a ColdFusion developer. Can you help me think of ways to accomplish this this the proper way in Java?

Here is a simplified version of my code.

import javax.swing.JFrame;

public class MemoryApp
{
    public static void main(String args[])
    {
        //Creates a new game with 3 columns and 4 rows
        final CardGame myGame = new CardGame(3, 4);

        javax.swing.SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI(myGame.getCols(), myGame.getRows());
            }
        });
    }

    private static void createAndShowGUI(int c, int r) {
        //Create and set up the window.
        Window frame = new Window("GridLayoutDemo", c, r);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //Set up the content pane.
        frame.addComponentsToPane(frame.getContentPane());
        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }
}

Card game class:

public class CardGame
{
    private int cols, rows;

    public CardGame(int c, int r)
    {
        cols = c;
        rows = r;
    }

    public int getCols(){
        return cols;
    }

    public int getRows(){
        return rows;
    }
}

The window with the grid layout:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;

public class Window extends JFrame
{
    private int cols, rows;
    GridLayout windowGrid = new GridLayout(1,1);

    public Window(String t, int c, int r)
    {
        super(t);
        cols = c;
        rows = r;
        windowGrid.setColumns(c);
        windowGrid.setRows(r);
    }

    public void addComponentsToPane(final Container pane)
    {
        final JPanel compsToExperiment = new JPanel();
        compsToExperiment.setLayout(windowGrid);
        JPanel controls = new JPanel();
        controls.setLayout(new GridLayout(cols,rows));

        int countCard = (cols * rows) / 2;

        /**
         * Add buttons to experiment with Grid Layout.
         * This is where I'd like to be able to loop through
         * countCard and create the required number of buttons
         * as well as put images on the buttons like so:
         * 
         * ImageIcon image1 = new ImageIcon(getClass().getResource("card1.png"));
         *  through
         * ImageIcon image1 = new ImageIcon(getClass().getResource("card" & cardCount & ".png"));
         * 
         * Below is how I would attempt this in ColdFusion- I know
         * the variable of variables syntax is invalid, it is just
         * to show you what I mean.
         */

        // for(int i = 0; i < countCard; i++;)
        // {    
        //      compsToExperiment.add(new JButton("../img/card" & i & ".png"));
        //      ImageIcon variables["image" & i] = new ImageIcon(getClass().getResource("card" & i & ".png"));
        //      imageButton.setIcon(variables["image" & i]);

        //      etc. with ButtonClickEventHandler, haven't gotten that far yet
        // }

        pane.add(compsToExperiment, BorderLayout.NORTH);
        pane.add(new JSeparator(), BorderLayout.CENTER);
    }
}

Solution

  • Based on the code commented-out code that you posted so far, one approach could be like this:

    public class Window extends JFrame
    {
        ...
    
        // A java.util.List that stores all the buttons, so
        // that their icons may be changed later
        private List<JButton> buttons = new ArrayList<JButton>();
    
        // A java.util.List that stores all the ImageIcons that
        // may be placed on the buttons
        private List<ImageIcon> imageIcons = new ArrayList<ImageIcon>();
    
        public void addComponentsToPane(final Container pane)
        {
            ...
    
            for(int i = 0; i < countCard; i++;)
            {    
                // String concatenation is done with "+" in Java, not with "&"
                String fileName = "card" + i + ".png";
    
                // Create the icon and the button containing the icon  
                ImageIcon imageIcon = new ImageIcon(getClass().getResource(fileName));
                JButton button = new JButton(imageIcon);
    
                // Add the button to the main panel
                compsToExperiment.add(button);
    
                // Store the button and the icon in the lists
                // for later retrieval
                imageIcons.add(imageIcon);
                buttons.add(button);
    
                // Attach an ActionListener to the button that will
                // be informed when the button was clicked.
                button.addActionListener(createActionListener(i));
            }
        }
    
    
        private ActionListener createActionListener(final int cardIndex) 
        {
            return new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent e)
                {
                    clickedCardButton(cardIndex);
                }
            };
        }
    
        private void clickedCardButton(int cardIndex)
        {
            System.out.println("Pressed button for card "+cardIndex);
    
            // Here you can change the icons of the buttons or so...
            JButton button = buttons.get(cardIndex);
            ImageIcon imageIcon = imageIcons.get(cardIndex);
            ....
        }
    

    You mentioned that this is your first attempt to build a GUI with Java. So I assume that this is only intended for "practicing". If your intention was to build a "real application", you should rather consider some Model-View-Controller (MVC) approach for this.