0

I am working with a list of elements that I want to rank. Ideally the program would randomly choose 2 elements to compare and present them, sort the list with the new ranking, then select 2 more so the user could pick the winner over and over again until the user wanted to stop. The parts I am having trouble with are:

  1. getting the button to interact with the object to change its rank and
  2. getting the JFrame to close and reopen with new elements to compare

I created a class called action that is supposed to handle the clicks:

public class action extends JFrame implements ActionListener{
  private JButton b1;
  private JButton b2;
  private JButton b3;
  private JPanel jp;


  public action(Bond one, Bond two){
    //JFrame mf = new JFrame("My Frame");
    //JPanel jp = new JPanel();
    jp = new JPanel();
    setSize(500,500);
    setVisible(true);
    setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
    b1 = new JButton(one.name+"\nRating: "+one.rating);
    b1.addActionListener(this);
    b2 = new JButton(two.name);
    b2.addActionListener(this);
    b3 = new JButton("Tie");
    b3.addActionListener(this);
    jp.add(b1);
    jp.add(b2);
    jp.add(b3);
    add(jp);
  }
  public void actionPerformed(ActionEvent e){
    int clickCount = 0;
    if (clickCount > 3) {
      System.exit(0);
    }
    JOptionPane jo = new JOptionPane();
    if(e.getSource() == b1){
      //  jo.showMessageDialog(null, "button 1");
      clickCount++;
      //System.exit(0);
    }
    if(e.getSource() == b2){
      jo.showMessageDialog(null, "button 2");
      clickCount++;
      //System.exit(0);
    }
    if(e.getSource() == b3){
      jo.showMessageDialog(null, "button 3");
      clickCount++;
      //System.exit(0);
    }
  }
}

And here is how I am calling the action class from my main:

    while(compCount < 3){
      //these generate random numbers that correspond with the index of the elements to be compared
      //comp is the name of the list of objects
      int r1 = r.nextInt(comp.size());
      int r2 = r.nextInt(comp.size());
      int t;
      if(r1 == r2){
        continue;
      }
      else{
        action a = new action(comp.get(r1), comp.get(r2));
        compCount++;
      }
    }

