Fixing method latency in Java - java

Another problem, same program:
The following is MainGUI.java
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class MainGUI extends JFrame implements ActionListener
{
private static final long serialVersionUID = 4149825008429377286L;
private final double version = 8;
public static int rows;
public static int columns;
private int totalCells;
private MainCell[] cell;
public static Color userColor;
JTextField speed = new JTextField("250");
Timer timer = new Timer(250,this);
String generationText = "Generation: 0";
JLabel generationLabel = new JLabel(generationText);
int generation = 0;
public MainGUI(String title, int r, int c)
{
rows = r;
columns = c;
totalCells = r*c;
System.out.println(totalCells);
cell = new MainCell[totalCells];
setTitle(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Timer set up
timer.setInitialDelay(Integer.parseInt(speed.getText()));
timer.setActionCommand("timer");
//set up menu bar
JMenuBar menuBar = new JMenuBar();
JMenu optionsMenu = new JMenu("Options");
JMenu aboutMenu = new JMenu("About");
menuBar.add(optionsMenu);
menuBar.add(aboutMenu);
JMenuItem helpButton = new JMenuItem("Help!");
helpButton.addActionListener(this);
helpButton.setActionCommand("help");
aboutMenu.add(helpButton);
JMenuItem aboutButton = new JMenuItem("About");
aboutButton.addActionListener(this);
aboutButton.setActionCommand("about");
aboutMenu.add(aboutButton);
JMenuItem colorSelect = new JMenuItem("Select a Custom Color");
colorSelect.addActionListener(this);
colorSelect.setActionCommand("colorSelect");
optionsMenu.add(colorSelect);
JMenuItem sizeChooser = new JMenuItem("Define a Custom Size");
sizeChooser.addActionListener(this);
sizeChooser.setActionCommand("sizeChooser");
optionsMenu.add(sizeChooser);
//Create text field to adjust speed and its label
JPanel speedContainer = new JPanel();
JLabel speedLabel = new JLabel("Enter the speed of a life cycle (in ms):");
speedContainer.add(speedLabel);
speedContainer.add(speed);
speedContainer.add(generationLabel);
Dimension speedDim = new Dimension(100,25);
speed.setPreferredSize(speedDim);
//Create various buttons
JPanel buttonContainer = new JPanel();
JButton randomizerButton = new JButton("Randomize");
randomizerButton.addActionListener(this);
randomizerButton.setActionCommand("randomize");
buttonContainer.add(randomizerButton);
JButton nextButton = new JButton("Next"); //forces a cycle to occur
nextButton.addActionListener(this);
nextButton.setActionCommand("check");
buttonContainer.add(nextButton);
JButton startButton = new JButton("Start");
startButton.addActionListener(this);
startButton.setActionCommand("start");
buttonContainer.add(startButton);
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(this);
stopButton.setActionCommand("stop");
buttonContainer.add(stopButton);
JButton clearButton = new JButton("Clear");
clearButton.addActionListener(this);
clearButton.setActionCommand("clear");
buttonContainer.add(clearButton);
//holds the speed container and button container, keeps it neat
JPanel functionContainer = new JPanel();
BoxLayout functionLayout = new BoxLayout(functionContainer, BoxLayout.PAGE_AXIS);
functionContainer.setLayout(functionLayout);
functionContainer.add(speedContainer);
speedContainer.setAlignmentX(CENTER_ALIGNMENT);
functionContainer.add(buttonContainer);
buttonContainer.setAlignmentX(CENTER_ALIGNMENT);
//finish up with the cell container
GridLayout cellLayout = new GridLayout(rows,columns);
JPanel cellContainer = new JPanel(cellLayout);
cellContainer.setBackground(Color.black);
int posX = 0;
int posY = 0;
for(int i=0;i<totalCells;i++)
{
MainCell childCell = new MainCell();
cell[i] = childCell;
childCell.setName(String.valueOf(i));
childCell.setPosX(posX);
posX++;
childCell.setPosY(posY);
if(posX==columns)
{
posX = 0;
posY++;
}
cellContainer.add(childCell);
childCell.deactivate();
Graphics g = childCell.getGraphics();
childCell.paint(g);
}
//make a default color
userColor = Color.yellow;
//change icon
URL imgURL = getClass().getResource("images/gol.gif");
ImageIcon icon = new ImageIcon(imgURL);
System.out.println(icon);
setIconImage(icon.getImage());
//add it all up and pack
JPanel container = new JPanel();
BoxLayout containerLayout = new BoxLayout(container, BoxLayout.PAGE_AXIS);
container.setLayout(containerLayout);
container.add(cellContainer);
container.add(functionContainer);
add(menuBar);
setJMenuBar(menuBar);
add(container);
pack();
}
private void checkCells()
{
//perform check for every cell
for(int i=0;i<totalCells;i++)
{
cell[i].setNeighbors(checkNeighbors(i));
}
//use value from check to determine life
for(int i=0;i<totalCells;i++)
{
int neighbors = cell[i].getNeighbors();
if(cell[i].isActivated())
{
System.out.println(cell[i].getName()+" "+neighbors);
if(neighbors==0||neighbors==1||neighbors>3)
{
cell[i].deactivate();
}
}
if(cell[i].isActivated()==false)
{
if(neighbors==3)
{
cell[i].activate();
}
}
}
}
public void actionPerformed(ActionEvent e)
{
if(e.getActionCommand().equals("randomize"))
{
Random rn = new Random();
for(int i=0;i<totalCells;i++)
{
cell[i].deactivate();
if(rn.nextInt(6)==0)
{
cell[i].activate();
}
}
}
//help button, self-explanatory
if(e.getActionCommand().equals("help"))
{
JOptionPane.showMessageDialog(this, "The game is governed by four rules:\nFor a space that is 'populated':"
+ "\n Each cell with one or no neighbors dies, as if by loneliness."
+ "\n Each cell with four or more neighbors dies, as if by overpopulation."
+ "\n Each cell with two or three neighbors survives."
+ "\nFor a space that is 'empty' or 'unpopulated':"
+ "\n Each cell with three neighbors becomes populated."
+ "\nLeft click populates cells. Right click depopulates cells.","Rules:",JOptionPane.PLAIN_MESSAGE);
}
//shameless self promotion
if(e.getActionCommand().equals("about"))
{
JOptionPane.showMessageDialog(this, "Game made and owned by *****!"
+ "\nFree usage as see fit, but give credit where credit is due!\nVERSION: "+version,"About:",JOptionPane.PLAIN_MESSAGE);
}
//clears all the cells
if(e.getActionCommand().equals("clear"))
{
timer.stop();
generation = 0;
generationText = "Generation: "+generation;
generationLabel.setText(generationText);
for(int i=0;i<totalCells;i++)
{
cell[i].deactivate();
}
}
//starts timer
if(e.getActionCommand().equals("start"))
{
if(Integer.parseInt(speed.getText())>0)
{
timer.setDelay(Integer.parseInt(speed.getText()));
timer.restart();
}
else
{
JOptionPane.showMessageDialog(this, "Please use a value greater than 0!","Rules:",JOptionPane.ERROR_MESSAGE);
}
}
//stops timer
if(e.getActionCommand().equals("stop"))
{
timer.stop();
}
//run when timer
if(e.getActionCommand().equals("timer"))
{
generation++;
generationText = "Generation: "+generation;
generationLabel.setText(generationText);
timer.stop();
checkCells();
timer.setInitialDelay(Integer.parseInt(speed.getText()));
timer.restart();
}
//see checkCells()
if(e.getActionCommand().equals("check"))
{
generation++;
generationText = "Generation: "+generation;
generationLabel.setText(generationText);
checkCells();
}
//color select gui
if(e.getActionCommand().equals("colorSelect"))
{
userColor = JColorChooser.showDialog(this, "Choose a color:", userColor);
if(userColor==null)
{
userColor = Color.yellow;
}
}
//size chooser!
if(e.getActionCommand().equals("sizeChooser"))
{
SizeChooser size = new SizeChooser();
size.setLocationRelativeTo(null);
size.setVisible(true);
}
}
private int checkNeighbors(int c)
{
//if a LIVE neighbor is found, add one
int neighbors = 0;
if(cell[c].getPosX()!=0&&cell[c].getPosY()!=0)
{
if(c-columns-1>=0)
{
if(cell[c-columns-1].isActivated())
{
System.out.println(cell[c].getName()+" found "+cell[c-columns-1].getName());
neighbors++;
}
}
}
if(cell[c].getPosY()!=0)
{
if(c-columns>=0)
{
if(cell[c-columns].isActivated())
{
System.out.println(cell[c].getName()+" found "+cell[c-columns].getName());
neighbors++;
}
}
}
if(cell[c].getPosX()!=columns-1&&cell[c].getPosY()!=0)
{
if(c-columns+1>=0)
{
if(cell[c-columns+1].isActivated())
{
System.out.println(cell[c].getName()+" found "+cell[c-columns+1].getName());
neighbors++;
}
}
}
if(cell[c].getPosX()!=0)
{
if(c-1>=0)
{
if(cell[c-1].isActivated())
{
System.out.println(cell[c].getName()+" found "+cell[c-1].getName());
neighbors++;
}
}
}
if(cell[c].getPosX()!=columns-1)
{
if(c+1<totalCells)
{
if(cell[c+1].isActivated())
{
System.out.println(cell[c].getName()+" found "+cell[c+1].getName());
neighbors++;
}
}
}
if(cell[c].getPosX()!=0&&cell[c].getPosY()!=rows-1)
{
if(c+columns-1<totalCells)
{
if(cell[c+columns-1].isActivated())
{
System.out.println(cell[c].getName()+" found "+cell[c+columns-1].getName());
neighbors++;
}
}
}
if(cell[c].getPosY()!=rows-1&&cell[c].getPosY()!=rows-1)
{
if(c+columns<totalCells)
{
if(cell[c+columns].isActivated())
{
System.out.println(cell[c].getName()+" found "+cell[c+columns].getName());
neighbors++;
}
}
}
if(cell[c].getPosX()!=columns-1&&cell[c].getPosY()!=rows-1)
{
if(c+columns+1<totalCells)
{
if(cell[c+columns+1].isActivated())
{
System.out.println(cell[c].getName()+" found "+cell[c+columns+1].getName());
neighbors++;
}
}
}
return neighbors;
}
}
The following is MainCell.java:
public class MainCell extends JPanel implements MouseListener
{
//everything here should be self-explanatory
private static final long serialVersionUID = 1761933778208900172L;
private boolean activated = false;
public static boolean leftMousePressed;
public static boolean rightMousePressed;
private int posX = 0;
private int posY = 0;
private int neighbors = 0;
private URL cellImgURL_1 = getClass().getResource("images/cellImage_1.gif");
private ImageIcon cellImageIcon_1 = new ImageIcon(cellImgURL_1);
private Image cellImage_1 = cellImageIcon_1.getImage();
private URL cellImgURL_2 = getClass().getResource("images/cellImage_2.gif");
private ImageIcon cellImageIcon_2 = new ImageIcon(cellImgURL_2);
private Image cellImage_2 = cellImageIcon_2.getImage();
private URL cellImgURL_3 = getClass().getResource("images/cellImage_3.gif");
private ImageIcon cellImageIcon_3 = new ImageIcon(cellImgURL_3);
private Image cellImage_3 = cellImageIcon_3.getImage();
public MainCell()
{
Dimension dim = new Dimension(17, 17);
setPreferredSize(dim);
addMouseListener(this);
}
public void activate()
{
setBackground(MainGUI.userColor);
System.out.println(getName()+" "+posX+","+posY+" activated");
setActivated(true);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
if(getPosX()==MainGUI.columns-1&&getPosY()==0)
{
//do nothing
}
else if(getPosY()!=0&&getPosX()!=MainGUI.columns-1)
{
g.drawImage(cellImage_1,0,0,null);
}
else if(getPosY()==0)
{
g.drawImage(cellImage_2,0,0,null);
}
else if(getPosX()==MainGUI.columns-1)
{
g.drawImage(cellImage_3,0,0,null);
}
}
public void setActivated(boolean b)
{
activated = b;
}
public void deactivate()
{
setBackground(Color.gray);
System.out.println(getName()+" "+posX+","+posY+" deactivated");
setActivated(false);
}
public boolean isActivated()
{
return activated;
}
public void setNeighbors(int i)
{
neighbors = i;
}
public int getNeighbors()
{
return neighbors;
}
public int getPosX()
{
return posX;
}
public void setPosX(int x)
{
posX = x;
}
public int getPosY()
{
return posY;
}
public void setPosY(int y)
{
posY = y;
}
public void mouseClicked(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
if(leftMousePressed&&SwingUtilities.isLeftMouseButton(e))
{
activate();
}
if(rightMousePressed&&SwingUtilities.isRightMouseButton(e))
{
deactivate();
}
}
public void mouseExited(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
if(SwingUtilities.isRightMouseButton(e)&&!leftMousePressed)
{
deactivate();
rightMousePressed = true;
}
if(SwingUtilities.isLeftMouseButton(e)&&!rightMousePressed)
{
activate();
leftMousePressed = true;
}
}
public void mouseReleased(MouseEvent e)
{
if(SwingUtilities.isRightMouseButton(e))
{
rightMousePressed = false;
}
if(SwingUtilities.isLeftMouseButton(e))
{
leftMousePressed = false;
}
}
}
The following is SizeChooser.java:
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import javax.swing.*;
public class SizeChooser extends JFrame
{
private static final long serialVersionUID = -6431709376438241788L;
public static MainGUI GUI;
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
JTextField rowsTextField = new JTextField(String.valueOf((screenSize.height/17)-15));
JTextField columnsTextField = new JTextField(String.valueOf((screenSize.width/17)-10));
private static int rows = screenSize.height/17-15;
private static int columns = screenSize.width/17-10;
public SizeChooser()
{
setResizable(false);
setTitle("Select a size!");
JPanel container = new JPanel();
BoxLayout containerLayout = new BoxLayout(container, BoxLayout.PAGE_AXIS);
container.setLayout(containerLayout);
add(container);
JLabel rowsLabel = new JLabel("Rows:");
container.add(rowsLabel);
container.add(rowsTextField);
JLabel columnsLabel = new JLabel("Columns:");
container.add(columnsLabel);
container.add(columnsTextField);
JButton confirmSize = new JButton("Confirm");
confirmSize.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
GUI.setVisible(false);
GUI = null;
if(Integer.parseInt(rowsTextField.getText())>0)
{
rows = Integer.parseInt(rowsTextField.getText());
}
else
{
JOptionPane.showMessageDialog(rootPane, "Please use a value greater than 0!","Rules:",JOptionPane.ERROR_MESSAGE);
}
if(Integer.parseInt(columnsTextField.getText())>0)
{
columns = Integer.parseInt(columnsTextField.getText());
}
else
{
JOptionPane.showMessageDialog(rootPane, "Please use a value greater than 0!","Rules:",JOptionPane.ERROR_MESSAGE);
}
GUI = new MainGUI("The Game of Life!", rows, columns);
GUI.setLocationRelativeTo(null);
GUI.setVisible(true);
setVisible(false);
}
});
container.add(confirmSize);
URL imgURL = getClass().getResource("images/gol.gif");
ImageIcon icon = new ImageIcon(imgURL);
System.out.println(icon);
setIconImage(icon.getImage());
pack();
}
public static void main(String[]args)
{
GUI = new MainGUI("The Game of Life!", rows, columns);
GUI.setLocationRelativeTo(null);
GUI.setVisible(true);
}
}
So the problem now is, when the randomize button is pressed, or a large number of cells exist and then the timer is started, the cells aren't as snappy as they would be with less active cells. For example, with 100 columns and 50 rows, when the randomize button is pressed, one cell activates, then the next, then another, and so forth. Can I have them all activate at exactly the same time? Is this just a problem with too many things calculated at once? Would concurrency help?
QUICK EDIT: Is the swing timer the best idea for this project?

