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, t
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.
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 letterMap;
private Random random;
public LetterMap() {
this.letterMap = createLetterMap();
this.random = new Random();
}
private Map createLetterMap() {
Map 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 letterSet = letterMap.keySet();
Iterator 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);
}
}
}