I have written a short game. In the existing implementation I have a GridBagLayout with buttons located as chess board. Each button occupies the whole grid. Game works fine. My next task is to change the board to be consist of hexagonal buttons, not rectangles like currently. I completely don't know how to do this. Buttons should look like these on the picture:
This isn't the prettiest way, but It will at least give you an Idea:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class HexagonPattern extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ROWS = 7;
private static final int COLUMNS = 7;
private HexagonButton[][] hexButton = new HexagonButton[ROWS][COLUMNS];
public HexagonPattern() {
setLayout(null);
initGUI();
}
public void initGUI() {
int offsetX = -10;
int offsetY = 0;
for(int row = 0; row < ROWS; row++) {
for(int col = 0; col < COLUMNS; col++){
hexButton[row][col] = new HexagonButton(row, col);
hexButton[row][col].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
HexagonButton clickedButton = (HexagonButton) e.getSource();
System.out.println("Button clicked: [" + clickedButton.getRow() + "][" + clickedButton.getCol() + "]");
}
});
add(hexButton[row][col]);
hexButton[row][col].setBounds(offsetY, offsetX, 105, 95);
offsetX += 87;
}
if(row%2 == 0) {
offsetX = -52;
} else {
offsetX = -10;
}
offsetY += 76;
}
}
public static void main(String[] args) {
HexagonPattern hexPattern = new HexagonPattern();
JFrame frame = new JFrame();
frame.setTitle("Hexagon Pattern");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(new Point(700, 300));
frame.add(hexPattern);
frame.setSize(550, 525);
frame.setResizable(false);
frame.setVisible(true);
}
//Following class draws the Buttons
class HexagonButton extends JButton {
private static final long serialVersionUID = 1L;
private static final int SIDES = 6;
private static final int SIDE_LENGTH = 50;
public static final int LENGTH = 95;
public static final int WIDTH = 105;
private int row = 0;
private int col = 0;
public HexagonButton(int row, int col) {
setContentAreaFilled(false);
setFocusPainted(true);
setBorderPainted(false);
setPreferredSize(new Dimension(WIDTH, LENGTH));
this.row = row;
this.col = col;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Polygon hex = new Polygon();
for (int i = 0; i < SIDES; i++) {
hex.addPoint((int) (50 + SIDE_LENGTH * Math.cos(i * 2 * Math.PI / SIDES)), //calculation for side
(int) (50 + SIDE_LENGTH * Math.sin(i * 2 * Math.PI / SIDES))); //calculation for side
}
g.drawPolygon(hex);
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
}
}
Test it out!
This program consists of 2 classes:
HexagonButton, which uses Graphics to draw a hexagon into a JButton. It also returns the row and column values when getRow or getCol are called.
HexagonPattern, which is the main class. It makes the pattern by laying them out with setBounds(x, y, width, height). It uses an ActionListener to print the coordinates of the Hexagon clicked, by calling getRow and getCol.
Like I said, this isn't the greatest program. If you want to make the hexagons smaller, then you'll have to change many variables.
Related
I'm learning Java these days, my first project is to create a "Go board", 9 * 9 rows and columns, and place black and white stones on the intersections.
I created a board with 9 * 9 lines and columns, now I have to create black and white stones using the JButton component.
Other than the color, size, and position of the button on the first row (setLayout), I was unable to turn the button into a circle and place the stone on the intersection points.
From multiple searches for related guides, I have noticed that there is some unique structure that I am not familiar with for creating and designing buttons.
And now my question comes in - what is the code structure I need to create in order to produce a button in the shape of a circle, size 65 * 65, in black or white? Do I need to create a new class for this? How and where should I integrate JPanel?
public class Main {
public static void main(String[] args) {
Board board = new Board(900, 900, "Go board");
}
}
import java.awt.*;
import javax.swing.*;
public class Board extends JPanel {
private int width;
private int height;
private String title;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Board(int width, int height, String title) {
super();
this.width = width;
this.height = height;
this.title = title;
this.initBoard();
}
public Board() {
super();
}
public void initBoard() {
JFrame f = new JFrame(this.getTitle());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// f.getContentPane().setBackground(Color.getHSBColor(25, 75, 47));
f.setSize(this.getWidth(), this.getHeight());
// f.setLocation(550, 25);
f.add(this, BorderLayout.CENTER);
f.setVisible(true);
JButton stone = new JButton(" ");
f.add(stone);
f.setLayout(new FlowLayout());
stone.setBackground(Color.BLACK.darker());
stone.setBorder(BorderFactory.createDashedBorder(getForeground()));
stone.setPreferredSize(new Dimension(65, 65));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < 10; i++) {
g.drawLine(0, 10 + (i * ((this.getWidth() - 20) / 9)), this.getWidth(),
10 + (i * ((this.getWidth() - 20) / 9)));
g.drawLine(10 + (i * ((this.getHeight() - 20) / 9)), 0, 10 + (i * ((this.getHeight() - 20) / 9)),
this.getHeight());
}
}
}
Before uploading the post, I read the following posts:
Design Button in Java (like in CSS)
How can I set size of a button?
Java: JButton with custom Shape: Fill with Metal Look and Feel Gradient
How to Use Borders
Java JButton
How to use setUI method in javax.swing.JButton
Note: I do not want to access posts that explain how to produce a "Go board", the learning process in this context is my goal.
Use a JPanel with a 9x9 GridLayout and ad to it JButtons configured to your need as demonstrated in the following very basic mre:
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
public class GridOfButtons extends JPanel {
private static final int ROWS = 9, COLS = 9, SIZE = 65, BORDER = 2;
private static final Color BOARD_COLOR = Color.BLACK;
public GridOfButtons() {
setLayout(new GridLayout(ROWS, COLS, BORDER, BORDER));
setBackground(BOARD_COLOR);
StonesFactory factory = new StonesFactory(SIZE);
boolean isBlack = false;
for (int col = 0; col < COLS; col++) {
for (int row = 0; row < ROWS; row++) {
add(factory.makeButton(isBlack));
isBlack = !isBlack;
}
}
this.initBoard();
}
public void initBoard() {
JFrame f = new JFrame("Board Of Buttons");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridBagLayout());
f.add(this);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()->new GridOfButtons());
}
}
class StonesFactory{
private static final Color STONE = Color.YELLOW, WHITE_STONE = Color.WHITE, BLACK_STONE = Color.BLACK;
private final int size;
private final ImageIcon whiteIcon, blackIcon;
public StonesFactory(int size) {
this.size = size;
whiteIcon = new ImageIcon(createImage(false));
blackIcon = new ImageIcon(createImage(true));
}
JButton makeButton(boolean isBlack){
JButton stone = new JButton();
stone.setPreferredSize(new Dimension(size, size));
stone.setBackground(STONE);
stone.setIcon(isBlack ? blackIcon : whiteIcon);
return stone;
}
//construct image for button's icon
private BufferedImage createImage(boolean isBlack) {
BufferedImage img = new BufferedImage(size , size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(isBlack ? BLACK_STONE : WHITE_STONE);
g2.fillOval(0,0,size,size);
g2.dispose();
return img;
}
}
(Run it online)
Alternatively you can produce the board by custom painting of a JPanel. This will make the individual "stones" not clickable and more difficult to modify:
import java.awt.*;
import javax.swing.*;
public class GridByPainting extends JPanel {
private static final int ROWS = 9, COLS = 9, SIZE = 65, BORDER = 2;
private static final Color BOARD_COLOR = Color.BLACK, STONE = Color.YELLOW,
WHITE_STONE = Color.WHITE, BLACK_STONE = Color.BLACK;
private final Dimension size;
public GridByPainting() {
int x = BORDER + COLS*(SIZE + BORDER);
int y = BORDER + ROWS*(SIZE + BORDER);
size = new Dimension(x,y);
this.initBoard();
}
#Override
public Dimension getPreferredSize() {
return size;
}
public void initBoard() {
JFrame f = new JFrame("Grid By Painting");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridBagLayout());
f.add(this);
f.pack();
f.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth(); int height = getHeight();
int stoneWidth = (width - BORDER) / COLS - BORDER;
int stoneHeight = (height -BORDER)/ ROWS - BORDER ;
//draw board
g.setColor(BOARD_COLOR);
g.fillRect(0, 0, width, height);
boolean isBlack = true;
//draw square stones
for (int col = 0; col < COLS; col++) {
for (int row = 0; row < ROWS; row++) {
int x = BORDER + col*(stoneWidth + BORDER);
int y = BORDER + row*(stoneHeight + BORDER);
g.setColor(STONE);
g.fillRect(x, y, stoneWidth, stoneHeight);
//draw circle
g.setColor(isBlack ? BLACK_STONE : WHITE_STONE);
isBlack = !isBlack;
g.fillOval(x, y, stoneWidth, stoneHeight);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()->new GridByPainting());
}
}
(Run it online)
It seems like you skipped over some of the important parts of the Oracle tutorial, Creating a GUI With Swing.
Here's my comment from August 23rd, just 10 days ago.
Generally, you create a logical model of a Go board using a plain Java
getter / setter class. You use a drawing JPanel to create the Go board
in the GUI and draw circles to represent the stones. The Oracle
tutorial, Creating a GUI With Swing, will show you the steps to
creating a Swing GUI. Skip the Netbeans section.
So, where's your logical model? Where's your drawing JPanel?
Here's a quick GUI I created.
My code has a logical model. My code has a drawing JPanel.
The first thing I did was create a plain Java getter / setter class to hold a logical representation of a Go Board. I named this the Board class.
The next thing I did was start my Swing GUI with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I used the run method of my Runnable class to create the JFrame. The JFrame methods must be called in a specific order. This is the order I use for all my Swing applications.
I separate the creation of the JFrame from the creation of any subsequent JPanels. I do this to keep my code organized, easy to read, and easy to understand.
I extend a JPanel to create the drawing JPanel. I do this so I can override the paintComponent method of the JPanel class. The drawing JPanel draws (paints) the board state. That's all. Nothing else. Another class will take care of adding pieces to the logical Go board and repainting the drawing JPanel.
The MoveListener class implements a MouseListener (extends a MouseAdapter) to respond to mouse clicks on the Go board. The MoveListener class keeps track of whose turn it is. In a more elaborate version of a Go board, you would have another plain Java getter / setter class to keep track of the game state.
Here's the complete runnable code. I made all the classes inner classes so I could post this code as one block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GoBoard implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new GoBoard());
}
private Board board;
private DrawingPanel drawingPanel;
public GoBoard() {
this.board = new Board();
}
#Override
public void run() {
JFrame frame = new JFrame("Go Board");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.drawingPanel = new DrawingPanel(board);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final int margin, pieceRadius, lineSpacing;
private Board board;
public DrawingPanel(Board board) {
this.board = board;
this.margin = 60;
this.pieceRadius = 40;
this.lineSpacing = 100;
this.setBackground(new Color(0x993300));
int width = 8 * lineSpacing + margin + margin;
this.setPreferredSize(new Dimension(width, width));
this.addMouseListener(new MoveListener(board));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
paintHorizontalLines(g2d);
paintVerticalLines(g2d);
paintPieces(g2d);
}
private void paintHorizontalLines(Graphics2D g2d) {
int x = margin;
int y1 = margin;
int y2 = getHeight() - margin;
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(3f));
for (int index = 0; index < 9; index++) {
g2d.drawLine(x, y1, x, y2);
x += lineSpacing;
}
}
private void paintVerticalLines(Graphics2D g2d) {
int x1 = margin;
int x2 = getWidth() - margin;
int y = margin;
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(3f));
for (int index = 0; index < 9; index++) {
g2d.drawLine(x1, y, x2, y);
y += lineSpacing;
}
}
private void paintPieces(Graphics2D g2d) {
int[][] b = board.getBoard();
for (int row = 0; row < b.length; row++) {
for (int column = 0; column < b[row].length; column++) {
int x = column * lineSpacing + margin;
int y = row * lineSpacing + margin;
if (b[row][column] == 1) {
g2d.setColor(Color.BLACK);
g2d.fillOval(x - pieceRadius, y - pieceRadius,
pieceRadius + pieceRadius, pieceRadius + pieceRadius);
} else if (b[row][column] == 2) {
g2d.setColor(Color.WHITE);
g2d.fillOval(x - pieceRadius, y - pieceRadius,
pieceRadius + pieceRadius, pieceRadius + pieceRadius);
}
}
}
}
}
public class MoveListener extends MouseAdapter {
private boolean isBlackTurn = true;
private Board board;
public MoveListener(Board board) {
this.board = board;
}
#Override
public void mouseReleased(MouseEvent event) {
Point point = event.getPoint();
int margin = 60;
int pieceRadius = 40;
int lineSpacing = 100;
int column = (point.x - margin + pieceRadius) / lineSpacing;
int row = (point.y - margin + pieceRadius) / lineSpacing;
int piece = (isBlackTurn) ? 1 : 2;
board.setPiece(piece, row, column);
drawingPanel.repaint();
isBlackTurn = !isBlackTurn;
}
}
public class Board {
private int[][] board;
public Board() {
this.board = new int[9][9];
}
/**
* <p>
* This method inserts a piece on the board.
* </p>
*
* #param piece - 1 for black, 2 for white
* #param row - row of piece
* #param column - column of piece
*/
public void setPiece(int piece, int row, int column) {
this.board[row][column] = piece;
}
public int[][] getBoard() {
return board;
}
}
}
I'm working on a really basic bar chart which has to display 6 values. The problem I'm running into is that when i put the bars on the screen once they stay on the screen, and i cannot get them off. I've tried using the remove, repaint and revalidate functions but these all do not work.
What do I have to do to remove the bars so they don't clog up?
My code:
import javax.swing.*;
import java.awt.*;
import java.util.Collections;
public class BarChart extends JPanel
{
private JLabel[] bars;
public BarChart(int[] data)
{
update(data);
}
public void update(int[] data)
{
this.setSize(190, 155);
this.setLayout(null);
int max = 0;
for (int i = 0; i < 6; i++) {if (data[i] > max) {max = data[i];}}
bars = new JLabel[6];
for (int i = 0; i < 6; i++)
{
bars[i] = new JLabel();
bars[i].setOpaque(true);
bars[i].setBackground(Color.RED);
int height = (max != 0) ? (data[i]*155)/max : 0;
System.out.printf("%d, %d, %d,... ", height, data[i], max);
this.add(bars[i]);
bars[i].setSize(25, height);
bars[i].setLocation((31*i)+5, 155-height);
}
System.out.println("");
}
}
For your current code, you would need to call removeAll(), then revalidate() and repaint() on the JPanel would "solve" your problem, but you have other unrelated problems:
You're setting a component's size when you should be producing a preferred size -- this is what layout managers generally work with
You're using null layouts, a very dangerous thing
You're using a "magic" number as a for loop ending condition -- a VERY dangerous thing to do. How do you know that the data array has 6 and only items within it. What harm is there in simply using the data array's length as you've likely done hundreds of times before?
Instead, consider using more flexible code that will adapt to any size of data you give it and that avoids null layouts. For example consider the following code that draws the bars within the JPanel's paintComponent method:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class TestBarChart extends JPanel {
private static final int[] INIT_DATA = { 1, 2, 4, 5, 6, 9 };
protected static final int MIN_DATA_lENGTH = 5;
protected static final int MAX_DATA_LENGTH = 9;
private static final int MAX_VALUE = 9;
private static final int PREF_W = 300;
private static final int PREF_H = 240;
private BarChart2 barChart2 = new BarChart2(INIT_DATA, MAX_VALUE, PREF_W, PREF_H);
public TestBarChart() {
barChart2.setBorder(BorderFactory.createLineBorder(Color.BLUE));
JPanel chartsPanel = new JPanel(new GridLayout(1, 0));
chartsPanel.setLayout(new GridLayout(1, 0));
chartsPanel.add(barChart2);
JButton resetDataBtn = new JButton(new AbstractAction("Reset Data") {
#Override
public void actionPerformed(ActionEvent e) {
int dataLength = (int) ((MAX_DATA_LENGTH - MIN_DATA_lENGTH) * Math.random()) + MIN_DATA_lENGTH;
int[] data = new int[dataLength];
for (int i = 0; i < data.length; i++) {
data[i] = (int) (MAX_VALUE * Math.random()) + 1;
}
barChart2.setData(data, MAX_VALUE);
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(resetDataBtn);
setLayout(new BorderLayout());
add(chartsPanel);
add(btnPanel, BorderLayout.PAGE_END);
}
private static void createAndShowGui() {
TestBarChart mainPanel = new TestBarChart();
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class BarChart2 extends JPanel {
private static final double BAR_WIDTH = 0.90;
private int prefW;
private int prefH;
private static final Color BAR_COLOR = Color.RED;
private int[] data;
private int maxValue;
public BarChart2(int[] data, int maxValue, int prefW, int prefH) {
setData(data, maxValue);
this.prefW = prefW;
this.prefH = prefH;
}
public final void setData(int[] data, int maxValue) {
this.data = data;
this.maxValue = maxValue;
repaint();
}
public int[] getData() {
return data;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(BAR_COLOR);
// simple algebraic calculations on where to place the bars
double denom = data.length + 1 - BAR_WIDTH;
int barWidth = (int) ((getWidth() * BAR_WIDTH) / denom);
for (int i = 0; i < data.length; i++) {
int x = (int) (getWidth() * (i + 1 - BAR_WIDTH) / denom);
int height = (int) (getHeight() * data[i] / (double) maxValue);
int y = (int) (getHeight() - height);
g.fillRect(x, y, barWidth, height);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(prefW, prefH);
}
}
Note that the bar charts re-size to fill the GUI if you re-size the GUI. Note that the chart accomodates any number of data bars, all depending on the length of the data array passed into it.
You need to repaint the component.
getContentPane().validate();
getContentPane().repaint();
Currently I'm working on an assignment about creating a version of "Game of Life". However my cells won't appear.
This is my Cell Class:
class Cell{
boolean alive; //true if cell is alive, false if cell is dead
int numNeighbors; //number of alive neightboring cells
//change alive/dead state of the cell
void setAlive(boolean state){
alive = state;
}
//return alive/dead state of the cell
boolean isAlive(){
return alive;
}
//set numNeightbors of the cell to n
void setNumNeighbors(int n){
numNeighbors = n;
}
//take the cell to the next generation
void update(){
if(numNeighbors <2 || numNeighbors >3){
alive = false;
} else if((numNeighbors == 2 || numNeighbors == 3) && alive == true){
alive = true;
} else if(numNeighbors == 3 && alive == false){
alive = true;
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor( Color.blue );
g.setOpaque(true);
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
if(grid[i][j].isAlive()){
g.setColor( Color.BLACK);
} else {
g.setColor ( Color.WHITE);
g.fillRect(50, 50, 50*i, 50*j);
}
}
}
}
And this is my GameOfLife Class
<pre>import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import java.io.*;
public class GameOfLife implements MouseListener{
Cell[][] grid; //contain grid of cells
String birthFilename = "birth.txt"; //text file where initial generation is stored
int row; //number of rows
int col; //number of columns
ActionListener actionListener = new ActionListener(){
javax.swing.Timer timer = new javax.swing.Timer(500, this); //new timer
#Override
public void actionPerformed(ActionEvent event){
}
};
public void buildIt() {
int width = 600;
int height = 600;
JFrame frame = new JFrame("Game of Life");
readInitial();
//adds button interface
JPanel buttonbar = new JPanel();
frame.add(buttonbar, BorderLayout.SOUTH);
JButton start = new JButton("Start");
JButton stop = new JButton("Stop");
JButton nextg = new JButton("Next Generation");
buttonbar.add(nextg);
buttonbar.add(start);
buttonbar.add(stop);
JPanel panel = new JPanel();
frame.add(panel);
panel.setPreferredSize(new Dimension(width, height));
panel.setLayout(new GridLayout(row, col, 4, 4));
frame.pack();
frame.setBackground(Color.WHITE);
frame.setVisible(true);
}
public void mousePressed( MouseEvent e) {
//add code to update x and y
}
public void mouseReleased( MouseEvent e) { }
public void mouseClicked( MouseEvent e) { }
public void mouseEntered( MouseEvent e) { }
public void mouseExited( MouseEvent e) { }
//calculate number of living neightbors of each cell and sets numNeighbors
//Does not update dead/live state of cells
void calculateNumNeighbors(){
int numNeighbors = 0;
for(int i = 1; i < row + 1; i++){
for(int j = 1; j < col + 1; j++){
for(int k = -1; k < 2; k++){
for(int m = -1; m < 2; m++){
if(grid[i+k][j+m].isAlive() && !(k == 0 && m == 0)){
numNeighbors++;
}
}
}
grid[i][j].setNumNeighbors(numNeighbors);
}
}
}
//create grid and read initial generation from file
void readInitial(){
try{
grid = new Cell[row + 2][col + 2]; //empty neighbors at corners, so + 2
File file = new File(birthFilename);
Scanner scanner = new Scanner( file );
row = scanner.nextInt();
col = scanner.nextInt();
for(int i = 0; i < row + 2; i++){
for (int j = 0; j < col + 2; j++){
grid[i][j] = new Cell();
}
}
for(int i = 1; i < row + 1; i++){
for (int j = 1; j < col + 1; j++){
if(scanner.next().equals(".")){
grid[i][j].setAlive(false);
} else if(scanner.next().equals("*")){
grid[i][j].setAlive(true);
}
}
}
for(int i = 0; i < row + 2; i++){
grid[0][i].setAlive(false);
grid[row+2][i].setAlive(false);
}
for(int j = 0; j < col + 2; j++){
grid[j][0].setAlive(false);
grid[j][col+2].setAlive(false);
}
} catch(FileNotFoundException e) {
grid = new Cell[12][12];
row = 10;
col = 10;
for(int i = 0; i < 12; i++){
for (int j = 0; j < 12; j++){
grid[i][j] = new Cell();
grid[i][j].setAlive(false);
}
}
}
}
//update grid to the next generation, using the values of numNeightbors in the cells
void nextGeneration(){
for(int i = 1; i < row + 1; i++){
for (int j = 1; j < col + 1; j++){
grid[i][j].update();
}
}
}
public static void main(String[] arg) {
(new GameOfLife()).buildIt();
}
I hope anyone can help me out to make this program work.
I don't see why anything should draw. You've got a Cell class that yes has a paintComponent method, but this method is meaningless since it's not part of a Swing component. Your JPanel, where you should do drawing -- does nothing. You've other problems with the Cell class too in that it appears to be trying to draw the whole grid and not just a single cell.
Get rid of Cell's paintComponent method
Instead give it a public void draw(Graphics g) method that allows it to draw itself.
Create a JPanel that holds a grid of cells
Have this JPanel do the drawing in its paintComponent override. It will call the draw(g) method of all the Cells that it holds within a for loop.
Always place an #Override annotation above any overridden method. If you had done this, above your paintComponent, the compiler would have warned you that something was wrong.
For example: here's a small program that does not do the Game of Life, but shows an example of a JPanel holding and displaying a grid of non-component cells. By "non-component" the SimpleCell class does not extend from a Swing component, does not have any Swing methods, but as suggested above, does have a draw(...) method and can use this to draw itself. It also has a public boolean contains(Point p) method that the main program can use in its MouseListener to decide if it's been clicked:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class SimpleCellGrid extends JPanel {
private static final int ROWS = 40;
private static final int COLS = 40;
private static final int CELL_WIDTH = 10;
private static final int PREF_W = CELL_WIDTH * COLS;
private static final int PREF_H = CELL_WIDTH * ROWS;
private SimpleCell[][] cellGrid = new SimpleCell[ROWS][COLS];
public SimpleCellGrid() {
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
for (int row = 0; row < cellGrid.length; row++) {
for (int col = 0; col < cellGrid[row].length; col++) {
int x = col * CELL_WIDTH;
int y = row * CELL_WIDTH;
cellGrid[row][col] = new SimpleCell(x, y, CELL_WIDTH);
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (SimpleCell[] cellRow : cellGrid) {
for (SimpleCell simpleCell : cellRow) {
simpleCell.draw(g2);
}
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
for (SimpleCell[] cellRow : cellGrid) {
for (SimpleCell simpleCell : cellRow) {
if (simpleCell.contains(e.getPoint())) {
simpleCell.setAlive(!simpleCell.isAlive());
}
}
}
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("SimpleCellGrid");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new SimpleCellGrid());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
public class SimpleCell {
private static final Color CELL_COLOR = Color.RED;
private boolean alive = false;
private int x;
private int y;
private int width;
private Rectangle rectangle;
public SimpleCell(int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
rectangle = new Rectangle(x, y, width, width);
}
public boolean isAlive() {
return alive;
}
public void setAlive(boolean alive) {
this.alive = alive;
}
public void draw(Graphics2D g2) {
if (alive) {
g2.setColor(CELL_COLOR);
g2.fill(rectangle);
}
}
public boolean contains(Point p) {
return rectangle.contains(p);
}
#Override
public String toString() {
return "SimpleCell [alive=" + alive + ", x=" + x + ", y=" + y + ", width=" + width + ", rectangle=" + rectangle
+ "]";
}
}
I'm trying to create a grid of customs JComponents. I want the grid being resizable without deform on my JComponent.
But I am only able to get a grid with clipped JComponent and bad resizing.
What I get :
What I want :
Here is my code :
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import javax.swing.*;
public class Pawn extends JComponent {
private static Color pileColor = Color.LIGHT_GRAY;
private static Color faceColor = Color.DARK_GRAY;
private static Color mouseOverColor = new Color(255, 255, 127);
private static int padding = 10;
private String label;
private boolean pawnState;
private int radius;
private int row;
private int column;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame testPawnFrame = new JFrame("Test de la classe Pawn");
JPanel testPawnPanel = new JPanel();
testPawnPanel.setLayout(new GridLayout(3, 3,Pawn.padding*2,Pawn.padding*2));
for (int i = 0; i < 9; i++) {
testPawnPanel.add(new Pawn());
}
testPawnFrame.add(testPawnPanel);
addQuit(testPawnFrame);
testPawnFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testPawnFrame.pack();
testPawnFrame.setVisible(true);
}
public Pawn() {
this.pawnState = true;
this.radius = 50;
this.row = -1;
this.column = -1;
}
public Pawn(int row, int column) {
this();
this.row = row;
this.column = column;
}
public void setPosition(int row, int column) {
if (row >= 0 && column >= 0) {
this.row = row;
this.column = column;
} else {
throw new Error("La position donee est incorrecte.");
}
}
public void paint(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Ellipse2D circle = new Ellipse2D.Double(padding, padding, radius * 2, radius * 2);
if (pawnState) {
g2.setColor(pileColor);
} else {
g2.setColor(faceColor);
}
g2.fill(circle);
g2.setColor(Color.BLACK);
g2.draw(circle);
g2.drawRect(0, 0, 2 * (radius + padding), 2 * (radius + padding));
}
public Dimension getPreferredSize() {
return new Dimension(2 * (radius + padding) , 2 * (radius + padding) );
}
public Dimension getMinimumSize() {
return new Dimension(2 * (radius + padding) , 2 * (radius + padding) );
}
public void setBounds(int x, int y, int width, int height) {
radius = (int) Math.min(width, height) / 2;
super.setBounds(x, y, width+(padding*2), height+(padding*2));
repaint();
}
public static void addQuit(JFrame frame) {
ActionListener actionQuit = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
};
Box quitBox = new Box(BoxLayout.X_AXIS);
frame.add(quitBox, BorderLayout.SOUTH);
JButton quitButton = new JButton("Quit");
quitButton.addActionListener(actionQuit);
quitBox.add(Box.createHorizontalGlue());
quitBox.add(quitButton);
}
}
Edit :
I done some improvement to the code, thanks you all.
It's not perfect and it's not really optimized ,the resizing is pretty slow.
(if you have idea for optimize please tell me !)
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import javax.swing.*;
public class Pawn extends JComponent {
private static Color pileColor = Color.LIGHT_GRAY;
private static Color faceColor = Color.DARK_GRAY;
private static Color mouseOverColor = new Color(255, 255, 127);
private String label;
private boolean pawnState;
private int radius;
private double padding;
private int row;
private int column;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame testPawnFrame = new JFrame("Test de la classe Pawn");
JPanel testPawnPanel = new JPanel();
testPawnPanel.setLayout(new GridLayout(3, 3));
for (int i = 0; i < 9; i++) {
testPawnPanel.add(new Pawn());
}
testPawnFrame.add(testPawnPanel);
addQuit(testPawnFrame);
testPawnFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testPawnFrame.pack();
testPawnFrame.setVisible(true);
}
public Pawn() {
this.pawnState = true;
this.radius = 50;
this.padding = 0.1;
this.row = -1;
this.column = -1;
}
public Pawn(int row, int column) {
this();
this.row = row;
this.column = column;
}
public void setPosition(int row, int column) {
if (row >= 0 && column >= 0) {
this.row = row;
this.column = column;
} else {
throw new Error("La position donee est incorrecte.");
}
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
Dimension size = getSize();
radius = (int)(
(Math.min(size.width, size.height) / 2)
-(radius*padding));
Ellipse2D circle = new Ellipse2D.Double(
(size.width/2)-radius,
(size.height/2)-radius,
radius * 2, radius * 2);
if (pawnState) {
g2.setColor(pileColor);
} else {
g2.setColor(faceColor);
}
g2.fill(circle);
g2.setColor(Color.BLACK);
g2.draw(circle);
g2.drawRect(0, 0, getWidth()-1, getHeight()-1);
}
public Dimension getPreferredSize() {
int size = 2 * (radius);
return new Dimension(size, size);
}
public static void addQuit(JFrame frame) {
ActionListener actionQuit = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
};
Box quitBox = new Box(BoxLayout.X_AXIS);
frame.add(quitBox, BorderLayout.SOUTH);
JButton quitButton = new JButton("Quit");
quitButton.addActionListener(actionQuit);
quitBox.add(Box.createHorizontalGlue());
quitBox.add(quitButton);
}
}
That what i get now :
//super.paint(g);
super.paintComponent(g);
As already mentioned you need to invoke super on the method your are overriding
Don't override setBounds(). It is the job of the layout manager to determine the size/location of a component. This is the main problem with your code.
Some other problems:
There is no need to override getMinimumSize(). If you did, you would simply use:
return getPreferredSize();
Don't duplicate code if you don't have to.
return new Dimension(2 * (radius + padding) , 2 * (radius + padding) );
Don't repeat calculations. Create a variable. It is easier to debug and change.
//return new Dimension(2 * (radius + padding) , 2 * (radius + padding) );
int size = 2 * (radius + padding) + 1;
return new Dimension(size, size);
Note the "+1". I added this because the way the drawRect(...)method works. Try the code without the "+1" and you will see the bottom/right lines are missing. This is a perfect example of why you use variable to hold calculations. The change only needs to be done in one place.
Edit:
One more change:
//testPawnPanel.setLayout(new GridLayout(3, 3,Pawn.padding*2,Pawn.padding*2));
testPawnPanel.setLayout(new GridLayout(3, 3));
You don't want any space between your components, so let the layout manger do its job.
I have a class called BoardSquare that is an inherited class of JButton. Each of the BoardSquare objects is stored in an array BoardSquare[][] boardsquares. I have used the following code
BoardSquare.boardSquares[j][i].add(new JLabel((j+1)+":"+(i+1)));
to add labels to each of the squares in the array according to their coordinates. I need them to have these labels(I think) so that I can identify them and addActionListeners, etc. How do I make the JLabels invisible so they don't show up in my JFrame?
Alternatively, how can I make the JLabel of each button an instance variable so that I can call JLabel.setVisible(false) but still use them when I add action listeners?
EDIT: If anyone's interested, it's for a Checkers Game.
Here are my classes:
GameWindow
BoardSquare
Checker
MyListener
Thank you for the edit. If this were my application, I'd probably do things very differently including,
Use a grid of JLabels not JButtons. I see no need to use JButtons, and a problem in that the button would not be as visually appealing as other possible solutions.
Either give the JLabel cells or the containing JPanel a MouseListener,
Have the JPanel cells have no Icon and thus be empty if no checker is on them,
Or have them hold an ImageIcon of an appropriately colored checker if they are not empty.
You could even animate the GUI by using the glass pane to hold a JPanel with an appropriate checker ImageIcon that the user can drag.
Note that if you absolutely have to use JButtons, then don't add JLabels to them. Instead simply set the JButton's Icon to null or to an appropriate Checker ImageIcon.
Edit
For example, a bad code example as a proof of concept. Try compiling and running this.
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.*;
#SuppressWarnings("serial")
public class Checkers extends JPanel {
public static final int SIDE_LENGTH = 60;
public static final int ROW_COUNT = 8;
private static final String ROW = "row";
private static final String COLUMN = "column";
private static final Color LIGHT_COLOR = new Color(210, 180, 140);
private static final Color DARK_COLOR = new Color(107, 68, 35);
private Map<Checker, Icon> checkerIconMap = new EnumMap<Checker, Icon>(
Checker.class);
private JLabel[][] labelGrid = new JLabel[ROW_COUNT][ROW_COUNT];
private Checker[][] checkerGrid = new Checker[ROW_COUNT][ROW_COUNT];
public Checkers() {
for (Checker checker : Checker.values()) {
checkerIconMap.put(checker, createCheckerIcon(checker));
}
setLayout(new GridLayout(ROW_COUNT, ROW_COUNT));
for (int row = 0; row < labelGrid.length; row++) {
for (int col = 0; col < labelGrid[row].length; col++) {
checkerGrid[row][col] = Checker.EMPTY;
JLabel gridCell = new JLabel(checkerIconMap.get(Checker.EMPTY));
gridCell.setOpaque(true);
gridCell.putClientProperty(ROW, row);
gridCell.putClientProperty(COLUMN, col);
Color c = row % 2 == col % 2 ? LIGHT_COLOR : DARK_COLOR;
gridCell.setBackground(c);
add(gridCell);
labelGrid[row][col] = gridCell;
}
}
for (int i = 0; i < labelGrid.length / 2 - 1; i++) {
for (int j = 0; j < labelGrid.length / 2; j++) {
int row = i;
int col = j * 2;
col += row % 2 == 0 ? 1 : 0;
labelGrid[row][col].setIcon(checkerIconMap.get(Checker.BLACK));
checkerGrid[row][col] = Checker.BLACK;
row = ROW_COUNT - row - 1;
col = ROW_COUNT - col - 1;
labelGrid[row][col].setIcon(checkerIconMap.get(Checker.RED));
checkerGrid[row][col] = Checker.RED;
}
}
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
private Icon createCheckerIcon(Checker checker) {
BufferedImage img = new BufferedImage(SIDE_LENGTH, SIDE_LENGTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(checker.getColor());
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int x = 3;
int y = x;
int width = SIDE_LENGTH - 2 * x;
int height = width;
g2.fillOval(x, y, width, height);
g2.dispose();
return new ImageIcon(img);
}
private class MyMouseAdapter extends MouseAdapter {
private int selectedRow = -1;
private int selectedCol = -1;
private Checker selectedChecker = null;
private JPanel glassPane = null;
private Point p = null;
private JLabel movingLabel = new JLabel(checkerIconMap.get(Checker.EMPTY));
public MyMouseAdapter() {
movingLabel.setSize(movingLabel.getPreferredSize());
movingLabel.setVisible(false);
}
#Override
public void mousePressed(MouseEvent e) {
p = e.getPoint();
for (int row = 0; row < labelGrid.length; row++) {
for (int col = 0; col < labelGrid[row].length; col++) {
JLabel gridCell = labelGrid[row][col];
if (gridCell == getComponentAt(p)) {
if (checkerGrid[row][col] != Checker.EMPTY) {
selectedRow = row;
selectedCol = col;
selectedChecker = checkerGrid[row][col];
checkerGrid[row][col] = Checker.EMPTY;
labelGrid[row][col].setIcon(checkerIconMap.get(Checker.EMPTY));
JRootPane rootPane = SwingUtilities.getRootPane(Checkers.this);
glassPane = (JPanel) rootPane.getGlassPane();
glassPane.setVisible(true);
glassPane.setLayout(null);
movingLabel.setIcon(checkerIconMap.get(selectedChecker));
movingLabel.setVisible(true);
glassPane.add(movingLabel);
int x = p.x - SIDE_LENGTH / 2;
int y = p.y - SIDE_LENGTH / 2;
movingLabel.setLocation(x, y);
}
}
}
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (selectedChecker == null) {
return;
}
p = e.getPoint();
if (!Checkers.this.contains(p)) {
// if mouse releases and is totally off of the grid
returnCheckerToOriginalCell();
clearGlassPane();
return;
}
for (int row = 0; row < labelGrid.length; row++) {
for (int col = 0; col < labelGrid[row].length; col++) {
JLabel gridCell = labelGrid[row][col];
if (gridCell == getComponentAt(p)) {
if (isMoveLegal(row, col)) {
checkerGrid[row][col] = selectedChecker;
labelGrid[row][col].setIcon(checkerIconMap.get(selectedChecker));
// todo: check for jumped pieces...
} else {
// illegal move
returnCheckerToOriginalCell();
}
}
}
}
clearGlassPane();
}
// this code would go in the model class
private boolean isMoveLegal(int row, int col) {
if (checkerGrid[row][col] != Checker.EMPTY) {
// trying to put a checker on another checker
returnCheckerToOriginalCell();
} else if (row == selectedRow && col == selectedCol) {
// trying to put checker back in same position
returnCheckerToOriginalCell();
} else if (row % 2 == col % 2) {
// invalid square
returnCheckerToOriginalCell();
} else {
// TODO: more logic needs to go here to test for a legal move
// and to remove jumped pieces
return true;
}
return false;
}
#Override
public void mouseDragged(MouseEvent e) {
if (selectedChecker == null || p == null) {
return;
}
p = e.getPoint();
int x = p.x - SIDE_LENGTH / 2;
int y = p.y - SIDE_LENGTH / 2;
movingLabel.setLocation(x, y);
}
private void clearGlassPane() {
glassPane.setVisible(false);
movingLabel.setVisible(false);
selectedChecker = null;
p = null;
selectedCol = -1;
selectedRow = -1;
}
private void returnCheckerToOriginalCell() {
checkerGrid[selectedRow][selectedCol] = selectedChecker;
labelGrid[selectedRow][selectedCol].setIcon(checkerIconMap.get(selectedChecker));
}
}
private static void createAndShowGui() {
Checkers mainPanel = new Checkers();
JFrame frame = new JFrame("JLabelGrid");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class CheckerModel {
}
enum Checker {
EMPTY(new Color(0, 0, 0, 0)), RED(Color.red), BLACK(Color.black);
private Color color;
private Checker(Color color) {
this.color = color;
}
public Color getColor() {
return color;
}
}
Better Model-View example being worked on...
Instead of adding the new Jlabel() directly to the array , make an instance first then add that ... i guess this is done in a loop so for example :
JLabel lbl;
for(....) {
lbl = new JLabel(new JLabel((j+1)+":"+(i+1));
lbl.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent evt()){
// TODO here
}
});
BoardSquare.boardSquares[j][i].add(lbl);