I havent read through your code completely but I am guessing that your listener methods are fairly computationally intensive and hence the lag in updating the display.

This simple test reveals that your randomize operation should be fairly quick:
public static void main(String args[]) {
Random rn = new Random();
boolean b[] = new boolean[1000000];
long timer = System.nanoTime();
for (int i = 0; i < b.length; i++) {
b[i] = rn.nextInt(6) == 0;
}
timer = System.nanoTime() - timer;
System.out.println(timer + "ns / " + (timer / 1000000) + "ms");
}
The output for me is:
17580267ns / 17ms
So this leads me into thinking activate() or deactivate() is causing your UI to be redrawn.
I am unable to run this because I don't have your graphical assets, but I would try those changes to see if it works:
In MainGUI#actionPerformed, change:
if(e.getActionCommand().equals("randomize"))
{
Random rn = new Random();
for(int i=0;i<totalCells;i++)
{
cell[i].deactivate();
if(rn.nextInt(6)==0)
{
cell[i].activate();
}
}
}
to:
if(e.getActionCommand().equals("randomize"))
{
Random rn = new Random();
for(int i=0;i<totalCells;i++)
{
// This will not cause the object to be redrawn and should
// be a fairly cheap operation
cell[i].setActivated(rn.nextInt(6)==0);
}
// Cause the UI to repaint
repaint();
}
Add this to MainCell
// You can specify those colors however you like
public static final Color COLOR_ACTIVATED = Color.RED;
public static final Color COLOR_DEACTIVATED = Color.GRAY;
And change:
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
if(getPosX()==MainGUI.columns-1&&getPosY()==0)
{
//do nothing
}
to:
protected void paintComponent(Graphics g)
{
// We now make UI changes only when the component is painted
setBackground(activated ? COLOR_ACTIVATED : COLOR_DEACTIVATED);
super.paintComponent(g);
if(getPosX()==MainGUI.columns-1&&getPosY()==0)
{
//do nothing
}

Related

String won't display using drawString()

I'm writing a simple Trivia game and I ran into a problem where the question string won't display
In my frame I'm using border layout with "new game" and "end game" on the bottom (South) and my Trivia panel in the center.
The Trivia panel is made of grid layout with 2 rows and 1 column with the 4 answers on the bottom half and on the upper half I used another panel with border layout with the question string in the center and the score in the east.
It should look like this:
However all of the components display except for the question string.
My paintComponent is:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
g.drawString(tf,0,0);
}
where tf is my string and my Trivia panel code is:
public TriviaPanel(){
score = 0;
scoreString = new JTextField();
scoreString.setFont(font);
questionPanel = new JPanel();
questionPanel.setLayout(new BorderLayout());
questionPanel.add(scoreString,BorderLayout.EAST);
this.setLayout(new GridLayout(2,1));
pool = null;
try {
pool = new Pool();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
question = pool.getQuestion();
String[] answers = question.shuffle();
tf = question.getQuestion();
this.add(questionPanel);
answersPanel = new JPanel();
answersPanel.setLayout(new GridLayout(2,2));
buttons = new JButton[NUM_OF_ANSWERS];
for (int i = 0; i < NUM_OF_ANSWERS; i++) {
buttons[i] = new JButton(answers[i]);
answersPanel.add(buttons[i]);
buttons[i].addActionListener(lis);
}
this.add(answersPanel);
scoreString.setText("Score: "+score+"/"+pool.getIterator()*CORRECT);
}
where pool is used to hold my pool of questions.
When I debugged the code I see the tf string being updated to the question string but it won't display.
Is it because of the coordinates?
Any insights would be greatly appreciated.
[Edit] Although not finished but full code below:
import java.util.Arrays;
import java.util.Collections;
public class Question {
private final int NUM_OF_ANSWERS = 4;
private String question;
private String[] answers = new String[NUM_OF_ANSWERS];
private final int CORRECT_ANSWER = 0;
public Question(String qu, String an, String dm1, String dm2, String
dm3){
question = qu;
answers[0] = an;
answers[1] = dm1;
answers[2] = dm2;
answers[3] = dm3;
}
public String getCorrectAnswer() {
return answers[CORRECT_ANSWER];
}
public String getQuestion(){
return question;
}
public String[] getAnswers(){
return answers;
}
public String toString(){
String str = question;
for (int i = 0; i<4; i++)
str+=" "+answers[i];
str+="\n";
return str;
}
public String[] shuffle(){
String[] shuffled = new String[NUM_OF_ANSWERS];
for (int i=0;i<NUM_OF_ANSWERS;i++)
shuffled[i]=answers[i];
Collections.shuffle(Arrays.asList(shuffled));
return shuffled;
}
}
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Scanner;
public class Pool {
private ArrayList<Question> questions = new ArrayList<>();
private Scanner input = new Scanner(new File("src/trivia.txt"));
private static int iterator = 0;
public Pool() throws FileNotFoundException {
while (input.hasNext()){
String q = input.nextLine();
String a = input.nextLine();
String d1 = input.nextLine();
String d2 = input.nextLine();
String d3 = input.nextLine();
Question question = new Question(q,a,d1,d2,d3);
questions.add(question);
}
Collections.shuffle(questions);
//System.out.println(questions);
}
public Question getQuestion(){
Question q = questions.get(iterator);
iterator++;
return q;
}
public int getSize(){
return questions.size();
}
public static int getIterator() {
return iterator;
}
}
import javax.swing.*;
import java.awt.*;
public class GameFrame extends JFrame {
private JButton restart, finish;
private JPanel buttons;
public GameFrame(){
super("Trivia");
TriviaPanel tp = new TriviaPanel();
this.setLayout(new BorderLayout());
this.add(tp,BorderLayout.CENTER);
restart = new JButton("New game");
finish = new JButton("End game");
buttons = new JPanel();
buttons.add(restart);
buttons.add(finish);
this.add(buttons,BorderLayout.SOUTH);
this.setSize(1000,600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
public static void main(String[] args) {
GameFrame gf = new GameFrame();
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import java.util.concurrent.TimeUnit;
public class TriviaPanel extends JPanel {
private TimerListener tl = new TimerListener();
private Timer timer = new Timer(10000,tl);
private static int score;
private JTextField scoreString;
private final int CORRECT = 10, INCORRECT = 5;
private JButton[] buttons;
private Pool pool;
private Question question;
private JButton pressed;
private final int NUM_OF_ANSWERS = 4;
private Listener lis = new Listener();
//private JPanel questionPanel;
private JPanel answersPanel;
private String tf;
private Font font = new Font("Serif",Font.BOLD,24);
private JTextField tf2 = new JTextField();
private QuestionPanel questionPanel;
public TriviaPanel(){
score = 0;
scoreString = new JTextField();
scoreString.setFont(font);
questionPanel = new QuestionPanel();
//questionPanel.setLayout(new BorderLayout());
//questionPanel.add(scoreString,BorderLayout.EAST);
this.setLayout(new GridLayout(2,1));
pool = null;
try {
pool = new Pool();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
question = pool.getQuestion();
String[] answers = question.shuffle();
tf = question.getQuestion();
//tf2.setText(question.getQuestion());
//questionPanel.add(tf2,BorderLayout.CENTER);
this.add(questionPanel);
answersPanel = new JPanel();
answersPanel.setLayout(new GridLayout(2,2));
buttons = new JButton[NUM_OF_ANSWERS];
for (int i = 0; i < NUM_OF_ANSWERS; i++) {
buttons[i] = new JButton(answers[i]);
answersPanel.add(buttons[i]);
buttons[i].addActionListener(lis);
}
this.add(answersPanel);
timer.start();
scoreString.setText("Score: "+score+"/"+pool.getIterator()*CORRECT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
questionPanel.repaint();
}
private void next(){
Question q = pool.getQuestion();
question=q;
tf = q.getQuestion();
String[] answers = q.shuffle();
for (int i = 0; i < NUM_OF_ANSWERS; i++)
buttons[i].setText(answers[i]);
}
private void gameOver(){
JOptionPane.showConfirmDialog(null,
"Score: "+score, "Select an Option...", JOptionPane.YES_NO_CANCEL_OPTION);
}
private void check(String guess) {
timer.stop();
String answer = question.getCorrectAnswer();
if (guess.equals(answer)) {
score += CORRECT;
tf = "Correct!!!";
} else {
score -= INCORRECT;
tf = "Wrong answer";
}
scoreString.setText("Score: "+score+"/"+pool.getIterator()*CORRECT);
repaint();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class QuestionPanel extends JPanel{
public QuestionPanel(){
this.setLayout(new BorderLayout());
this.add(scoreString,BorderLayout.EAST);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
g.drawString(tf,0,200);
}
}
private class Listener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
pressed = (JButton) e.getSource();
String guess = pressed.getText();
check(guess);
if (pool.getIterator() < pool.getSize()) {
timer.restart();
next();
}
else
gameOver();
}
}
private class TimerListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
timer.stop();
score-=INCORRECT;
scoreString.setText("Score: "+score+"/"+pool.getIterator()*CORRECT);
repaint();
timer.restart();
next();
}
}
}
Start by getting rid of
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
questionPanel.repaint();
}
Seriously this is just dangerous and could set you for a endless loop which will consume your CPU cycles
Next, I modified your QuestionPanel so the text is actually rendered somewhere within the realms of probability ...
private class QuestionPanel extends JPanel {
public QuestionPanel() {
this.setLayout(new BorderLayout());
this.add(scoreString, BorderLayout.EAST);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
FontMetrics fm = g.getFontMetrics();
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g.drawString(tf, 10, y);
}
}
But seriously, why aren't you just using a JLabel?
Now, any time tf changes, you need to call QuestionPanel's repaint method.
So, that's in the next and check(String) methods.
And finally (for now), you should never, ever do...
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
inside the context of the event dispatching thread. This is stopping the repaints from been processed, so, for one whole second, nothing will change.
If you want to stop the user for a second, disable the buttons and/or other controls and use another Swing Timer (for simplicity)
This is demonstrated in this self contained, compilable and runnable example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GameFrame frame = new GameFrame();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GameFrame extends JFrame {
private JButton restart, finish;
private JPanel buttons;
public GameFrame() {
super("Trivia");
TriviaPanel tp = new TriviaPanel();
this.setLayout(new BorderLayout());
this.add(tp, BorderLayout.CENTER);
restart = new JButton("New game");
finish = new JButton("End game");
buttons = new JPanel();
buttons.add(restart);
buttons.add(finish);
this.add(buttons, BorderLayout.SOUTH);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
public static class TriviaPanel extends JPanel {
// private TimerListener tl = new TimerListener();
// private Timer timer = new Timer(10000, tl);
private static int score;
private JTextField scoreString;
private final int CORRECT = 10, INCORRECT = 5;
private JButton[] buttons;
private Pool pool;
private Question question;
private JButton pressed;
private final int NUM_OF_ANSWERS = 4;
private Listener lis = new Listener();
//private JPanel questionPanel;
private JPanel answersPanel;
private String tf;
private Font font = new Font("Serif", Font.BOLD, 24);
private JTextField tf2 = new JTextField();
private QuestionPanel questionPanel;
public TriviaPanel() {
score = 0;
scoreString = new JTextField();
scoreString.setFont(font);
questionPanel = new QuestionPanel();
//questionPanel.setLayout(new BorderLayout());
//questionPanel.add(scoreString,BorderLayout.EAST);
this.setLayout(new GridLayout(2, 1));
pool = null;
try {
pool = new Pool();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
question = pool.getQuestion();
String[] answers = question.shuffle();
tf = question.getQuestion();
//tf2.setText(question.getQuestion());
//questionPanel.add(tf2,BorderLayout.CENTER);
this.add(questionPanel);
answersPanel = new JPanel();
answersPanel.setLayout(new GridLayout(2, 2));
buttons = new JButton[NUM_OF_ANSWERS];
for (int i = 0; i < NUM_OF_ANSWERS; i++) {
buttons[i] = new JButton(answers[i]);
answersPanel.add(buttons[i]);
buttons[i].addActionListener(lis);
}
this.add(answersPanel);
// timer.start();
scoreString.setText("Score: " + score + "/" + pool.getIterator() * CORRECT);
}
// #Override
// protected void paintComponent(Graphics g) {
// super.paintComponent(g);
// questionPanel.repaint();
// }
private void next() {
Question q = pool.getQuestion();
question = q;
tf = q.getQuestion();
String[] answers = q.shuffle();
for (int i = 0; i < NUM_OF_ANSWERS; i++) {
buttons[i].setText(answers[i]);
}
questionPanel.repaint();
}
private void gameOver() {
JOptionPane.showConfirmDialog(null,
"Score: " + score, "Select an Option...", JOptionPane.YES_NO_CANCEL_OPTION);
}
private void check(String guess) {
// timer.stop();
String answer = question.getCorrectAnswer();
if (guess.equals(answer)) {
score += CORRECT;
tf = "Correct!!!";
} else {
score -= INCORRECT;
tf = "Wrong answer";
}
questionPanel.repaint();
scoreString.setText("Score: " + score + "/" + pool.getIterator() * CORRECT);
// OH GOD THIS IS A BAD IDEA!
// try {
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Timer timer = (Timer) e.getSource();
timer.stop();
// Personally, I'd use a listener, but this will do
afterCheckDelay();
}
});
timer.start();
}
protected void afterCheckDelay() {
if (pool.getIterator() < pool.getSize()) {
//timer.restart();
next();
} else {
gameOver();
}
}
private class QuestionPanel extends JPanel {
public QuestionPanel() {
this.setLayout(new BorderLayout());
this.add(scoreString, BorderLayout.EAST);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
FontMetrics fm = g.getFontMetrics();
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g.drawString(tf, 10, y);
}
}
private class Listener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
pressed = (JButton) e.getSource();
String guess = pressed.getText();
check(guess);
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// timer.stop();
// score -= INCORRECT;
// scoreString.setText("Score: " + score + "/" + pool.getIterator() * CORRECT);
// repaint();
// timer.restart();
// next();
}
}
}
public static class Pool {
private ArrayList<Question> questions = new ArrayList<>();
// private Scanner input = new Scanner(new File("src/trivia.txt"));
private static int iterator = 0;
public Pool() throws FileNotFoundException {
Question question = new Question("Why am I doing this", "Because I'm awesome", "You feel for the trick", "You have no idea", "To much caffine");
questions.add(question);
// while (input.hasNext()) {
// String q = input.nextLine();
// String a = input.nextLine();
// String d1 = input.nextLine();
// String d2 = input.nextLine();
// String d3 = input.nextLine();
// Question question = new Question(q, a, d1, d2, d3);
// questions.add(question);
// }
Collections.shuffle(questions);
//System.out.println(questions);
}
public Question getQuestion() {
Question q = questions.get(iterator);
iterator++;
return q;
}
public int getSize() {
return questions.size();
}
public static int getIterator() {
return iterator;
}
}
public static class Question {
private final int NUM_OF_ANSWERS = 4;
private String question;
private String[] answers = new String[NUM_OF_ANSWERS];
private final int CORRECT_ANSWER = 0;
public Question(String qu, String an, String dm1, String dm2, String dm3) {
question = qu;
answers[0] = an;
answers[1] = dm1;
answers[2] = dm2;
answers[3] = dm3;
}
public String getCorrectAnswer() {
return answers[CORRECT_ANSWER];
}
public String getQuestion() {
return question;
}
public String[] getAnswers() {
return answers;
}
public String toString() {
String str = question;
for (int i = 0; i < 4; i++) {
str += " " + answers[i];
}
str += "\n";
return str;
}
public String[] shuffle() {
String[] shuffled = new String[NUM_OF_ANSWERS];
for (int i = 0; i < NUM_OF_ANSWERS; i++) {
shuffled[i] = answers[i];
}
Collections.shuffle(Arrays.asList(shuffled));
return shuffled;
}
}
}