Currently this just creates 3 popup windows that do nothing when I click the buttons. Any help or insight would be appreciated!

  • 4
    For better help sooner post a proper [mre] that demonstrates your issue. We don't want the whole code, if you're having trouble with the `ActionListener` then just create a simple program with 2 buttons, and make them do something different each when you click them. And you [shouldn't be using multiple JFrames](https://stackoverflow.com/questions/9554636/the-use-of-multiple-jframes-good-or-bad-practice). That's a bad user experience, instead use `CardLayout` or `JDialog`s. – Frakcool Jul 10 '20 at 17:19

1 Answers1

1

The OP wants to make a complex Swing GUI. He didn't provide a minimal, runnable, example, so I created my own.

The best way to make a complex Swing GUI is to construct it one small piece at a time. By testing each small piece in isolation, you too can construct a complex GUI.

Here's the GUI I created.

Favorite Character GUI

The object of this game is to choose your favorite characters from the lower case characters of the English alphabet. You can left-click on the Pass button if neither character is among your favorites.

I'd already written the JPanel to display a character for a previous Stack Overflow answer.

The first thing I did was create the main class. I named this class FavoriteCharacter.

I called the SwingUtilities invokeLater method to ensure that the Swing components were created and executed on the Event Dispatch Thread.

Next, I wrote the JFrame code. The JFrame code is nearly identical for every Swing GUI I create. The JFrame code is located in the run method of the FavoriteCharacter class.

Next, I wrote the model class, the LetterMap class. Whenever I create a complex Swing GUI, I use the model / view / controller pattern. This model is pretty simple. I have a map that contains the lower case characters of the English alphabet, along with vote counts.

Once I had the model working, I went back to creating the view. Each small part of the GUI is contained in a method of the FavoriteCharacter class. That way, I could test each small piece of the view individually. No throwing spaghetti code against the wall and seeing what sticks for me.

The actual drawing of the characters happens in a LetterPanel class. All Swing custom painting must be done in the paintComponent method of a JPanel.

After I finished the GUI, I worked on the two controller classes, PassListener and VoteListener. PassListener calls a method in the FavoriteCharacters class to select another pair of characters. VoteListener updates the LetterMap model class, displays the vote total for the selected character in a JOptionPane, and selects another pair of letters.

Here's the runnable, example code.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class FavoriteCharacter implements Runnable {

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

    private char[] letterPair;

    private JFrame frame;

    private LetterMap letterMap;

    private LetterPanel letterPanel1;
    private LetterPanel letterPanel2;

    public FavoriteCharacter() {
        this.letterMap = new LetterMap();
        this.letterPair = letterMap.pickTwo();
    }

    @Override
    public void run() {
        frame = new JFrame("Favorite Character");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(createTitlePanel(),
                BorderLayout.BEFORE_FIRST_LINE);
        frame.add(createMainPanel(letterPair),
                BorderLayout.CENTER);
        frame.add(createSkipPanel(),
                BorderLayout.AFTER_LAST_LINE);

        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JPanel createTitlePanel() {
        JPanel panel = new JPanel();

        JLabel label = new JLabel("Vote for your favorite character");
        label.setHorizontalAlignment(JLabel.CENTER);
        panel.add(label);

        return panel;
    }

    private JPanel createMainPanel(char[] letterPair) {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));

        panel.add(Box.createHorizontalStrut(10));
        letterPanel1 = new LetterPanel(Color.WHITE, letterPair[0]);
        panel.add(createLetterPanel(letterPanel1, "0"));

        panel.add(Box.createHorizontalStrut(10));

        letterPanel2 = new LetterPanel(Color.WHITE, letterPair[1]);
        panel.add(createLetterPanel(letterPanel2, "1"));

        panel.add(Box.createHorizontalStrut(10));

        return panel;
    }

    private void updateLetterPanels() {
        letterPanel1.setLetter(letterPair[0]);
        letterPanel2.setLetter(letterPair[1]);

        letterPanel1.repaint();
        letterPanel2.repaint();
    }

    private void updateLetterPair() {
        letterPair = letterMap.pickTwo();
        System.out.println(Arrays.toString(letterPair));
        updateLetterPanels();
    }

    private JPanel createLetterPanel(LetterPanel letterPanel,
            String actionCommand) {
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.CENTER;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = new Insets(0, 10, 10, 10);
        gbc.weightx = 0d;
        panel.add(letterPanel, gbc);

        gbc.gridy++;
        gbc.weightx = 1d;
        JButton button = new JButton("Vote");
        button.setActionCommand(actionCommand);
        button.setHorizontalAlignment(JButton.CENTER);
        button.addActionListener(new VoteListener());
        panel.add(button, gbc);

        return panel;
    }

    private JPanel createSkipPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.CENTER;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = new Insets(0, 18, 10, 18);
        gbc.weightx = 1d;

        JButton button = new JButton("Pass");
        button.setHorizontalAlignment(JButton.CENTER);
        button.addActionListener(new PassListener());
        panel.add(button, gbc);

        return panel;
    }

    public class LetterPanel extends JPanel {

        private static final long serialVersionUID = 1L;

        private char letter;

        private Color backgroundColor;

        private Font font;

        public LetterPanel(Color backgroundColor, char letter) {
            this.backgroundColor = backgroundColor;
            this.letter = letter;
            this.font = getFont().deriveFont(96f)
                    .deriveFont(Font.BOLD);
            this.setBorder(BorderFactory.createLineBorder(
                    Color.GREEN, 6));
            this.setPreferredSize(new Dimension(120, 200));
        }

        public void setLetter(char letter) {
            this.letter = letter;
        }

        public char getLetter() {
            return letter;
        }

        public LetterPanel getLetterPanel() {
            return this;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(backgroundColor);
            g2d.fillRect(0, 0, getWidth(), getHeight());

            g2d.setColor(Color.BLACK);
            drawCenteredString(g2d, Character.toString(letter),
                    font);
        }

        /**
         * Draw a String centered in the middle of the panel.
         *
         * @param g2d The Graphics2D instance.
         * @param text The String to draw.
         * @param font The Font to draw with.
         */
        public void drawCenteredString(Graphics2D g2d,
                String text, Font font) {
            FontMetrics metrics = g2d.getFontMetrics(font);
            int x = (getWidth() - metrics.stringWidth(text)) / 2;
            int y = ((getHeight() - metrics.getHeight()) / 2) +
                    metrics.getAscent();
            g2d.setFont(font);
            g2d.drawString(text, x, y);
        }

    }

    public class PassListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent event) {
            updateLetterPair();
        }

    }

    public class VoteListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent event) {
            int index = Integer.valueOf(event.getActionCommand());
            char c = letterPair[index];
            letterMap.addVote(c);
            int votes = letterMap.getVotes(c);

            showMessageDialog(c, votes);
        }

        private void showMessageDialog(char c, int votes) {
            String text = "The character '" + c + "' has ";
            text += Integer.toString(votes);
            if (votes == 1) {
                text += " vote.";
            } else {
                text += " votes.";
            }

            JOptionPane.showMessageDialog(frame, text);
            updateLetterPair();
        }

    }

    public class LetterMap {

        private Map<Character, Integer> letterMap;

        private Random random;

        public LetterMap() {
            this.letterMap = createLetterMap();
            this.random = new Random();
        }

        private Map<Character, Integer> createLetterMap() {
            Map<Character, Integer> letterMap = new TreeMap<>();

            for (int i = 0; i < 26; i++) {
                Character c = (char) (i + 'a');
                letterMap.put(c, 0);
            }
            return letterMap;
        }

        public char[] pickTwo() {
            int index1 = random.nextInt(letterMap.size());
            int index2 = random.nextInt(letterMap.size());
            while (index2 == index1) {
                index2 = random.nextInt(letterMap.size());
            }

            char[] output = new char[2];
            Set<Character> letterSet = letterMap.keySet();
            Iterator<Character> iter = letterSet.iterator();

            int count = 0;
            int index3 = 0;
            while (iter.hasNext() && index3 < 2) {
                Character key = iter.next();
                if (count == index1 || count == index2) {
                    if (index3 < 2) {
                        output[index3++] = key;
                    }
                }
                count++;
            }

            return output;
        }

        public void addVote(char c) {
            Integer vote = getVotes(c);
            letterMap.put(c, ++vote);
        }

        public int getVotes(char c) {
            return letterMap.get(c);
        }

    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111