I have having issues with getcomponentat in layered situation. I have researched a lot and found the following thread which is actually what I need but it does not work for me. I downloaded the code in the thread and it works but when I implemented it in my project it does not. I may be doing something really stupid mistake which I can't put my finger on.
I have a JFrame which has a base Panel. I have added a gridPanel (which extends JPanel on it and implemented mouselistner. On the grid panel I am adding cells (which extends JPanel and also implements mouselistener). when I click on any cell I want to know the position of that cell in the grid but everything comes back as 0,0.
GridLayout + Mouse Listener
so here it goes.
MAINCLASS
mainFrame = new JFrame("Connect-4");
basePanel = new JPanel();
gridPanel = new Grid(); //Grid extends JPanel
//GRIDCLASS
public class Grid extends JPanel implements MouseListener {
public Grid(){
// setPreferredSize(new Dimension(600,700));;
setLayout(new GridLayout(6, 7));
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
Cell tempCell = new Cell(i,j); //Cell Exntends JPANEL
tempCell.addMouseListener(this);
gridUI[i][j] = tempCell;
gridTrack[i][j] = 0;
add(tempCell);
int index = i*6 + j;
cellArray.add(tempCell);
}
}
addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("Grid Click");
Cell clickedCell;
Boolean filled = false;
Point mousePoint;
mousePoint = e.getPoint();
System.out.println(mousePoint.x + "||" + mousePoint.y);
clickedCell = (Cell)getComponentAt(mousePoint);
// Point mousePoint = MouseInfo.
int cellIndex;
cellIndex = Integer.parseInt(clickedCell.getName());
int cellX = cellIndex / 7;
int cellY = cellIndex % 7;
}
public class Cell extends JPanel implements MouseListener{
private String status;
private Color curColor;
private Boolean occupied;
public static Boolean gameOver = false;
public static int player;
public static boolean randPlayer = false;
private Color player1 = Color.BLUE;
private Color player2 = Color.RED;
private static int[][] gridTrack = new int[6][7];
public int row,column;
public static int cellSize = 80;
public Cell(int row_in, int column_in){
setPreferredSize(new Dimension(cellSize,cellSize));
setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
setBackground(Color.GRAY);
player = 0;
this.setName(Integer.toString(row_in*6+column_in));
curColor = Color.WHITE;
addMouseListener(this);
occupied = false;
player = 1;
row = row_in;
column = column_in;
gridTrack[row][column] = 0;
}
When the Listener is fired, the point of the event relative to the Component that fired the event. Adding MouseListeners to a Cell results in the coordinates being relative to that Cell - as a result using getComponentAt on the parent Component with these coordinates will always return the Cell at 0,0 as the coordinates of the Point of the event will never be greater than the width/height of a Cell.
Consider using a single listener to handle the behavior, using the appropriate technique to get the Component that fired the event:
Add a listener to the parent JPanel- the coordinates of the event are relative to the parent. Thus using getComponentAt will return the Component where the MouseEvent occurred
Add a listener to each Cell, and get the Cell that fired the event using Cell cell = (Cell)e.getSource().
Related
I am in the middle of doing a project and I need some help with make the circles that I am drawing to respond to my timer. I want to make them go slower and faster using my slider that I put in. Attached is the code below. (I know that my code is going to be a jumbled mess, I am using some source code that our professor provide to get the slider on my screen. I am just having a hard time getting the slider/timer talk to my circles.)
import java.util.ArrayList;
import java.util.Random;
import java.awt.*;
import java.awt.event.*;
import javax.swing. *;
import javax.swing.event.*;
import SpeedControlPanel.SlideListener;
public class DotsPanel extends JPanel
{
private final int SIZE = 6; // radius of each dot
private static final long serialVersionUID = 1L;
private ArrayList<Point> pointList;
private Timer timer;
private JLabel lLabel;
private JSlider sSlider;
private JPanel pPanel;
private Circle bouncingBall;
int sSliderHt;
private int moveX, moveY;
AnimationListener animationList;
//-----------------------------------------------------------------
// Constructor: Sets up this panel to listen for mouse events.
//-----------------------------------------------------------------
public DotsPanel()
{
timer = new Timer(30, new AnimationListener());
this.setLayout(new BorderLayout());
bouncingBall = new Circle(SIZE);
Random rand = new Random();
pointList = new ArrayList<Point>();
addMouseListener (new DotsListener());
addMouseMotionListener( new DotsListener());
setBackground(Color.black);
setPreferredSize(new Dimension(500, 300));
lLabel= new JLabel("Timer Delay");
lLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
sSlider = new JSlider(JSlider.HORIZONTAL, 0, 200, 30);
sSlider.setMajorTickSpacing(40);
sSlider.setMinorTickSpacing(10);
sSlider.setPaintTicks(true);
sSlider.setPaintLabels(true);
sSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
sSlider.addChangeListener(new SlideListener());
pPanel= new JPanel();
pPanel.add(lLabel);
pPanel.add(sSlider);
add(pPanel, BorderLayout.SOUTH);
animationList = new AnimationListener();
animationList.timer.start();
/*int[] xArray = new int[1000];
int[] yArray = new int[1000];
for(int i = 0; i < xArray.length; i++)
{
xArray[i] = rand.nextInt(10) + 1;
yArray[i] = rand.nextInt(10) + 1;
}*/
}
//-----------------------------------------------------------------
// Draws all of the dots stored in the list.
//-----------------------------------------------------------------
public void paintComponent(Graphics page)
{
Random rand = new Random();
int R = rand.nextInt(256);
int G = rand.nextInt(256);
int B = rand.nextInt(256);
super.paintComponent(page);
page.setColor(new Color(R%255, (G*3)%255, (B+128)%255));
for (Point spot : pointList)
//page.fillOval(spot.x-SIZE, spot.y-SIZE, SIZE*2, SIZE*2);
page.fillOval(spot.x, spot.y, SIZE*2, SIZE*2);
page.drawString("Count: " + pointList.size(), 5, 15);
}
private class AnimationListener implements ActionListener {
int xRand[];
int yRand[];
Random rand;
public AnimationListener() {
rand = new Random();
xRand = new int[1000];
yRand = new int[1000];
//Filling random values between 0 to 10
for (int i = 0; i < 1000; i++) {
xRand[i] = rand.nextInt(10) + 1;
yRand[i] = rand.nextInt(10) + 1;
}
}
private Timer timer = new Timer(50, this);
#Override
public void actionPerformed(ActionEvent e) {
//Put here for the bounce off of the wall
Rectangle window = getBounds();
for (int i = 0; i < pointList.size(); i++) {
Point spot = pointList.get(i);
spot.x += xRand[i];
spot.y += yRand[i];
if (spot.x <= 0) {
xRand[i] = Math.abs(xRand[i]);
} else if (spot.x >= window.width - (SIZE*2)) {
xRand[i] = -Math.abs(xRand[i]);
}
if (spot.y <= 0) {
yRand[i] = Math.abs(yRand[i]);
} else if (spot.y >= window.height - (SIZE*2)) {
yRand[i] = -Math.abs(yRand[i]);
}
}
bouncingBall.move(moveX, moveY);
// change direction if ball hits a side
int x = bouncingBall.getX();
int y = bouncingBall.getY();
if (x < 0 || x >= WIDTH - SIZE)
{
moveX = moveX * -1;
}
if (y <= 0 || y >= HEIGHT - SIZE)
{
moveY = moveY * -1;
}
sSliderHt =sSlider.getSize().height;
repaint();
repaint();
}
}
//*****************************************************************
// Represents the listener for mouse events.
//*****************************************************************
private class DotsListener implements MouseListener, MouseMotionListener
{
//--------------------------------------------------------------
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
//--------------------------------------------------------------
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
public void mouseDragged(MouseEvent event) {
pointList.add(event.getPoint());
repaint();
}
//--------------------------------------------------------------
// Provide empty definitions for unused event methods.
//--------------------------------------------------------------
public void mouseClicked(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
}
private class SlideListener implements ChangeListener
{
// ------------------------------------------------
// Called when the state of the slider has changed;
// resets the delay on the timer.
// ------------------------------------------------
public void stateChanged (ChangeEvent event)
{
//int sSliderHt =sSlider.getSize().height;
timer.setDelay(sSlider.getValue());
}
}
}
I want to make them go slower and faster using my slider that I put in.
So it looks to me like you already have logic to make each circle move a random distance each time the Timer fires.
So, just use the slider to change the interval at which the timer fires. Then each time the Timer fires you set the new location of the circles based on your random distance.
Other issues:
The paintComponent(...) method is for painting only. It should NOT set properties of the class. For example you should not be generating random values in the method. You can't control when the paintComponent() is invoked and you don't want to randomly change the properties of the objects you are painting.
Don't use an Array to hold random values. First of all you don't know how many object will be added so you don't want to set a size limit. Instead use an ArrayList.
Create a custom object to contain all the properties you want to control. So you would have the size/location/color etc for each circle you want to paint in a custom object.
See: get width and height of JPanel outside of the class for an example using the above suggestions.
I am trying to recreate the Game of Life in Java using JFrame. I have already completed most of the program but this one thing is bugging me. How do I make a bunch of fields(panels) which are clickable, so that the user can input their own pattern, instead of the computer randomly generating the pattern each time?
You could use a GridLayout layout manager to put all the JPanels in a grid, and for each JPanel, add an instance of the MouseAdapter class with addMouseListener() to listen for mouse clicks to flip their state. The instance of MouseAdapter would override mouseClicked() and within that function, flip the state of the JPanel.
This is just to make a complete example, but here would be the creation of the frame and setting its layout manager:
public static void main(String[] args) {
JFrame frame = new JFrame();
int width = 200, height = 200;
frame.setSize(width, height);
int rows = width/10, cols = height/10;
frame.setLayout(new GridLayout(rows, cols));
// add all the cells
for(int j = 0; j < cols; j++) {
for(int i = 0; i < rows; i++) {
frame.add(new Cell(i, j));
}
}
frame.setVisible(true);
}
Then for each cell, we have instances of this class:
class Cell extends JPanel {
int row, col;
public static final int STATE_DEAD = 0;
public static final int STATE_ALIVE = 1;
int state = STATE_DEAD;
public Cell(int row, int col) {
this.row = row;
this.col = col;
// MouseAdapter tells a component how it should react to mouse events
MouseAdapter mouseAdapter = new MouseAdapter() {
// using mouseReleased because moving the mouse slightly
// while clicking will register as a drag instead of a click
#Override
public void mouseReleased(MouseEvent e) {
flip();
repaint(); // redraw the JPanel to reflect new state
}
};
// assign that behavior to this JPanel for mouse button events
addMouseListener(mouseAdapter);
}
// Override this method to change drawing behavior to reflect state
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// fill the cell with black if it is dead
if(state == STATE_DEAD) {
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public void flip() {
if(state == STATE_DEAD) {
state = STATE_ALIVE;
} else {
state = STATE_DEAD;
}
}
}
Alternatively, you could override the paintComponent() method of one JPanel, and do the above but use addMouseMotionListener() as well, that way your one panel can track which drawn grid cell the mouse is in, and you can control how they are drawn.
What the code should do is draw X randomized shapes with the click of a button. What I have so far is a subclass that creates the random shape to be placed inside the JPanels, but the issue is that the same shape is used in all panels, I need each shape to be randomized.
The subclass looks like this:
public class Shapes extends JPanel
{
Random rand = new Random();
private int x = 5;
private int y = 5;
private int diameter = 200;
private Color outline;
private Color internal;
private Color internal2;
private Color internal3;
public Shapes() {
this(new Random());
}
//randomizes colors
public Shapes(Random rand) {
outline = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
internal = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
internal2 = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
internal3 = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
}
{
super.paintComponent(g);
g.setColor(outline);
g.drawOval(x, y, diameter, diameter);
g.setColor(internal);
g.fillOval(x+2, y+2, diameter-4, diameter-4);
g.setColor(internal2);
g.fillOval(x+25, y+66, diameter/3, diameter/3);
g.fillOval(x+125, y+66, diameter/3, diameter/3);
g.setColor(internal3);
g.fillArc(x+55, y+105, diameter/3, diameter/3, 180, 180);
}
}
While the main class looks like so [currently set up to make 6 images]:
public class ShapeGrid extends JFrame implements ActionListener
{
private JButton button;
int i = 2;
int j = 3;
JPanel[][] panelHolder = new JPanel[i][j];
private Shapes shapes;
public static void main(String[] args)
{
ShapeGrid myGrid = new ShapeGrid();
myGrid.setSize(800, 800);
myGrid.createGUI();
myGrid.setVisible(true);
}
public ShapeGrid()
{
setLayout(new GridLayout(i,j, 5, 5));
for(int m = 0; m < i; m++) {
for(int n = 0; n < j; n++) {
panelHolder[m][n] = new JPanel();
add(panelHolder[m][n]);
}
}
}
private void createGUI()
{
shape = new Shapes();
setDefaultCloseOperation(EXIT_ON_CLOSE);
button = new JButton("Press me");
add(button);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent ae)
{
if (ae.getSource() == button) {
for(int m = 0; m < i; m++) {
for(int n = 0; n < j; n++) {
shape.paintComponent(panelHolder[m][n].getGraphics());
}
}
}
}
}
shape.paintComponent(panelHolder[m][n].getGraphics());
You should NEVER invoke paintCopmonent() directly and you should never use getGraphics(). Swing will determine when a component needs to be painted and Swing will pass the Graphics object to the paintComponent() method.
Once you follow the advice below there is no need for the "Press Me" button (or the above) because the Shapes will be created when the when the ShapeGrid class is created. And the Shapes will automatically be painted when the frame is made visible.
is that the same shape is used in all panels,
You only ever create a single Shape object. You need to create a Shape object for each grid. So in the constructor of the ShapeGrid you need to create one Shape object for each grid.
I suggest you should pass in the rows/columns you want for your grid (instead of hardcoding i/j in your class). So your ShapeGrid constructor code might be something like:
public ShapeGrid(int rows, columns)
{
setLayout(new GridLayout(rows, columns, 5, 5));
int drawShapes = rows * columns;
for(int i = 0; i < drawShapes; i++) {
add( new Shape() );
}
}
That's it. There is no need for the panel holder. You will now have uniques Smileys added to the frame.
Then in your main() method you do something like:
ShapeGrid myGrid = new ShapeGrid(2, 3);
I have to display images using the fly weight pattern, I can't get the images to print to screen, here's the code that demonstrates the problem.
public void draw(Graphics g, int tx, int ty, String name) {
grem.paintIcon(null, g, tx, ty);
g.drawString(name, tx, ty + H + 15 );
ImageIcon grem = new ImageIcon("../images/grem.png");
}
/// next class that calls the above class
public void paint(Graphics g) {
Folder folderIcon;
String name;
int j = 0; //count number in row
int row = Top; //start in upper left
int x = Left;
//go through all the names and folders
for (int i = 0; i< names.size(); i++) {
name = (String)names.elementAt(i);
if (name.equals(selectedName))
folderIcon = fact.getFolder(true);
else
folderIcon = fact.getFolder(false);
//have that folder draw itself at this spot
folderIcon.paint(g);
x = x + HSpace; //change to next posn
j++;
if (j >= HCount) { //reset for next row
j = 0;
row += VSpace;
x = Left;
}
}
}
Don't override paint(). Custom painting is done by overriding paintComponent().
Don't do I/O in a painting method. You can't control when Swing will repaint a component so you don't want to read images in the painting method. The images should be read in the constructor of your class.
Override the getPreferredSize(...) method to return the size of your component, otherwise the size of the component will be (0, 0) so there may be nothing to paint (depending on the layout manager being used.
If you need more help the post a proper SSCCE that demonstrates the problem because we don't know the context of how your code is being used and don't have time to spend guessing what you may or may not be doing.
Read the section from the Swing tutorial on Custom Painting for more information. Also, instead of doing custom painting you could also use a JList to display the Icon in a grid pattern. Check out the table of contents for the tutorial link to find the section on How to Use Lists for more information.
Maybe the null is the problem : it should be something like this if im not mistaken
class MyComponent extends JComponent {
public void paint(Graphics g) {
ImageIcon icon = new ImageIcon("a.png");
int x = 0;
int y = 100;
icon.paintIcon(this, g, x, y);
}
public class Gremlin extends JFrame implements ActionListener {
String names[] = {"Andy","Bill","Bob","Dan","Eugene","Frank","Gary","Harry","Ian","Jack",
"Killlian","Liam","Mark","Nial","Obi","Phil","Richard","Stephan","Terry","Viny",}; // 20 names
public Icon img = new ImageIcon("grem1.jpg");
public JLabel grem = new JLabel(img);
JLabel bigLabel = new JLabel();
JLabel grem2 = new JLabel("New Gremlin");
public JPanel panel2 = new JPanel();
JPanel panel = new JPanel();
public Gremlin() {
JButton button = new JButton("Add Gremlin");
this.add(panel);
panel.setLayout(new GridLayout(9,6));
panel.add(panel2);
panel2.add(button);
for(int i = 0; i<20; i++){
bigLabel.add(grem = new JLabel(names[i]), panel.add(grem = new JLabel(img)));
panel.add(bigLabel);
}
button.addActionListener(this);
setSize(550,600);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
Gremlin frame = new Gremlin();
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() != null ){
System.out.println("add a Gremlin");
panel.add(grem = new JLabel("NEW GREMLIN"), panel.add(grem = new JLabel(img)));
revalidate();
}
}
}
I believe I have got most of the MVC part down for this program - Game Of Life. However I can't get the MouseListener to work properly. How can I change this match the MVC design pattern?
View
package lifepreparation;
import java.awt.Color;
import java.awt.*;
import java.awt.event.*;
import java.util.Hashtable;
import javax.swing.*;
import java.awt.event.MouseListener;
public class LifeView extends JFrame {
//Label used to house cells
private LifeModel[][] cellHouse;
private LifeModel model;
//Timer used to fire the next generation
private Timer timer;
//Generation counter - used to count the number of generations
private int generationCount = 0;
private JLabel generationLabel = new JLabel("Generation: 0");
//Declare action buttons
private JButton startB = new JButton("Start");
private JButton pauseB = new JButton("Pause");
private JButton clearB = new JButton("Clear");
//Slider to adjust the time interval between generations
private static final int minSpeed = 0;
private static final int maxSpeed = 1000;
private static final int speedMajorTicks = (maxSpeed-minSpeed)/5;
private static final int speedMinorTicks = (maxSpeed-minSpeed)/20;
JSlider generationS = new JSlider(minSpeed,maxSpeed);
//Identifies game status: false=pause, true=running
private boolean runStatus = false;
//Panel for the city
private JPanel panel;
public LifeView(int boardRow, int boardCol, LifeModel model) {
super("Game Of Life");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//**START Create labels to house cells, +2 more
cellHouse = new LifeModel[boardRow+2][boardCol+2];
for(int r = 0; r < boardRow+2; r++) {
for(int c = 0; c < boardCol+2; c++) {
cellHouse[r][c] = new LifeModel();
}
}
//--END Create Labels
//Panel to hold cell houses
panel = new JPanel(new GridLayout(boardRow, boardCol, 1, 1));
panel.setBackground(Color.BLACK);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
//Add cellHouses to the panel
for(int r = 1; r < boardRow+1; r++) {
for(int c = 1; c < boardCol+1; c++) {
panel.add(cellHouse[r][c]);
cellHouse[r][c].addNeighbor(cellHouse[r-1][c]); //Add to TOP ^
cellHouse[r][c].addNeighbor(cellHouse[r+1][c]); //Add to BOTTOM _
cellHouse[r][c].addNeighbor(cellHouse[r][c-1]); //Add to left <-
cellHouse[r][c].addNeighbor(cellHouse[r][c+1]); //Add to right ->
cellHouse[r][c].addNeighbor(cellHouse[r-1][c-1]); //Add to top left ^<-
cellHouse[r][c].addNeighbor(cellHouse[r-1][c+1]); //Add to top right ^->
cellHouse[r][c].addNeighbor(cellHouse[r+1][c-1]); //Add to bottom left _<-
cellHouse[r][c].addNeighbor(cellHouse[r+1][c+1]); //Add to bottom right _->
}
}
//Panel with cellHouses added to the container
add(panel, BorderLayout.CENTER);
//South Panel to hold buttons and widgets with extra features
JPanel panelBottom = new JPanel();
//buttonPanel to hold start, pause, clear features
JPanel buttonPanel = new JPanel();
buttonPanel.add(clearB);
pauseB.setEnabled(false);
buttonPanel.add(pauseB);
buttonPanel.add(startB);
//speedPanel to hold slider to adjust the time interval
JPanel speedPanel = new JPanel();
JLabel speedText = new JLabel("Set Speed:");
generationS.setMajorTickSpacing(speedMajorTicks);
generationS.setMinorTickSpacing(speedMinorTicks);
generationS.setPaintTicks(true);
// the labels for the Slider
Hashtable<Integer, JLabel> speedLabel = new Hashtable<Integer, JLabel>();
for(int i = 0; i <= maxSpeed; i++) {
speedLabel.put( new Integer( i * speedMajorTicks ), new JLabel("" + i) );
}
generationS.setLabelTable(speedLabel);
generationS.setPaintLabels(true);
generationLabel.setHorizontalAlignment(SwingConstants.CENTER);
speedPanel.add(speedText);
speedPanel.add(generationS);
panelBottom.add(buttonPanel);
panelBottom.add(speedPanel);
panelBottom.add(generationLabel);
// add bottom panel to the JFrame
add(panelBottom, BorderLayout.SOUTH);
// put the frame on
setLocation(20, 20);
pack();
setVisible(true);
}
//Action to take dependent on the action referenced by the JButton and Timer
public void startPauseClear(ActionEvent e) {
//Get action reference
Object o = e.getSource();
//Action reference is to clear
if(o == clearB) {
timer.stop(); //Stop the timer
runStatus = false; //Set game as not running
pauseB.setEnabled(false); //Disable the pause button
startB.setEnabled(true); //Enable the start button
//Remove/Clear all cells from the cellHouse
for(int r = 1; r < cellHouse.length -1; r++) {
for(int c = 1; c < cellHouse[r].length -1; c++) {
cellHouse[r][c].clear();
}
}
//Reset the generation count
generationCount = 0;
generationLabel.setText("Generation: 0");
return;
}
//Action reference is to pause
if(o == pauseB) {
timer.stop(); //Stop the timer
runStatus = false; //Set game as not running
pauseB.setEnabled(false); //Disable the pause button
startB.setEnabled(true); //Enable the start button
return;
}
//Action reference is to start
if(o == startB) {
pauseB.setEnabled(true); //Enable the pause button
startB.setEnabled(false); //Disable the start button
runStatus = true; //Set game as running
timer.setDelay(maxSpeed - generationS.getValue()); //Adjust the speed
timer.start();
return;
}
//If the action is set by timer, set speed
timer.setDelay(maxSpeed - generationS.getValue());
//If the game is not running, exit and do nothing more
if(!runStatus) return;
//Update generation count and display
++generationCount;
generationLabel.setText("Generation: " + generationCount);
//Check to see if the cell should be buried
for(int r = 0; r < cellHouse.length; r++) {
for(int c = 0; c < cellHouse[r].length; c++) {
cellHouse[r][c].checkGeneration();
}
}
//Update to the next generation
for(int r = 0; r < cellHouse.length; r++) {
for(int c = 0; c < cellHouse[r].length; c++) {
cellHouse[r][c].updateGeneration();
}
}
}
public void addActionListener(ActionListener e) {
clearB.addActionListener(e);
pauseB.addActionListener(e);
startB.addActionListener(e);
timer = new Timer(maxSpeed - generationS.getValue(), e);
}
}
Controller
package lifepreparation;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class LifeController {
//Run App
LifeModel model;
LifeView view;
//Constructor
public LifeController(LifeModel model, LifeView view) {
this.model = model;
this.view = view;
//Add Listeners
view.addActionListener(new LifeActionListener());
view.addMouseListener(new LifeMouseListener());
}
class LifeActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
view.startPauseClear(e);
}
}
class LifeMouseListener implements MouseListener {
public void mouseClicked(MouseEvent arg0) {
model.unselectCell();
}
//If the mouse is in a cellHouse while being pressed, cell becomes alive
public void mouseEntered(MouseEvent arg0) {
model.selectCells();
}
public void mouseExited(MouseEvent arg0) {
}
//If the mouse is clicked on a cellHouse, cell comes to life
public void mousePressed(MouseEvent arg0) {
model.selectACell();
}
//Set mouse as not being held anymore
public void mouseReleased(MouseEvent arg0) {
model.setMouseHold();
}
}
}
Model
package lifepreparation;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
//LifeModel to handle cell life and death algorithm
public class LifeModel extends JLabel {
//Cell Color: cell[0] = dead cell, cell[1] = live cell.
private static final Color[] color = {Color.LIGHT_GRAY, Color.GREEN};
//Size of cells
private static final int cellSize = 15;
private static final Dimension citySize = new Dimension(cellSize, cellSize);
//checks if the mouse is still pressed or not
private boolean mouseHold = false;
private int currentStatus, newStatus;
private int nbNeighbor;
private LifeModel[] Neighbor = new LifeModel[8];
LifeModel() {
currentStatus = newStatus = 0; //Currently Dead
setOpaque(true); //Show color
setBackground(color[0]); //Light Gray color
this.setPreferredSize(citySize); //Set the dimension of the board
}
//Add a neighbor
void addNeighbor(LifeModel n) {
Neighbor[nbNeighbor++] = n;
}
//Check to see if a cell should live or not
void checkGeneration() {
//Number of neighbors that are alive
int nbAlive = 0;
//Check the status of the neighbors
for(int i = 0; i < nbNeighbor; i++)
nbAlive += Neighbor[i].currentStatus;
//If status of cell is alive
if(currentStatus == 1) {
//Bury cell if it has less than two neighbors
if(nbAlive < 2)
newStatus = 0;
//Bury cell if it has more than three live neighbors
if(nbAlive > 3)
newStatus = 0;
}
else {
//Dead cells with three live neighbors get reborn
if(nbAlive == 3)
newStatus = 1;
}
}
//Switch to next generation
void updateGeneration() {
if(currentStatus != newStatus) { //Adjust color for the new generation
currentStatus = newStatus;
setBackground(color[currentStatus]);
}
}
//Bury all cells in the city
void clear() {
if(currentStatus == 1 || newStatus == 1) {
currentStatus = newStatus = 0;
setBackground(color[currentStatus]);
}
}
public void unselectCell() {
if(currentStatus == 1 || newStatus == 1) {
currentStatus = newStatus = 0;
setBackground(color[currentStatus]);
}
}
//If the mouse is in a cellHouse while being pressed, cell becomes alive
public void selectCells() {
if(mouseHold) {
currentStatus = newStatus = 1;
setBackground(color[1]);
}
}
//If the mouse is clicked on a cellHouse, cell comes to life
public void selectACell() {
mouseHold = true;
currentStatus = newStatus = 1;
setBackground(color[1]);
}
//Set mouse as not being held anymore
public void setMouseHold() {
mouseHold = false;
}
}
Program without MVC implementation
package lifepreparation;
import java.awt.Color;
import java.awt.*;
import java.awt.event.*;
import java.util.Hashtable;
import javax.swing.*;
public class LifeView extends JFrame implements ActionListener {
//Cell Color: cell[0] = dead cell, cell[1] = live cell.
private static final Color[] color = {Color.LIGHT_GRAY, Color.GREEN};
//Size of cells
private static final int cellSize = 15;
private static final Dimension citySize = new Dimension(cellSize, cellSize);
//Label used to house cells
private LifeLabel[][] cellHouse;
//Timer used to fire the next generation
private Timer timer;
//Generation counter - used to count the number of generations
private int generationCount = 0;
private JLabel generationLabel = new JLabel("Generation: 0");
//Declare default buttons
private JButton startB = new JButton("Start");
private JButton pauseB = new JButton("Pause");
private JButton clearB = new JButton("Clear");
//Slider to adjust the time interval between generations
private static final int minSpeed = 0;
private static final int maxSpeed = 1000;
private static final int speedMajorTicks = (maxSpeed-minSpeed)/5;
private static final int speedMinorTicks = (maxSpeed-minSpeed)/20;
JSlider generationS = new JSlider(minSpeed,maxSpeed);
//Identifies game status: false=pause, true=running
private boolean runStatus = false;
// if the mouse is down or not
private boolean mouseHold = false;
public LifeView(int boardRow, int boardCol) {
super("Game Of Life");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//**START Create labels to house cells, +2 more to calculate cells that are out of bounds
cellHouse = new LifeLabel[boardRow+2][boardCol+2];
for(int r = 0; r < boardRow+2; r++) {
for(int c = 0; c < boardCol+2; c++) {
cellHouse[r][c] = new LifeLabel();
}
}
//--END Create Labels
//Panel to hold cell houses
JPanel panel = new JPanel(new GridLayout(boardRow, boardCol, 1, 1));
panel.setBackground(Color.BLACK);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
//Add cellHouses to the panel
for(int r = 1; r < boardRow+1; r++) {
for(int c = 1; c < boardCol+1; c++) {
panel.add(cellHouse[r][c]);
cellHouse[r][c].addNeighbor(cellHouse[r-1][c]); //Add to TOP ^
cellHouse[r][c].addNeighbor(cellHouse[r+1][c]); //Add to BOTTOM _
cellHouse[r][c].addNeighbor(cellHouse[r][c-1]); //Add to left <-
cellHouse[r][c].addNeighbor(cellHouse[r][c+1]); //Add to right ->
cellHouse[r][c].addNeighbor(cellHouse[r-1][c-1]); //Add to top left ^<-
cellHouse[r][c].addNeighbor(cellHouse[r-1][c+1]); //Add to top right ^->
cellHouse[r][c].addNeighbor(cellHouse[r+1][c-1]); //Add to bottom left _<-
cellHouse[r][c].addNeighbor(cellHouse[r+1][c+1]); //Add to bottom right _->
}
}
//Panel with cellHouses added to the container
add(panel, BorderLayout.CENTER);
//South Panel to hold buttons and widgets with extra features
JPanel panelBottom = new JPanel();
//buttonPanel to hold start, pause, clear features
JPanel buttonPanel = new JPanel();
clearB.addActionListener(this);
buttonPanel.add(clearB);
pauseB.addActionListener(this);
pauseB.setEnabled(false);
buttonPanel.add(pauseB);
startB.addActionListener(this);
buttonPanel.add(startB);
//speedPanel to hold slider to adjust the time interval
JPanel speedPanel = new JPanel();
JLabel speedText = new JLabel("Set Speed:");
generationS.setMajorTickSpacing(speedMajorTicks);
generationS.setMinorTickSpacing(speedMinorTicks);
generationS.setPaintTicks(true);
// the labels for the Slider
Hashtable<Integer, JLabel> speedLabel = new Hashtable<Integer, JLabel>();
for(int i = 0; i <= maxSpeed; i++) {
speedLabel.put( new Integer( i * speedMajorTicks ), new JLabel("" + i) );
}
generationS.setLabelTable(speedLabel);
generationS.setPaintLabels(true);
generationLabel.setHorizontalAlignment(SwingConstants.CENTER);
speedPanel.add(speedText);
speedPanel.add(generationS);
panelBottom.add(buttonPanel);
panelBottom.add(speedPanel);
panelBottom.add(generationLabel);
// add bottom panel to the JFrame
add(panelBottom, BorderLayout.SOUTH);
// put the frame on
setLocation(20, 20);
pack();
setVisible(true);
// start the thread that run the cycles of life
timer = new Timer(maxSpeed - generationS.getValue(), this);
}
//Action to take dependent on the action referenced by the JButton and Timer
public void actionPerformed(ActionEvent e) {
//Get action reference
Object o = e.getSource();
//Action reference is to clear
if(o == clearB) {
timer.stop(); //Stop the timer
runStatus = false; //Set game as not running
pauseB.setEnabled(false); //Disable the pause button
startB.setEnabled(true); //Enable the start button
//Remove/Clear all cells from the cellHouse
for(int r = 1; r < cellHouse.length -1; r++) {
for(int c = 1; c < cellHouse[r].length -1; c++) {
cellHouse[r][c].clear();
}
}
//Reset the generation count
generationCount = 0;
generationLabel.setText("Generation: 0");
return;
}
//Action reference is to pause
if(o == pauseB) {
timer.stop(); //Stop the timer
runStatus = false; //Set game as not running
pauseB.setEnabled(false); //Disable the pause button
startB.setEnabled(true); //Enable the start button
return;
}
//Action reference is to start
if(o == startB) {
pauseB.setEnabled(true); //Enable the pause button
startB.setEnabled(false); //Disable the start button
runStatus = true; //Set game as running
timer.setDelay(maxSpeed - generationS.getValue()); //Adjust the speed
timer.start();
return;
}
//If the action is set by timer, set speed
timer.setDelay(maxSpeed - generationS.getValue());
//If the game is not running, exit and do nothing more
if(!runStatus) return;
//Update generation count and display
++generationCount;
generationLabel.setText("Generation: " + generationCount);
//Check to see if the cell should be buried
for(int r = 0; r < cellHouse.length; r++) {
for(int c = 0; c < cellHouse[r].length; c++) {
cellHouse[r][c].checkGeneration();
}
}
//Update to the next generation
for(int r = 0; r < cellHouse.length; r++) {
for(int c = 0; c < cellHouse[r].length; c++) {
cellHouse[r][c].updateGeneration();
}
}
}
//Run App
public static void main(String[] arg) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new LifeView(30, 50);
}
});
}
//LifeModel to handle cell life and death algorithm
class LifeLabel extends JLabel implements MouseListener {
private int currentGen, newGen;
private int nbNeighbor;
private LifeLabel[] Neighbor = new LifeLabel[8];
LifeLabel() {
currentGen = newGen = 0; //Currently Dead
setOpaque(true); //Show color
setBackground(color[0]); //Light Gray color
addMouseListener(this); //Add mouseListener
this.setPreferredSize(citySize); //Set the dimension of the board
}
//Add a neighbor
void addNeighbor(LifeLabel n) {
Neighbor[nbNeighbor++] = n;
}
//Check to see if a cell should live or not
void checkGeneration() {
//Number of neighbors that are alive
int nbAlive = 0;
//Check the status of the neighbors
for(int i = 0; i < nbNeighbor; i++)
nbAlive += Neighbor[i].currentGen;
//If status of cell is alive
if(currentGen == 1) {
//Bury cell if it has less than two neighbors
if(nbAlive < 2)
newGen = 0;
//Bury cell if it has more than three live neighbors
if(nbAlive > 3)
newGen = 0;
}
else {
//Dead cells with three live neighbors get reborn
if(nbAlive == 3)
newGen = 1;
}
}
//Switch to next generation
void updateGeneration() {
if(currentGen != newGen) { //Adjust color for the new generation
currentGen = newGen;
setBackground(color[currentGen]);
}
}
//Bury all cells in the city
void clear() {
if(currentGen == 1 || newGen == 1) {
currentGen = newGen = 0;
setBackground(color[currentGen]);
}
}
public void mouseClicked(MouseEvent arg0) {
if(currentGen == 1 || newGen == 1) {
currentGen = newGen = 0;
setBackground(color[currentGen]);
}
}
//If the mouse is in a cellHouse while being pressed, cell becomes alive
public void mouseEntered(MouseEvent arg0) {
if(mouseHold) {
currentGen = newGen = 1;
setBackground(color[1]);
}
}
public void mouseExited(MouseEvent arg0) {
}
//If the mouse is clicked on a cellHouse, cell comes to life
public void mousePressed(MouseEvent arg0) {
mouseHold = true;
currentGen = newGen = 1;
setBackground(color[1]);
}
//Set mouse as not being held anymore
public void mouseReleased(MouseEvent arg0) {
mouseHold = false;
}
}
}
Your code it too hard to follow, but I have done an application using mvc in swing.
My advice is to have something like this in a Main class:
MainModel model = new MainModel();
MainView view = new MainView(model);
MainController controller = new MainController(model, view);
Your controller is ok.
The view looks ok, but you should have separate addActionListener methods for clearB, startB, pauseB (perhaps that's the problem).
The model is where the brain should be. Do not extend JLabel, doesn't make sense at all.
Hope this helps.