How to calculate a total depending on what a user seletcs in the combobox?

I want the user to select options from a combo box and based on what they select the output field will calculate the price in it.
For example if they select Intel Core i5 it will add $50 to the calculated price
They error I have is that my output box is adding only the highest/last values together.
My three combo boxes are name Processor, Memory, Disk
the box I want the price to calculate in is named Output
i mainly just need help with how to select the options in my combo boxes in the if statements
private void calculateButtonActionPerformed(java.awt.event.ActionEvent evt) {
double price; //initilize variable
double windowsUpgrade; //Initilize variable
double processorPrice;
double memoryPrice;
double diskPrice;
if(Processor.getSelectedItem().equals("Intel Core i3"));
{
processorPrice = 0;
}
if(Processor.getSelectedItem().equals("Intel Core i5"));
{
processorPrice = 50;
}
if(Processor.getSelectedItem().equals("Intel Core i7"));
{
processorPrice = 150;
}
if(Memory.getSelectedItem().equals("4GB"));
{
memoryPrice = 0;
}
if(Memory.getSelectedItem().equals("8GB"));
{
memoryPrice = 50;
}
if(Memory.getSelectedItem().equals("16GB"));
{
memoryPrice = 100;
}
if(Memory.getSelectedItem().equals("32GB"));
{
memoryPrice = 150;
}
if(Disk.getSelectedItem().equals("1TB"));
{
diskPrice = 0;
}
if(Disk.getSelectedItem().equals("2TB"));
{
diskPrice = 50;
}
if(Disk.getSelectedItem().equals("512GB SSD"));
{
diskPrice = 150;
}
double calculation;
calculation = processorPrice + memoryPrice + diskPrice;
NumberFormat currency = NumberFormat.getCurrencyInstance();
String message =
currency.format(calculation);
Output.setText(message);
}
Here is the code.
You could use listener which react on your selection.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
class ComboBoxTest
{
//A reference to the UI class where you initialize the buttons,
//combo box etc
private ComboBoxUI _ui;
private double _processorPrice;
private double _memoryPrice;
private double _diskPrice;
public ComboBoxTest()
{
_processorPrice = 0;
_memoryPrice = 0;
_diskPrice = 0;
_ui = new ComboBoxUI();
registerListener();
}
//Register three different comboBoxes and a button for
//the calculation to listener
private void registerListener()
{
_ui.getProcessorBox().addItemListener(new ItemListener()
{
#Override
public void itemStateChanged(ItemEvent e)
{
if (e.getItem().equals("Intel Core i3"))
{
_processorPrice = 0;
}
else if (e.getItem().equals("Intel Core i5"))
{
_processorPrice = 50;
}
else if (e.getItem().equals("Intel Core i7"))
{
_processorPrice = 150;
}
}
});
_ui.getMemoryBox().addItemListener(new ItemListener()
{
#Override
public void itemStateChanged(ItemEvent e)
{
if (e.getItem().equals("4GB"))
{
_memoryPrice = 0;
}
else if (e.getItem().equals("8GB"))
{
_memoryPrice = 50;
}
else if (e.getItem().equals("16GB"))
{
_memoryPrice = 100;
}
else if (e.getItem().equals("32GB"))
{
_memoryPrice = 150;
}
}
});
_ui.getDiscBox().addItemListener(new ItemListener()
{
#Override
public void itemStateChanged(ItemEvent e)
{
if (e.getItem().equals("1TB"))
{
_diskPrice = 0;
}
else if (e.getItem().equals("2TB"))
{
_diskPrice = 50;
}
else if (e.getItem().equals("512GB SSD"))
{
_diskPrice = 150;
}
}
});
//getCalcButton() is a new Button that calcualtes the price
_ui.getCalcButton().addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println(_processorPrice + _memoryPrice + _diskPrice);
}
});
}
}
With my example you need a UI class of your components. Something like this :)
import java.awt.BorderLayout;
import javax.swing.*;
public class ComboBoxUI
{
JFrame _frame;
JPanel _componentPanel;
JButton _calcButton;
JComboBox _procBox;
JComboBox _memBox;
JComboBox _discBox;
ComboBoxUI()
{
initFrame();
initComponenentPanel();
showGui();
}
//initialize a new JFrame with BorderLayout in this example
private void initFrame()
{
_frame = new JFrame("Title");
_frame.setLayout(new BorderLayout());
}
//initialize a new JPanel with your components like comboBox, buttons...
private void initComponenentPanel()
{
_componentPanel = new JPanel();
String[] proc = { "Intel Core i3", "Intel Core i5", "Intel Core i7"};
_procBox = new JComboBox(proc);
String[] memory = { "4GB", "8GB", "16GB", "32GB"};
_memBox = new JComboBox(memory);
String[] disc = { "1TB", "2TB", "512GB SSD"};
_discBox = new JComboBox(disc);
_calcButton = new JButton("Calculate");
_componentPanel.add(_procBox);
_componentPanel.add(_memBox);
_componentPanel.add(_discBox);
_componentPanel.add(_calcButton);
_frame.add(_componentPanel, BorderLayout.NORTH);
}
//Set the size of your JFrame and make it visible
private void showGui()
{
_frame.setSize(500, 200);
_frame.setVisible(true);
_frame.setDefaultCloseOperation(_frame.EXIT_ON_CLOSE);
}
public JComboBox getProcessorBox()
{
return _procBox;
}
public JComboBox getMemoryBox()
{
return _memBox;
}
public JComboBox getDiscBox()
{
return _discBox;
}
public JButton getCalcButton()
{
return _calcButton;
}
}

