I'm trying to build a Scrabble game to help get more familiar with building GUIs and practice java skills in general. The board is mainly composed of a JLayeredPane with JPanels to include the board image and board spaces. I'm trying to be able to drag and drop the Tile objects (extends JLabel) around the board by adding the tile to the drag_layer and then moving it from there, similar to the following example: dragging a jlabel around the screen. The mouse press is working and correctly adding the tile to the drag layer, but after that, it seems like the mouse stops listening completely. I tried just printing "Dragging" in the mouseDragged override method, but it won't even print that. There are two relevant classes - Console and MouseInput, which I'll show below, but I'll also add the link to the GitHub repo at the end if you want to pull the whole project.
Console:
public Console () throws IOException{
// Create gameConsole
gameConsole = new JFrame();
final int frameWidth = 850;
final int frameHeight = 950;
gameConsole.setPreferredSize(new Dimension(frameWidth, frameHeight));
gameConsole.setTitle("Scrabble");
gameConsole.setLayout(new GridBagLayout());
gameConsole.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.ipadx = 810;
c.ipady = 950;
// Create center console panel and add to gameConsole
centerConsole = new JPanel();
centerConsole.setLayout(new BorderLayout());
gameConsole.add(centerConsole,c);
// Create layered pane that holds the board and playerbox
gameContainer = new JLayeredPane();
gameContainer.setBounds(0,0,810,950);
gameContainer.addMouseListener(new MouseInput(gameContainer));
gameContainer.addMouseMotionListener(new MouseInput(gameContainer));
centerConsole.add(gameContainer, BorderLayout.CENTER);
// Create board image label and add to JPanel
BufferedImage scrabbleImage = ImageIO.read(Console.class.getResource("/board.jpg"));
JLabel background = new JLabel(new ImageIcon(scrabbleImage));
boardImage = new JPanel();
boardImage.setBounds(0, 0, 810, 810);
boardImage.add(background);
boardImage.setOpaque(true);
// create JPanel with gridBagLayout
boardGrid = new JPanel();
boardGrid.setBounds(0, 3, 810, 810);
boardGrid.setLayout(new GridBagLayout());
boardGrid.setOpaque(false);
// Create panels to add to boardGrid
spaces = new BoardSpace [15][15];
BoardSpace.setBoardSpaces(spaces);
// Set grid constraints
GridBagConstraints cGrid = new GridBagConstraints();
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 15; j++) {
// panel constraints
cGrid.gridx = i; // grid x location
cGrid.gridy = j; // grid y location
cGrid.gridheight = 1; // spans 1 row
cGrid.gridwidth = 1; // spans 1 column
cGrid.weightx = 0.0;
cGrid.weighty = 0.0;
cGrid.fill = GridBagConstraints.BOTH; // Resize veritically & horizontally
// Set size of board space and add to grid
spaces[i][j].setOpaque(false);
spaces[i][j].setPreferredSize(new Dimension((int) Info.GRIDSIZE,(int) Info.GRIDSIZE));
boardGrid.add(spaces[i][j], cGrid);
}
}
// Add to layeredPane
gameContainer.add(boardImage, new Integer(0),0);
gameContainer.add(boardGrid, new Integer(1),0);
// Create player box panel
playerPanel = new JPanel();
playerPanel.setLayout(new GridBagLayout());
playerBox = new JPanel();
playerBox.setLayout(new GridLayout(1,7, 10, 0));
// Create player box constraints
GridBagConstraints cp = new GridBagConstraints();
cp.ipadx = 50;
cp.ipady = 50;
// Create playerBox spaces
playerSpaces = new BoardSpace [1][7];
BoardSpace.setPlayerSpaces(playerSpaces);
// Add playerSpaces to playerBox
for (int j = 0; j < 7; j++) {
// panel constraints
cGrid.gridx = 0; // grid x location
cGrid.gridy = j; // grid y location
cGrid.gridheight = 1; // spans 1 row
cGrid.gridwidth = 1; // spans 1 column
cGrid.weightx = 0.0;
cGrid.weighty = 0.0;
cGrid.fill = GridBagConstraints.BOTH; // Resize veritically & horizontally
// Set size of board space and add to grid
playerSpaces[0][j].setOpaque(false);
playerSpaces[0][j].setPreferredSize(new Dimension((int) Info.GRIDSIZE,(int) Info.GRIDSIZE));
playerBox.add(playerSpaces[0][j], cGrid);
}
// Add player box to south panel
playerPanel.add(playerBox, cp);
// Add player box to bottom of layeredPane
playerPanel.setBounds(0,825,810,75);
gameContainer.add(playerPanel, new Integer(0),0);
gameConsole.pack();
// Make gameConsole visible
gameConsole.setVisible(true);
}
Mouse Adapter:
public class MouseInput extends MouseAdapter {
/**
*
*/
private Component mouseArea;
private boolean dragging;
private Tile draggingTile;
private BoardSpace currentSpace;
private BoardSpace startingSpace;
private Component selectedObject;
Component [] panelObjects;
private JLayeredPane dragLayer;
private Point dragPoint;
private int dragWidth;
private int dragHeight;
public MouseInput(Component mouseArea) {
// TODO Auto-generated constructor stub
super();
mouseArea = this.mouseArea;
}
void eventOutput(String eventDescription, MouseEvent e) {
Point p = e.getPoint();
System.out.println(eventDescription
+ " (" + p.getX() + "," + p.getY() + ")"
+ " detected on "
+ e.getComponent().getClass().getName()
+ "\n");
}
public void mouseMoved(MouseEvent e) {
//eventOutput("Mouse moved", e);
}
public void mouseDragged(MouseEvent e) {
if (dragging) {
System.out.println("Dragging");
}
if (!dragging) {
return;
} else {
System.out.println("Dragging " + Tile.getLetter(draggingTile));
int x = e.getPoint().x - dragWidth;
int y = e.getPoint().y - dragHeight;
draggingTile.setLocation(x, y);
draggingTile.repaint();
}
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
//eventOutput("Mouse Clicked", e);
}
#Override
public void mouseEntered(MouseEvent e) {
/*
dragPoint = e.getPoint();
selectedObject = e.getComponent().getComponentAt(dragPoint);
// Get the current space
while (!selectedObject.getClass().getSimpleName().equals("BoardSpace")) {
try {
dragPoint = selectedObject.getMousePosition();
selectedObject = selectedObject.getComponentAt(dragPoint);
} catch (NullPointerException illegalSpace){
currentSpace = startingSpace;
break;
}
}
if (selectedObject.getClass().getSimpleName().equals("BoardSpace")) {
currentSpace = (BoardSpace) selectedObject;
System.out.println(BoardSpace.getID(currentSpace));
} */
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
//eventOutput("Mouse Exited", e);
}
#Override
public void mousePressed(MouseEvent e) {
dragLayer = (JLayeredPane) e.getSource();
dragPoint = e.getPoint();
selectedObject = e.getComponent().getComponentAt(dragPoint);
// Get the current space
while (!selectedObject.getClass().getSimpleName().equals("BoardSpace")) {
try {
dragPoint = selectedObject.getMousePosition();
selectedObject = selectedObject.getComponentAt(dragPoint);
} catch (NullPointerException illegalSpace){
return;
}
}
currentSpace = (BoardSpace) selectedObject;
startingSpace = currentSpace;
// If the boardspace has a tile, remove Tile from boardspace and add to dragging layer
if (BoardSpace.Taken(currentSpace)) {
// get dragging tile
draggingTile = BoardSpace.getTile(currentSpace);
dragging = true;
// remove tile and repaint space
BoardSpace.removeTile(currentSpace, draggingTile);
currentSpace.revalidate();
currentSpace.repaint();
// Add tile to dragging layer
dragWidth = draggingTile.getWidth() / 2;
dragHeight = draggingTile.getHeight() / 2;
int x = e.getPoint().x - dragWidth;
int y = e.getPoint().y - dragHeight;
draggingTile.setLocation(x, y);
dragLayer.add(draggingTile, JLayeredPane.DRAG_LAYER);
draggingTile.revalidate();
draggingTile.repaint();
System.out.println("Selected Tile " + Tile.getLetter(draggingTile));
} else {
return;
}
}
#Override
public void mouseReleased(MouseEvent e) {
/*
// TODO Auto-generated method stub
if (!BoardSpace.Taken(currentSpace)) {
return;
} else {
dragging = false;
BoardSpace.setTile(currentSpace, draggingTile);
draggingTile = null;
currentSpace.repaint();
currentSpace.revalidate();
} */
}
}
Full code can be pulled from: https://github.com/jowarren13/scrabble.git
So after playing around some more, I figured out that every time a new mouse operation is called (mouse pressed, mouse dragged, etc.), it was instantiating new instances of the private class variables, so the variables weren't holding their set values. To work around this, I created a new class as an extension of JLayeredPane with these additional variables and used my new class as the layered game console. Still working out some error handling for when a tile is released outside of bounds or in an invalid space, but the code below at least lets me move the tiles around in valid spaces. The following post was helpful in figuring this out: MouseListener in separate class not working. Again, full code can be pulled from the git repo listed above.
BoardPane class:
package objects;
import java.awt.Component;
import java.awt.Point;
import javax.swing.JLayeredPane;
public class BoardPane extends JLayeredPane {
/**
*
*/
private static final long serialVersionUID = 1L;
private boolean dragging;
private Tile draggingTile;
private BoardSpace currentSpace;
private BoardSpace startingSpace;
private Component selectedObject;
Component [] panelObjects;
private BoardPane dragLayer;
private Point dragPoint;
private int dragWidth;
private int dragHeight;
public BoardPane() {
this.dragging = false;
this.draggingTile = null;
this.currentSpace = null;
this.startingSpace = null;
this.selectedObject = null;
this.panelObjects = null;
this.dragLayer = null;
this.dragPoint = null;
this.dragWidth = 51/2;
this.dragHeight = 51/2;
}
public static void resetPane(BoardPane board) {
board.dragging = false;
board.draggingTile = null;
board.currentSpace = null;
board.startingSpace = null;
board.selectedObject = null;
board.panelObjects = null;
board.dragLayer = null;
board.dragPoint = null;
}
public static Boolean getDragStatus(BoardPane board) {
return board.dragging;
}
public static void setDragStatus(BoardPane board, Boolean status) {
board.dragging = status;
}
public static Tile getDragTile(BoardPane board) {
return board.draggingTile;
}
public static void setDragTile(BoardPane board, Tile t) {
board.draggingTile = t;
}
public static BoardSpace getCurrentSpace(BoardPane board) {
return board.currentSpace;
}
public static void setCurrentSpace(BoardPane board, BoardSpace bs) {
board.currentSpace = bs;
}
public static BoardSpace getStartingSpace(BoardPane board) {
return board.startingSpace;
}
public static void setStartingSpace(BoardPane board, BoardSpace bs) {
board.startingSpace = bs;
}
public static Component getSelectedObj(BoardPane board) {
return board.selectedObject;
}
public static void setSelectedObj(BoardPane board, Component obj) {
board.selectedObject = obj;
}
public static Component [] getPanelObjects(BoardPane board) {
return board.panelObjects;
}
public static BoardPane getDragLayer(BoardPane board) {
return board.dragLayer;
}
public static void setDragLayer(BoardPane board) {
board.dragLayer = board;
}
public static void setPanelObjects(BoardPane board, Component [] obj) {
board.panelObjects = obj;
}
public static Point getDragPoint (BoardPane board) {
return board.dragPoint;
}
public static void setDragPoint(BoardPane board, Point p) {
board.dragPoint = p;
}
public static int getDragWidth(BoardPane board) {
return board.dragWidth;
}
public static int getDragHeight(BoardPane board) {
return board.dragHeight;
}
}
Console:
package Main;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import objects.BoardPane;
import objects.BoardSpace;
import javax.imageio.ImageIO;
public class Console {
private JFrame gameConsole;
private JPanel centerConsole;
private BoardPane gameContainer;
private JPanel playerPanel;
private JPanel gamePanel;
private JPanel north;
private JPanel south;
private JPanel east;
private JPanel west;
private JLayeredPane center;
private JPanel splash;
private JPanel playerBox;
private JPanel boardGrid;
private JPanel boardImage;
private JButton start;
private BoardSpace [][] spaces;
private BoardSpace [][] playerSpaces;
public Console () throws IOException{
// Create gameConsole
gameConsole = new JFrame();
final int frameWidth = 850;
final int frameHeight = 950;
gameConsole.setPreferredSize(new Dimension(frameWidth, frameHeight));
gameConsole.setTitle("Scrabble");
gameConsole.setLayout(new GridBagLayout());
gameConsole.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.ipadx = 810;
c.ipady = 950;
// Create center console panel and add to gameConsole
centerConsole = new JPanel();
centerConsole.setLayout(new BorderLayout());
gameConsole.add(centerConsole,c);
// Create layered pane that holds the board and playerbox
gameContainer = new BoardPane();
gameContainer.setBounds(0,0,810,950);
MouseInput mouseActions = new MouseInput();
gameContainer.addMouseMotionListener(mouseActions);
gameContainer.addMouseListener(mouseActions);
centerConsole.add(gameContainer, BorderLayout.CENTER);
// Create board image label and add to JPanel
BufferedImage scrabbleImage = ImageIO.read(Console.class.getResource("/board.jpg"));
JLabel background = new JLabel(new ImageIcon(scrabbleImage));
boardImage = new JPanel();
boardImage.setBounds(0, 0, 810, 815);
boardImage.add(background);
boardImage.setOpaque(true);
// create JPanel with gridBagLayout
boardGrid = new JPanel();
boardGrid.setBounds(0, 3, 810, 810);
boardGrid.setLayout(new GridBagLayout());
boardGrid.setOpaque(false);
// Create panels to add to boardGrid
spaces = new BoardSpace [15][15];
BoardSpace.setBoardSpaces(spaces);
// Set grid constraints
GridBagConstraints cGrid = new GridBagConstraints();
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 15; j++) {
// panel constraints
cGrid.gridx = i; // grid x location
cGrid.gridy = j; // grid y location
cGrid.gridheight = 1; // spans 1 row
cGrid.gridwidth = 1; // spans 1 column
cGrid.weightx = 0.0;
cGrid.weighty = 0.0;
cGrid.fill = GridBagConstraints.BOTH; // Resize veritically & horizontally
// Set size of board space and add to grid
spaces[i][j].setOpaque(false);
spaces[i][j].setPreferredSize(new Dimension((int) Info.GRIDSIZE,(int) Info.GRIDSIZE));
boardGrid.add(spaces[i][j], cGrid);
}
}
// Add to layeredPane
gameContainer.add(boardImage, new Integer(0),0);
gameContainer.add(boardGrid, new Integer(1),0);
// Create player box panel
playerPanel = new JPanel();
playerPanel.setLayout(new GridBagLayout());
playerBox = new JPanel();
playerBox.setLayout(new GridLayout(1,7, 10, 0));
// Create player box constraints
GridBagConstraints cp = new GridBagConstraints();
cp.ipadx = 50;
cp.ipady = 50;
// Create playerBox spaces
playerSpaces = new BoardSpace [1][7];
BoardSpace.setPlayerSpaces(playerSpaces);
// Add playerSpaces to playerBox
for (int j = 0; j < 7; j++) {
// panel constraints
cGrid.gridx = 0; // grid x location
cGrid.gridy = j; // grid y location
cGrid.gridheight = 1; // spans 1 row
cGrid.gridwidth = 1; // spans 1 column
cGrid.weightx = 0.0;
cGrid.weighty = 0.0;
cGrid.fill = GridBagConstraints.BOTH; // Resize veritically & horizontally
// Set size of board space and add to grid
playerSpaces[0][j].setOpaque(false);
playerSpaces[0][j].setPreferredSize(new Dimension((int) Info.GRIDSIZE,(int) Info.GRIDSIZE));
playerBox.add(playerSpaces[0][j], cGrid);
}
// Add player box to south panel
playerPanel.add(playerBox, cp);
// Add player box to bottom of layeredPane
playerPanel.setBounds(0,825,810,75);
gameContainer.add(playerPanel, new Integer(0),0);
gameConsole.pack();
// Make gameConsole visible
gameConsole.setVisible(true);
}
}
Mouse Listener:
package Main;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JLayeredPane;
import objects.BoardSpace;
import objects.Tile;
import objects.BoardPane;
public class MouseInput implements MouseMotionListener, MouseListener {
/**
*
*/
private BoardPane dragLayer;
void eventOutput(String eventDescription, MouseEvent e) {
Point p = e.getPoint();
System.out.println(eventDescription
+ " (" + p.getX() + "," + p.getY() + ")"
+ " detected on "
+ e.getComponent().getClass().getName()
+ "\n");
}
#Override
public void mouseMoved(MouseEvent e) {
//eventOutput("Mouse moved", e);
}
#Override
public void mouseDragged(MouseEvent e) {
// Set dragLayer
dragLayer = BoardPane.getDragLayer((BoardPane) e.getSource());
// Drag tile across board
if (BoardPane.getDragStatus(dragLayer)) {
int x = e.getPoint().x - BoardPane.getDragWidth(dragLayer);
int y = e.getPoint().y - BoardPane.getDragHeight(dragLayer);
BoardPane.getDragTile(dragLayer).setLocation(x, y);
BoardPane.getDragTile(dragLayer).repaint();
System.out.println("Dragging Tile " + Tile.getLetter(BoardPane.getDragTile(dragLayer)));
}
}
#Override
public void mouseClicked(MouseEvent e) {
//eventOutput("Mouse Clicked", e);
}
#Override
public void mouseEntered(MouseEvent e) {
eventOutput("Mouse entered", e);
}
#Override
public void mouseExited(MouseEvent e) {
eventOutput("Mouse Exited", e);
}
#Override
public void mousePressed(MouseEvent e) {
eventOutput("Mouse Pressed", e);
// Set dragLayer, dragPoint, and selectedObject
BoardPane.setDragLayer((BoardPane) e.getSource());
dragLayer = (BoardPane) e.getSource();
BoardPane.setDragPoint(dragLayer, e.getPoint());
BoardPane.setSelectedObj(dragLayer, e.getComponent().getComponentAt(BoardPane.getDragPoint(dragLayer)));
// Find the current board space
try {
while (!BoardPane.getSelectedObj(dragLayer).getClass().getSimpleName().equals("BoardSpace")) {
BoardPane.setDragPoint(dragLayer, BoardPane.getSelectedObj(dragLayer).getMousePosition());
BoardPane.setSelectedObj(dragLayer, BoardPane.getSelectedObj(dragLayer).getComponentAt(BoardPane.getDragPoint(dragLayer)));
}
} catch (NullPointerException illegalSpace) {
return;
}
// Set the current board space & starting space
BoardPane.setCurrentSpace(dragLayer, (BoardSpace) BoardPane.getSelectedObj(dragLayer));
BoardPane.setStartingSpace(dragLayer, BoardPane.getCurrentSpace(dragLayer));
// If the board space has a tile, remove Tile from board space then add to dragging layer
if (BoardSpace.Taken(BoardPane.getCurrentSpace(dragLayer))) {
// get dragging tile
BoardPane.setDragTile(dragLayer, BoardSpace.getTile(BoardPane.getCurrentSpace(dragLayer)));
BoardPane.setDragStatus(dragLayer, true);
// remove tile and repaint space
BoardSpace.removeTile(BoardPane.getCurrentSpace(dragLayer), BoardPane.getDragTile(dragLayer));
BoardPane.getCurrentSpace(dragLayer).revalidate();
BoardPane.getCurrentSpace(dragLayer).repaint();
// Add tile to dragging layer at specified location
int x = e.getPoint().x - BoardPane.getDragWidth(dragLayer);
int y = e.getPoint().y - BoardPane.getDragHeight(dragLayer);
BoardPane.getDragTile(dragLayer).setLocation(x, y);
dragLayer.add(BoardPane.getDragTile(dragLayer), JLayeredPane.DRAG_LAYER);
BoardPane.getDragTile(dragLayer).revalidate();
BoardPane.getDragTile(dragLayer).repaint();
System.out.println("Selected Tile " + Tile.getLetter(BoardPane.getDragTile(dragLayer)));
} else {
return;
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (BoardPane.getDragStatus(dragLayer) == true) {
// Change drag status to false
BoardPane.setDragStatus(dragLayer, false);
// Set dragLayer & remove tile
dragLayer = BoardPane.getDragLayer((BoardPane) e.getSource());
dragLayer.remove(BoardPane.getDragTile(dragLayer));
dragLayer.revalidate();
dragLayer.repaint();
//get selected object at given point
BoardPane.setDragPoint(dragLayer, e.getPoint());
BoardPane.setSelectedObj(dragLayer, e.getComponent().getComponentAt(BoardPane.getDragPoint(dragLayer)));
// Find the current board space
try {
while(!BoardPane.getSelectedObj(dragLayer).getClass().getSimpleName().equals("BoardSpace")) {
BoardPane.setDragPoint(dragLayer, BoardPane.getSelectedObj(dragLayer).getMousePosition());
BoardPane.setSelectedObj(dragLayer, BoardPane.getSelectedObj(dragLayer).getComponentAt(BoardPane.getDragPoint(dragLayer)));
}
} catch (NullPointerException illegalSpace) {
// if released on an invalid space, put tile back in starting space
BoardPane.getStartingSpace(dragLayer).add(BoardPane.getDragTile(dragLayer));
BoardPane.getStartingSpace(dragLayer).revalidate();
BoardPane.getStartingSpace(dragLayer).repaint();
BoardPane.resetPane(dragLayer);
return;
}
// Set the current board space & starting space
BoardPane.setCurrentSpace(dragLayer, (BoardSpace) BoardPane.getSelectedObj(dragLayer));
// If space is not taken, add tile to space, otherwise put back in starting space
if (!BoardSpace.Taken(BoardPane.getCurrentSpace(dragLayer))) {
BoardSpace.setTile(BoardPane.getCurrentSpace(dragLayer), BoardPane.getDragTile(dragLayer));
BoardPane.getCurrentSpace(dragLayer).revalidate();
BoardPane.getCurrentSpace(dragLayer).repaint();
//BoardPane.setDragTile(dragLayer, null);
} else {
BoardPane.getStartingSpace(dragLayer).add(BoardPane.getDragTile(dragLayer));
BoardPane.getStartingSpace(dragLayer).revalidate();
BoardPane.getStartingSpace(dragLayer).repaint();
}
BoardPane.resetPane(dragLayer);
}
}
}
Related
So I am trying to make a TicTacToe game in Java with JFrame and trying to implement a MouseListener so the player can click on the squares. I found one method on the internet but cant figure out why its not working for me. What I already did is I created a frame and draw the grid and background color. Maybe I missed something? Thanks for your time.
import java.awt.*;
import javax.swing.JFrame;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Main extends JFrame
{
public static JFrame root = new JFrame("TicTacToe");
public static void main(String[] args)
{
int[] board = {
0, 0, 0,
0, 0, 0,
0, 0, 0
};
root.getContentPane().setBackground(new Color(232,209,197));
TicTacToeCanvas Canvas = new TicTacToeCanvas(board);
root.add(Canvas);
root.setSize(915,939);
root.setVisible(true);
root.setResizable(false);
root.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
root.pack();
MouseEventClass mouseEvents = new MouseEventClass(root);
}
}
class MouseEventClass implements MouseListener{
public MouseEventClass(JFrame frame){
frame.addMouseListener(this);
}
#Override
public void mouseExited(MouseEvent e){
}
#Override
public void mouseEntered(MouseEvent e){
}
#Override
public void mousePressed(MouseEvent e){
}
#Override
public void mouseReleased(MouseEvent e){
}
#Override
public void mouseClicked(MouseEvent e){
System.out.println("Click");
}
}
}
I would advise that you add the listener to the components held by the main JPanel so you can get individual clicked components more easily. For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class TicTacToePanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final String ROW = "row";
private static final String COL = "col";
private static final Color BKGRD1 = new Color(230, 230, 230);
private static final Color BKGRD2 = new Color(180, 180, 180);
private static final Dimension CELL_SIZE = new Dimension(80, 80);
private JPanel[][] panelGrid = new JPanel[3][3];
public TicTacToePanel() {
setLayout(new GridLayout(3, 3, 1, 1));
setBackground(Color.BLACK);
setBorder(BorderFactory.createLineBorder(Color.BLACK));
MyMouse myMouse = new MyMouse();
for (int i = 0; i < panelGrid.length; i++) {
for (int j = 0; j < panelGrid[i].length; j++) {
JPanel cellPanel = new JPanel();
cellPanel.putClientProperty(ROW, i);
cellPanel.putClientProperty(COL, j);
cellPanel.setPreferredSize(CELL_SIZE);
cellPanel.setBackground(BKGRD1);
cellPanel.addMouseListener(myMouse);
add(cellPanel);
panelGrid[i][j] = cellPanel;
}
}
}
class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
JPanel cell = (JPanel) e.getComponent();
if (cell == null) {
return;
}
Color background = cell.getBackground().equals(BKGRD1) ? BKGRD2 : BKGRD1;
cell.setBackground(background);
int row = (int) cell.getClientProperty(ROW);
int col = (int) cell.getClientProperty(COL);
System.out.printf("cell row & column: [%d, %d]%n", row, col);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TicTacToePanel mainPanel = new TicTacToePanel();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Here, I create a 2D array of JPanels:
private JPanel[][] panelGrid = new JPanel[3][3];
and then in the main JPanel's constructor,
create individual "cell" JPanels in a nested for loop, adding them to the main JPanel
Adding my mouse listener to each individual cell.
This way, I can get each individual clicked cell cleanly and easily
public TicTacToePanel() {
// ....
// my mouse listener
MyMouse myMouse = new MyMouse();
// create cells and fill my grid with cells
for (int i = 0; i < panelGrid.length; i++) {
for (int j = 0; j < panelGrid[i].length; j++) {
JPanel cellPanel = new JPanel();
// gives each cell identifying information
cellPanel.putClientProperty(ROW, i);
cellPanel.putClientProperty(COL, j);
cellPanel.setPreferredSize(CELL_SIZE);
cellPanel.setBackground(BKGRD1);
// add the same mouse listener to all cells
cellPanel.addMouseListener(myMouse);
add(cellPanel); // add it to the main JPanel
panelGrid[i][j] = cellPanel; // and to a holding array
}
}
}
The mouse listener is simple:
class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
// get the cell clicked
JPanel cell = (JPanel) e.getComponent();
// make sure it's not null
if (cell == null) {
return;
}
// then do stuff to it
Color background = cell.getBackground().equals(BKGRD1) ? BKGRD2 : BKGRD1;
cell.setBackground(background);
int row = (int) cell.getClientProperty(ROW);
int col = (int) cell.getClientProperty(COL);
System.out.printf("cell row & column: [%d, %d]%n", row, col);
}
}
So basically I'm trying to make a 2D maze-like game where you can either create a map yourself or load a premade one. You change the tiles of the map by giving the coordinates of the new desired object which can be a wall, grass, enemy, bomb, weapon, the hero, etc, after clicking the corresponding JButton. My question is: up until now I've been using just JLabels to place images of the grass in the map because they're the "basic tiles" on which the hero can move, and then I'm trying to change those to custom objects of the different classes so when the hero moves to one of them it triggers different actions, but it looks like a JLabel can only contain text or images, so how should I do it then? This is the code (I'm sorry it's in spanish):
public class Tablero extends JFrame {
private int numFilas;
private int numColumnas;
private int numMuros;
public Tablero(final int numFilas, final int numColumnas, final int numMuros) {
this.numFilas = numFilas;
this.numColumnas = numColumnas;
this.numMuros = numMuros;
JFrame tablero = new JFrame();
JPanel contenedor = new JPanel();
final JLabel[][] casilla = new JLabel[60][60];
tablero.setSize(1280, 720);
contenedor.setSize(1280, 720);
tablero.add(contenedor);
for (int x = 0; x < this.numFilas; x++) {
for (int y = 0; y < this.numColumnas; y++) {
casilla[x][y] = new JLabel();
casilla[x][y].setIcon(new ImageIcon("C:\\Users\\Andres\\Desktop\\Programación orientada a objetos\\Juego\\build\\classes\\juego\\pasto.png"));
contenedor.add(casilla[x][y]);
}
}
JOptionPane.showMessageDialog(null, "Cree a continuación el héroe");
int coorX = Integer.parseInt(JOptionPane.showInputDialog("Digite la coordenada en x del héroe: "));
int coorY = Integer.parseInt(JOptionPane.showInputDialog("Digite la coordenada en y del héroe: "));
int lives = Integer.parseInt(JOptionPane.showInputDialog("Digite la cantidad de vidas del héroe: "));
final Heroe heroe = new Heroe(coorX, coorY, lives);
JButton añadirMuros = new JButton("Añadir muros");
contenedor.add(añadirMuros);
añadirMuros.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
Muro[] muros = new Muro[numMuros];
for (int i = 0; i < numMuros; i++) {
int coorX = Integer.parseInt(JOptionPane.showInputDialog("Digite la coordenada en x del muro # " + i + 1));
int coorY = Integer.parseInt(JOptionPane.showInputDialog("Digite la coordenada en y del muro # " + i + 1));
muros[i] = new Muro(coorX, coorY, heroe);
casilla[coorX][coorY] = new JLabel();
}
System.out.println("Clicked! muros");
}
});
JButton añadirBombas = new JButton("Añadir bombas");
contenedor.add(añadirBombas);
JButton añadirPistolas = new JButton("Añadir pistolas");
contenedor.add(añadirPistolas);
JButton añadirBallestas = new JButton("Añadir ballestas");
contenedor.add(añadirBallestas);
JButton añadirEnemigos = new JButton("Añadir enemigos");
contenedor.add(añadirPistolas);
JButton determinarEntrada = new JButton("Determinar entrada");
contenedor.add(determinarEntrada);
JButton determinarSalida = new JButton("Determinar salida");
contenedor.add(determinarSalida);
tablero.setVisible(true);
tablero.setResizable(true);
tablero.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
Yes, a JLabel can only contain text or an ImageIcon.
You can use a JPanel to draw images directly. Your Wall and Enemy classes can draw a representation of themselves on a JPanel.
Here's a GameImages and GuessingGamePanel from a game I put together. The GuesssingGamePanel draws the images from the GameImages class.
package com.ggl.guessing.game.view;
import java.awt.Dimension;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
public class GameImages {
private Image backgroundImage;
private Image victoryImage;
public GameImages() {
readBackgroundImage();
readVictoryImage();
}
private void readBackgroundImage() {
Image image = null;
try {
URL url = getClass().getResource("/v8k3reduced.jpg");
image = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
this.backgroundImage = image;
}
private void readVictoryImage() {
Image image = null;
try {
URL url = getClass().getResource("/r7f8reduced.jpg");
image = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
this.victoryImage = image;
}
public Image getBackgroundImage() {
return backgroundImage;
}
public Dimension getPreferredSize() {
return new Dimension(backgroundImage.getWidth(null),
backgroundImage.getHeight(null));
}
public Image getVictoryImage() {
return victoryImage;
}
}
.
package com.ggl.guessing.game.view;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import com.ggl.guessing.game.model.GuessingGameModel;
public class GuessingGamePanel extends JPanel {
private static final long serialVersionUID =
-2429103448910749064L;
private boolean guessed;
private Dimension guessesPanelDimension;
private GameImages gameImages;
private GuessesPanel guessesPanel;
private GuessingGameFrame frame;
private GuessingGameModel model;
public GuessingGamePanel(GuessingGameFrame frame,
GuessingGameModel model, GameImages gameImages,
Dimension guessesPanelDimension) {
this.frame = frame;
this.model = model;
this.gameImages = gameImages;
this.guessesPanelDimension = guessesPanelDimension;
this.guessed = false;
createPartControl();
}
private void createPartControl() {
this.setLayout(null);
this.setPreferredSize(gameImages.getPreferredSize());
guessesPanel = new GuessesPanel(frame, model);
Dimension gp = guessesPanelDimension;
Dimension tp = gameImages.getPreferredSize();
int x = (tp.width - gp.width) / 2;
int y = (tp.height - gp.height) / 2;
guessesPanel.getPanel().setBounds(x, y, gp.width, gp.height);
this.add(guessesPanel.getPanel());
}
public void setGuessed(boolean guessed) {
this.guessed = guessed;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(gameImages.getBackgroundImage(), 0, 0, null);
if (guessed) {
g.drawImage(gameImages.getVictoryImage(), 0, 0, null);
}
}
}
I'm trying to set up dragging of an image over a grid of Jlabels.
it's working fine, but the image is "refreshing" and sometimes lags behind the mouse.
Is there a way to improve this and get a perfectly "smooth" movement of the image ?
EDIT: ok now, thanks to Andrew Thompson's advice I updated the paint() method to paintComponent(). But now why does my component disappear when I drag it? I'm probably missing something here...
EDIT2: why is the following behaviour: when using paint() method the component displays on top of the JLabels. But when using paintComponent() the component disappears being masked by the opaque Jlabels?
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class DragNDrop {
public static void main(String[] args)
{
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(new DragPanel());
f.pack();
f.setLocation(200,200);
f.setVisible(true);
}
}
class DragPanel extends JPanel {
JLabel[][] labels;
SelectableAction action;
Image image;
Point p;
public DragPanel()
{
p = new Point();
setOpaque(true);
createLabels();
action = new SelectableAction(this);
addMouseListener(action);
addMouseMotionListener(action);
}
private void createLabels() {
labels = new JLabel[8][8];
Dimension dim50 = new Dimension(50,50);
GridBagConstraints gbc = new GridBagConstraints();
this.setLayout(new GridBagLayout());
for (int x=0;x<8;x++){
for (int y=0;y<8;y++){
labels[x][y] = new JLabel();
labels[x][y].setOpaque(true);
labels[x][y].setPreferredSize(dim50);
String str = new String("("+x+","+y+")");
labels[x][y].setText(str);
if ((x+y) % 2 == 0){
labels[x][y].setBackground(Color.lightGray);
} else
{
labels[x][y].setBackground(Color.white);
}
gbc.gridx = x;
gbc.gridy = 7-y;
this.add(labels[x][y],gbc);
}
URL url = getClass().getResource("images/50px-Knight.pgn");
Image img;
try {
img = ImageIO.read(url);
labels[0][0].setIcon(new ImageIcon(img));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public JLabel[][] getLabels()
{
return labels;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(action.dragging)
g.drawImage(image, p.x, p.y, this);
}
public void setImage(Image i)
{
image = i;
}
public void setOrigin(Point p)
{
this.p = p;
repaint();
}
}
/**
* Mouse code to enable image dragging
*/
class SelectableAction extends MouseInputAdapter
{
DragPanel MyPanel;
Image selectedImage;
boolean dragging;
Rectangle r;
Point offset;
public SelectableAction(DragPanel dp)
{
MyPanel = dp;
dragging = false;
offset = new Point();
}
public void mousePressed(MouseEvent e)
{
Point p = e.getPoint();
JLabel[][] labels = MyPanel.getLabels();
for(int i = 0; i < labels.length; i++)
{
for (int j=0;j<labels[0].length;j++){
r = labels[i][j].getBounds();
if(r.contains(p))
{
if ( ((ImageIcon)labels[i][j].getIcon()).getImage() != null) {
selectedImage = ((ImageIcon)labels[i][j].getIcon()).getImage();
MyPanel.setImage(selectedImage);
labels[i][j].setIcon(null);
offset.x = p.x - r.x;
offset.y = p.y - r.y;
dragging = true;
MyPanel.setOrigin(new Point(r.x, r.y));
break;
}
}
}
}
}
public void mouseReleased(MouseEvent e)
{
Point p = e.getPoint();
JLabel[][] labels = MyPanel.getLabels();
for(int i = 0; i < labels.length; i++)
{
for (int j=0;j<labels[0].length; j++){
r = labels[i][j].getBounds();
if(r.contains(p)) {
ImageIcon tmpIcon = new ImageIcon(selectedImage);
labels[i][j].setIcon(tmpIcon);
MyPanel.repaint();
dragging = false;
}
}
}
}
public void mouseDragged(MouseEvent e)
{
if(dragging)
{
r.x = e.getX() - offset.x;
r.y = e.getY() - offset.y;
MyPanel.setOrigin(new Point(r.x, r.y));
}
}
}
+1 to AndrewThompson and GuillaumePolet comments. especially:
It disappears because you have opaque children components that paint
themselves above.
to overcome this we dont need/want to override paintComponent(..) instead we want to override paintChildren(..) and call super.paintChildren(..) followed by our custom code. Thus all components will be drawn in call to super and our image will be drawn after, thus making it appear/visible above all others.
Replace the overridden paintComponent with below code and it will work:
#Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
if (action.dragging) {
g.drawImage(image, p.x, p.y, null);//also notice i use null and not this, unless the class you are using extends ImageObserver no need for this
}
}
I've had a look around for this problem but couldn't find an answer...
I currently have a JPanel in which I'm painting a load of unicode characters (music notes) using the Graphics2D g2.drawString() method.
I have an ArrayList of KeyPress objects, each of which contains one or more g2.drawString() calls.
So each KeyPress object is a music note and is painted on the JPanel.
How would I go about adding the functionality to enable the user to select and drag the objects?
Why not put your Strings in JLabels and simply drag them...
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class DragLabelEg {
private static final String[] LABEL_STRINGS = { "Do", "Re", "Me", "Fa",
"So", "La", "Ti" };
private static final int HEIGHT = 400;
private static final int WIDTH = 600;
private static final Dimension MAIN_PANEL_SIZE = new Dimension(WIDTH, HEIGHT);
private static final int LBL_WIDTH = 60;
private static final int LBL_HEIGHT = 40;
private static final Dimension LABEL_SIZE = new Dimension(LBL_WIDTH,
LBL_HEIGHT);
private JPanel mainPanel = new JPanel();
private Random random = new Random();
public DragLabelEg() {
mainPanel.setPreferredSize(MAIN_PANEL_SIZE);
mainPanel.setLayout(null);
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
for (int i = 0; i < LABEL_STRINGS.length; i++) {
JLabel label = new JLabel(LABEL_STRINGS[i], SwingConstants.CENTER);
label.setSize(LABEL_SIZE);
label.setOpaque(true);
label.setLocation(random.nextInt(WIDTH - LBL_WIDTH),
random.nextInt(HEIGHT - LBL_HEIGHT));
label.setBackground(new Color(150 + random.nextInt(105), 150 + random
.nextInt(105), 150 + random.nextInt(105)));
label.addMouseListener(myMouseAdapter);
label.addMouseMotionListener(myMouseAdapter);
mainPanel.add(label);
}
}
public JComponent getMainPanel() {
return mainPanel;
}
private class MyMouseAdapter extends MouseAdapter {
private Point initLabelLocation = null;
private Point initMouseLocationOnScreen = null;
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
// get label's initial location relative to its container
initLabelLocation = label.getLocation();
// get Mouse's initial location relative to the screen
initMouseLocationOnScreen = e.getLocationOnScreen();
}
#Override
public void mouseReleased(MouseEvent e) {
initLabelLocation = null;
initMouseLocationOnScreen = null;
}
#Override
public void mouseDragged(MouseEvent e) {
// if not dragging a JLabel
if (initLabelLocation == null || initMouseLocationOnScreen == null) {
return;
}
JLabel label = (JLabel) e.getSource();
// get mouse's new location relative to the screen
Point mouseLocation = e.getLocationOnScreen();
// and see how this differs from the initial location.
int deltaX = mouseLocation.x - initMouseLocationOnScreen.x;
int deltaY = mouseLocation.y - initMouseLocationOnScreen.y;
// change label's position by the same difference, the "delta" vector
int labelX = initLabelLocation.x + deltaX;
int labelY = initLabelLocation.y + deltaY;
label.setLocation(labelX, labelY);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGui();
}
});
}
private static void createGui() {
JFrame frame = new JFrame("App");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DragLabelEg().getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
See the tutorial on supporting user interaction. It comes down to determining which (if any) objects were underneath the mouse when it was clicked and held. On a drag event, the selected object is moved and the canvas is repainted.
You can obtain the bounds of the string by using FontMetrics:
String text = "Hello world!";
Rectangle2D bounds = g2.getFontMetrics().getStringBounds(text, g2);
I assume the rectangle top-left will be (0, 0), so you need to add (x, y) to it (where x, y are the parameters you passed to drawString).
This example shows one way to select multiple objects, using keyboard or mouse, and drag
them as a group. It manipulates arbitrary nodes rather than glyphs, but you may find it instructive.
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.