JPanel doesn't react to MouseEvents?

I'm trying to create a "Tic Tac Toe" game. I've chosen to create a variation of JPanel to represent each square. The class beneath represents one of 9 panels that together make up my game board.
Now the problem I'm having is that when I click the panel a 'X' should be displayed inside of the panel, but nothing happens. I'd very much appreciate it if someone steered me in the right direction.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TicTacToePanel extends JPanel implements MouseListener {
private boolean isPlayer1Turn = true;
private boolean isUsed = false;
private JLabel ticTacLbl = new JLabel();
public TicTacToePanel() {
setBorder(BorderFactory.createLineBorder(Color.BLACK));
addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
if (!isUsed) {
if (isPlayer1Turn) {
ticTacLbl.setForeground(Color.red);
ticTacLbl.setText("X");
add(ticTacLbl, 0);
isUsed = true;
} else {
ticTacLbl.setForeground(Color.blue);
ticTacLbl.setText("O");
add(ticTacLbl, 0);
isUsed = true;
}
} else {
}
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, new TicTacToePanel());
}
}
EDIT:
I simply added my label component in the constructor of my TicTacToePanel so that I no longer have to call revalidate() and I'm not adding components during runtime.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TicTacToePanel extends JPanel implements MouseListener{
private boolean isPlayer1Turn = true;
private boolean isUsed = false;
private JLabel ticTacLbl = new JLabel();
public TicTacToePanel(){
add(ticTacLbl, 0);
setBorder(BorderFactory.createLineBorder(Color.BLACK));
addMouseListener(this);
}
public void mouseClicked(MouseEvent e){
}
public void mousePressed(MouseEvent e){
if (!isUsed) {
if (isPlayer1Turn) {
ticTacLbl.setForeground(Color.red);
ticTacLbl.setText("X");
isUsed = true;
} else {
ticTacLbl.setForeground(Color.blue);
ticTacLbl.setText("O");
isUsed = true;
}
}
else{
}
}
public void mouseReleased(MouseEvent e){
}
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
public static void main(String[] args){
JOptionPane.showMessageDialog(null, new TicTacToePanel());
}
}
The GUI constructor:
public TicTacToeGUI(int gameMode){
if(gameMode == 0){
amountOfPanels = 9;
TicTacToePanel[] panelArr = new TicTacToePanel[amountOfPanels];
add(gamePanel, new GridLayout(3, 3));
setPreferredSize(new Dimension(100, 100));
for(int i = 0; i < amountOfPanels; i++){
panelArr[i] = new TicTacToePanel();
gamePanel.add(panelArr[i]);
}
}
else if(gameMode == 1){
amountOfPanels = 225;
TicTacToePanel[] panelArr = new TicTacToePanel[amountOfPanels];
add(gamePanel, new GridLayout(15, 15));
setPreferredSize(new Dimension(500, 500));
for(int i = 0; i < amountOfPanels; i++){
panelArr[i] = new TicTacToePanel();
gamePanel.add(panelArr[i]);
}
}
}
public static void main(String[] args){
JOptionPane.showMessageDialog(null, new TicTacToeGUI(0));
}
}
When you add/remove components at runtime, always call revalidate() afterwards. revalidate() makes the component refresh/relayout.
So just call revalidate() after you add the label and it will work.
If you're goal is to create a Tic Tac Toe game, then you may wish to re-think your current strategy of adding components to the GUI on the fly. Much better would be to create a grid of components, say of JLabel, and place them on the JPanel at program start up. This way you can change the pressed JLabel's text and color, and even its Icon if you want to be fancy during program run without having to add or remove components. For example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
#SuppressWarnings("serial")
public class TicTacToePanel extends JPanel {
private static final int ROWS = 3;
private static final int MY_C = 240;
private static final Color BG = new Color(MY_C, MY_C, MY_C);
private static final int PTS = 60;
private static final Font FONT = new Font(Font.SANS_SERIF, Font.BOLD, PTS);
public static final Color X_COLOR = Color.BLUE;
public static final Color O_COLOR = Color.RED;
private JLabel[][] labels = new JLabel[ROWS][ROWS];
private boolean xTurn = true;
public TicTacToePanel() {
setLayout(new GridLayout(ROWS, ROWS, 2, 2));
setBackground(Color.black);
MyMouse myMouse = new MyMouse();
for (int row = 0; row < labels.length; row++) {
for (int col = 0; col < labels[row].length; col++) {
JLabel label = new JLabel(" ", SwingConstants.CENTER);
label.setOpaque(true);
label.setBackground(BG);
label.setFont(FONT);
add(label);
label.addMouseListener(myMouse);
}
}
}
private class MyMouse extends MouseAdapter {
#Override // override mousePressed not mouseClicked
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
String text = label.getText().trim();
if (!text.isEmpty()) {
return;
}
if (xTurn) {
label.setForeground(X_COLOR);
label.setText("X");
} else {
label.setForeground(O_COLOR);
label.setText("O");
}
// information to help check for win
int chosenX = -1;
int chosenY = -1;
for (int x = 0; x < labels.length; x++) {
for (int y = 0; y < labels[x].length; y++) {
if (labels[x][y] == label) {
chosenX = x;
chosenY = y;
}
}
}
// TODO: check for win here
xTurn = !xTurn;
}
}
private static void createAndShowGui() {
TicTacToePanel mainPanel = new TicTacToePanel();
JFrame frame = new JFrame("Tic Tac Toe");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Game of Life in Java, Overpopulation but can't figure out why

This is homework. I included relevant code at the bottom.
Problem:
In an attempted to allow the user to resize the grid, the grid is now being drawn severely overpopuated.
Screen Shots:
"Overpopulation" -
http://i.imgur.com/zshAC6n.png
"Desired Population" -
http://i.imgur.com/5Rf6P42.png
Background:
It's a version of Conway's Game of Life. In class we completed 3 classes: LifeState which handles the game logic, LifePanel which is a JPanel that contains the game, and a driver that created a JFrame and added the LifePanel. The assignment was to develop it into a full GUI application with various requirements. My solution was to extend JFrame and do most of my work in that class.
Initializing the LifePanel outside of the actionlistener yields normal population, but intializing the LifePanel in the actionlistener "overpopulates" the grid.
Question: Why is the overpopulation occurring?
LifePanel class
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class LifePanel extends JPanel implements MouseListener
{
private int row;
private int col;
private int scale;
private LifeState life;
boolean state;
boolean wrap;
int delay;
Timer timer;
public LifePanel(int r, int c, int s, int d)
{
row = r;
col = c;
scale = s;
delay = d;
life = new LifeState(row,col);
Random rnd = new Random();
for(int i=0;i<row;i++)
for(int j=0;j<col;j++)
life.setCell(i,j,rnd.nextBoolean());
timer = new Timer(delay, new UpdateListener());
setPreferredSize( new Dimension(scale*row, scale*col));
addMouseListener(this);
timer.start();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i=0;i<row;i++)
for(int j=0;j<col;j++)
if(life.getCell(i,j))
g.fillRect(scale*i,scale*j,scale,scale);
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getCol() {
return col;
}
public void setCol(int col) {
this.col = col;
}
public int getScale() {
return scale;
}
public void setScale(int scale) {
this.scale = scale;
}
public int getDelay() {
return delay;
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
public void pauseGame(){
timer.stop();
}
public void playGame(){
timer.restart();
}
public void setInitState(boolean set){
state = set;
if(state){
timer.stop();
}
}
public void setWrap(boolean set){
wrap = set;
if(wrap){
//implement allow wrap
}
}
#Override
public void mouseClicked(MouseEvent e) {
if(state){
int x=e.getX();
int y=e.getY();
boolean isFilled;
isFilled = life.getCell(x,y);
//Test pop-up
JOptionPane.showMessageDialog(this, x+","+y+"\n"+life.getCell(x,y));
if(isFilled){
life.setCell(x,y,false);
}else{
life.setCell(x,y,true);
}
repaint();
}
}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
private class UpdateListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
life.iterate();
repaint();
}
}
}
LifeFrame class
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class LifeFrame extends JFrame implements ActionListener{
JMenuBar menuBar;
JMenu mainMenu, helpMenu;
JMenuItem restartItem, quitItem, helpItem;
JButton stopButton, playButton, pauseButton, startButton;
CardLayout cardLayout = new MyCardLayout();
CardLayout cardLayout2 = new MyCardLayout();
SetupPanel setupPanel; //panel for input
LifePanel gamePanel; //game panel
JPanel controls = new JPanel(); //controls for game
JPanel controls2 = new JPanel(); //controls for input panel
JPanel cardPanel = new JPanel(cardLayout);
JPanel cardPanel2 = new JPanel(cardLayout2);
int gridRow=480;
int gridCol=480;
int scale=1;
int delay=2;
boolean setState = false;
boolean setWrap = false;
public LifeFrame() {
setTitle("Game of Life");
setLayout(new BorderLayout());
//Add the Panels
setupPanel = new SetupPanel();
gamePanel = new LifePanel(gridRow,gridCol,scale,delay);
cardPanel.add(setupPanel, "1");
cardPanel.add(gamePanel, "2");
add(cardPanel, BorderLayout.NORTH);
cardPanel2.add(controls2, "1");
cardPanel2.add(controls, "2");
add(cardPanel2, BorderLayout.SOUTH);
//init menu
menuBar = new JMenuBar();
//button listener setup
stopButton = new JButton("Stop");
pauseButton = new JButton("Pause");
playButton = new JButton("Play");
startButton = new JButton("Start");
stopButton.addActionListener(this);
pauseButton.addActionListener(this);
playButton.addActionListener(this);
startButton.addActionListener(this);
//menu listener setup
restartItem = new JMenuItem("Restart", KeyEvent.VK_R);
quitItem = new JMenuItem("Quit", KeyEvent.VK_Q);
helpItem = new JMenuItem("Help", KeyEvent.VK_H);
restartItem.addActionListener(this);
quitItem.addActionListener(this);
helpItem.addActionListener(this);
//add buttons
controls.add(stopButton);
controls.add(pauseButton);
controls.add(playButton);
controls2.add(startButton);
//build the menus
mainMenu = new JMenu("Menu");
mainMenu.setMnemonic(KeyEvent.VK_M);
helpMenu = new JMenu("Help");
helpMenu.setMnemonic(KeyEvent.VK_H);
menuBar.add(mainMenu);
menuBar.add(helpMenu);
setJMenuBar(menuBar);
//add JMenuItems
restartItem.getAccessibleContext().setAccessibleDescription("Return to setup screen");
mainMenu.add(restartItem);
mainMenu.add(quitItem);
helpMenu.add(helpItem);
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
pack();
setLocationRelativeTo(null);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent e) {
try{
gridRow = setupPanel.getRowSize();
gridCol = setupPanel.getColSize();
scale = setupPanel.getScale();
delay = setupPanel.getDelay();
setWrap = setupPanel.getSetWrap();
setState = setupPanel.getSetState();
}catch (NumberFormatException n){
JOptionPane.showMessageDialog(LifeFrame.this, "Make sure the fields contain only digits and are completed!");
return;
}
if(e.getSource() == pauseButton){
gamePanel.pauseGame();
}else if(e.getSource() == playButton){
gamePanel.playGame();
}else if(e.getSource() == quitItem){
System.exit(0);
}else if(e.getSource() == restartItem || e.getSource() == stopButton){
cardLayout.show(cardPanel, "1");
cardLayout2.show(cardPanel2, "1");
pack();
setLocationRelativeTo(null);
}else if(e.getSource() == helpItem){
String helpText = "Help\nPlease make sure every field is completed and contains only digits\nCurrent Stats:\nGrid Size: "+gamePanel.getRow()+" by "+gamePanel.getCol()+"\nScale: "+ gamePanel.getScale() +"\nDelay: "+gamePanel.getDelay()+"\nManual Initial State: "+setState+"\nEnable Wrapping: "+setWrap;
JOptionPane.showMessageDialog(LifeFrame.this, helpText);
}else if(e.getSource() == startButton){
gamePanel = new LifePanel(gridRow,gridCol,scale,delay);
cardPanel.add(gamePanel, "2");
/*
* Alternate solution, throws array index out of bounds due to array usage in the LifePanel, but properly
* populates the grid.
*
gamePanel.setRow(gridRow);
gamePanel.setCol(gridCol);
gamePanel.setScale(scale);
gamePanel.setDelay(delay);
*/
if(setWrap){
gamePanel.setWrap(true);
gamePanel.playGame();
}else if(setState){
gamePanel.setInitState(true);
}else{
gamePanel.setWrap(false);
gamePanel.setInitState(false);
gamePanel.playGame();
}
gamePanel.repaint();
cardLayout.show(cardPanel, "2");
cardLayout2.show(cardPanel2, "2");
pack();
setLocationRelativeTo(null);
}
}
public static class MyCardLayout extends CardLayout {
#Override
public Dimension preferredLayoutSize(Container parent) {
Component current = findCurrentComponent(parent);
if (current != null) {
Insets insets = parent.getInsets();
Dimension pref = current.getPreferredSize();
pref.width += insets.left + insets.right;
pref.height += insets.top + insets.bottom;
return pref;
}
return super.preferredLayoutSize(parent);
}
public Component findCurrentComponent(Container parent) {
for (Component comp : parent.getComponents()) {
if (comp.isVisible()) {
return comp;
}
}
return null;
}
}
}
Thanks for reading all this, and in advance for any help/advice you offer.
EDIT: Added screen shots and refined question.
Based on how you initialize LifePanel
Random rnd = new Random();
for(int i=0;i<row;i++)
for(int j=0;j<col;j++)
life.setCell(i,j,rnd.nextBoolean());
what you call "overpopulation" is the expected state. The above code will set about 1/2 of the cells to "alive" (or "occupied"), which is what your "overpopulated" state looks like.
The "desired population" screenshot contains many "life" artifacts such as "beehives", "gliders", "traffic lights", etc, and was either manually constructed or is the result of running several iterations on an initially 50% random population. With a 50% occupied population the first generation will result in wholesale clearing ("death") of many, many cells due to the proximity rules.
Most crucially, consider that, when starting up, your program does not paint the initial configuration. At least one iteration occurs before the first repaint() call.
I don't think your code is broken at all, just your expectation for what the initial population looks like.

Swing Timers and Animations in JPanel

Im trying to animate 2 boxes to go from the top right to the bottom left of a JPanel. For the animation I'm using a Swing Timer and SwingUtilities.invokeLater(). The problem is that when I click the start button. It only animates and moves the blue box, but not the red one.
Heres the code:
//import neccessary stuff
public class Example extends JFrame {
public static void main(String[] args) {
Example e = new Example();
e.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
e.setSize(600, 565);
e.setVisible(true);
}</code>
private JButton startButton = new JButton("Start");
private JPanel theTable = new table();
public Example() {
add(startButton, BorderLayout.SOUTH);
add(theTable, BorderLayout.CENTER);
Handler handler = new Handler();
startButton.addActionListener(handler);
}
public ArrayList<Integer> xPos, yPos;
final int START_POSITION_X = 470;
final int START_POSITION_Y = 10;
final int[] END_POSITION_X = {70, 87};
final int END_POSITION_Y = 160;
private class table extends JPanel {
public table() {
xPos = new ArrayList<Integer>();
yPos = new ArrayList<Integer>();
xPos.add(START_POSITION_X); //default position for box1
yPos.add(START_POSITION_Y);
xPos.add(START_POSITION_X); //default position for box2
yPos.add(START_POSITION_Y);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(new Color(-16756217));
g.setColor(Color.RED);
g.fillRect(xPos.get(1), yPos.get(1), 89, 129);
g.setColor(Color.BLUE);
g.fillRect(xPos.get(0), yPos.get(0), 89, 129);
if (isAnimating) {
animator.start();
} else {
animator.stop();
isAnimating = false;
}
}
}
private class Handler implements ActionListener {
public void actionPerformed(ActionEvent e) {
Runnable r1 = new Runnable() {
#Override
public void run() {
animateCard(0, END_POSITION_X[0], END_POSITION_Y);
}
};
SwingUtilities.invokeLater(r1);
animateCard(1, END_POSITION_X[1], END_POSITION_Y);
}
}
public void animateCard(int card, int xDest, int yDest) {
cardID = card;
xDestination = xDest;
yDestination = yDest;
totalXDistance = Math.abs(xDestination - START_POSITION_X);
totalYDistance = Math.abs(yDestination - START_POSITION_Y);
animator.start();
}
int cardID;
int xDestination, yDestination, totalXDistance, totalYDistance;
boolean isAnimating = false;
Timer animator = new Timer(15, new ActionListener() {
int startVel = 20;
public void actionPerformed(ActionEvent e) {
double xRelDistance, xAbsDistance, xVel;
int xRealVel;
xAbsDistance = xDestination - xPos.get(cardID);
xRelDistance = xAbsDistance / totalXDistance;
xVel = startVel * xRelDistance;
double yRelDistance, yAbsDistance, yVel;
yAbsDistance = yDestination - yPos.get(cardID);
yRelDistance = yAbsDistance / totalYDistance;
yVel = startVel * yRelDistance;
if (xVel > 0) {
xRealVel = (int) java.lang.Math.ceil(xVel);
} else {
xRealVel = (int) java.lang.Math.floor(xVel);
}
xPos.set(cardID, xPos.get(cardID) + xRealVel);
int yRealVel;
if (xVel > 0) {
yRealVel = (int) java.lang.Math.ceil(yVel);
yPos.set(cardID, yPos.get(cardID) + yRealVel);
} else {
yRealVel = (int) java.lang.Math.floor(yVel);
}
yPos.set(cardID, yPos.get(cardID) + yRealVel);
if ((xPos.get(cardID) == xDestination) && (yPos.get(cardID) == yDestination)) {
isAnimating = false;
} else {
isAnimating = true;
}
repaint();
}
});
}
So all of those variable declared at the class level are shared... Your first call to animateCard will set them and then your second call to animateCard is going to completely overwrite the previous values. You need to change them from class variables to parameters of the animation.
Create a new class AnimationTask that implements ActionListener and save the variables in that class.
E.g.,
class AnimationTask implements ActionListener {
private int cardID;
private int xDest;
private int yDest;
private int totalXDistance;
private int totalYDistance;
public AnimationTask(int cardID, int xDest, int yDest) {
this.cardID = cardID;
this.xDest = xDest;
this.yDest = yDest;
this.totalXDistance = Math.abs(xDestination - START_POSITION_X);
this.totalYDistance = Math.abs(yDestination - START_POSITION_Y);
}
public void actionPerformed(ActionEvent e) {
// do your animation logic...
}
}
and use that custom class in your "animator"
E.g.,
Timer animator = new Timer(15, new AnimationTask(cardId, xDest, yDest);
put
animateCard(1, END_POSITION_X[1], END_POSITION_Y);
inside your run method:
public void run() {
animateCard(0, END_POSITION_X[0], END_POSITION_Y);
}

Categories