I'm developing an application on my Java classes and hit a wall with a strange issue. I need to represent data in a grid, so using GridLayout is an obvious choice, but here's a problem. I keep on getting almost empty frame (notice tiny white rectangle in top left corner).
Here's a code snippet producing this result
//not important class code
public static void main(String args[]) {
JFrame frame = new JFrame("Wolves & Rabbits");
frame.setSize(640, 480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//want to create a 12x9 grid with 2 black and 4 pink rectangles
Board board = new Board(12, 9, 2, 4, 1000);
frame.add(board);
frame.setResizable(false);
frame.setVisible(true);
}
//Board.java (Board class) extends JPanel
public JPanel fields[][];
private Integer boardWidth, boardHeight;
private ArrayList<AnimalThread> animals;
private Integer wolvesCount, rabbitsCount;
public Board(int w, int h) {
super(new GridLayout(h, w, 4, 4));
fields = new JPanel[w][h];
boardWidth = new Integer(w);
boardHeight = new Integer(h);
animals = null;
wolvesCount = new Integer(0);
rabbitsCount = new Integer(0);
//creating white rectangles
for (int i = 0; i < boardHeight; i++)
for (int j = 0; j < boardWidth; j++) {
fields[j][i] = new JPanel(true);
fields[j][i].setBackground(AnimalThread.NONE);
this.add(fields[j][i]);
}
AnimalThread.setLinkToBoard(this);
}
public Board(int w, int h, int wolves, int rabbits, int k) {
this(w, h);
animals = new ArrayList<AnimalThread>();
while (boardWidth*boardHeight < 2*wolves*rabbits) {
wolves--;
rabbits--;
}
wolvesCount = wolves;
rabbitsCount = rabbits;
WolfThread.setRabbitsCount(rabbitsCount);
//randomly place colored rectangles
this.randomize(wolves, rabbits, k);
}
The strange thing is that not changing Board class at all and with a little change in main method I was able to display the proper grid.
In this case the main method is
//not important class code
public static void main(String args[]) {
JFrame frame = new JFrame("Wolves & Rabbits");
frame.setSize(640, 480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Board board = new Board(12, 9, 2, 4, 1000);
//THE CHANGE!
JPanel panel = new JPanel(new GridLayout(12, 9, 4, 4));
for (int i = 0; i < 9; i++)
for (int j = 0; j < 12; j++) {
JPanel tmp = board.fields[j][i];
panel.add(tmp);
}
frame.add(panel);
frame.setResizable(false);
frame.setVisible(true);
}
Anyone has idea of what's causing this irritating issue? Any clue would be appreciated.
When you are working with Swing you must execute the Swing UI code on the EDT. So at the very least your main method should look like this:
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Wolves & Rabbits");
frame.setSize(640, 480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// want to create a 12x9 grid with 2 black and 4 pink rectangles
Board board = new Board(12, 9, 2, 4, 1000);
frame.add(board);
frame.setResizable(false);
frame.setVisible(true);
}
});
}
Accessing variables which are Swing objects from an other thread then the EDT will cause problems. Many of these problems will be intermittent and hard to trace (like most concurrency problems).
The name of the 'AnimalThread' object seems to imply its a Thread. You can't (well actually you can as you demonstrated :-) directly Swing objects which 'live' on the EDT. If another Thread wants to change something on the EDT it needs to use the 'SwingUtilities.invokeLater' or 'SwingUtilities.invokeAndWait' method.
Related
I am trying to make a chessboard, that will randomize the place of its pieces throughout the board.
Below is what I have so far
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class ChessBoard extends JFrame {
JLayeredPane layeredpane;
JPanel chessboard;
JButton[][] chessboardButtons;
Color black;
JLabel [][] chessboardLabels;
UIManager Ui;
ChessBoard() {
Dimension Size = new Dimension(600, 600);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
pack();
setResizable(true);
setLocationRelativeTo(null);
setVisible(true);
setSize(600, 600);
setTitle("Chess Board");
layeredpane = new JLayeredPane();
getContentPane().add(layeredpane);
layeredpane.setPreferredSize(Size);
chessboard = new JPanel();
layeredpane.add(chessboard, JLayeredPane.DEFAULT_LAYER);
chessboard.setLayout(new GridLayout(8, 8));
chessboard.setPreferredSize(Size);
chessboard.setBounds(0, 0, Size.width, Size.height);
Ui = new UIManager();
chessboardButtons = new JButton[8][8];
black = Color.black;
ButtonHandler handler = new ButtonHandler();
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
chessboardButtons[i][j] = new JButton();
chessboardButtons[i][j].setBorderPainted(false);
if ((i + j) % 2 != 0) {
chessboardButtons[i][j].setBackground(black);
chessboardButtons[i][j].setOpaque(true);
}
chessboard.add(chessboardButtons[i][j]);
chessboardButtons[i][j].addActionListener(handler);
}
}
chessboardLabels = new JLabel[8][8];
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
chessboardLabels[i][j] = new JLabel();
chessboardLabels[i][j].setFont(new Font("Ariel", Font.BOLD, 20));
chessboardLabels[i][j].setText("H");
chessboardLabels[i][j].setHorizontalAlignment(JLabel.CENTER);
chessboardLabels[i][j].setVerticalAlignment(JLabel.CENTER);
chessboardLabels[i][j].setOpaque(true);
chessboardButtons[i][j].add(chessboardLabels[i][j]);
if(chessboardButtons[i][j].getBackground() == Color.black) {
chessboardLabels[i][j].setBackground(Color.black);
chessboardLabels[i][j].setForeground(Color.white);
}
}
}
}
private class ButtonHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == chessboardButtons[0][0]) {
System.out.println("Button 0,0");
}
if (e.getSource() == chessboardButtons[0][1]) {
System.out.println("Button 0,1");
}
}
}
}
Right now I have the letter H filling all my buttons. What I need it to do is to:
limit the number of "H"'s there are on the board to be 32, 16 "White" and 16 "black" and
randomize the placement throughout the board.
Any ideas will be helpful!
I tried exactly this years ago, and I ran into several problems. One of the biggest is that dragging a piece from one square (JComponent) to another hard to do, because each JComponent has its bounds, and its Graphics clipping prevents you from drawing outside of these bounds. There are workarounds, like adding an Image to a higher level in the JLayeredPane, but this is still very hard to get right.
You really want to make you GUI a custom JComponent/JPanel that draws the whole board and can get mouse events across the whole board.
But before that, the right place to start is by creating a ChessModel that encapsulates the logic of the game. If you do that first and test it thoroughly, adding a GUI on top of that is easier than the other way around.
public class ChessModel {
char[][] board = new char[8][8];
...
public Point[] getPossibleMoves(Point pieceLocation) {
...
}
}
Create an ArrayList with 64 Integers numbered from 0 - 63
Use Collections.shuffle(...) to shuffle the numbers randomly
Take the first 16 values from the ArrayList and add white pieces to the board based on the integer value.
Take the next 16 values from the ArrayList and add the black pieces to the board.
First, I would start by decoupling parts of the system, this will provide you with a lot more flexibility.
For example, you want to decouple the "visual" from the "virtual". Components aren't managed in a "grid", instead, they are maintained in a list, so, you want some way you can quickly and easily ascertain the location of various components on the screen and how the relate to the "virtual" concept of the game or grid.
This is at the core concept of "model-view-controller", where the model represents a "grid" of "pieces" and the view is used to visually represent the model to the user. So you end up with a little bit of translation going on.
Now, you can do something like...
int row = (value / 8);
int col = (value % 8);
which, given a component index, will give you the row/col that they represent, but I'm also lazy 😉, so, I'm going to isolate the concept of a Piece...
public class Piece extends JPanel {
private JLabel label;
private Point cell;
public Piece(int index) {
setLayout(new GridBagLayout());
label = new JLabel(Integer.toString(index));
label.setForeground(Color.RED);
add(label);
setOpaque(false);
}
public void setCell(Point cell) {
this.cell = cell;
}
public Point getCell() {
return cell;
}
}
This does several things for me, first, it gives me a simple building block which can be used to represent a piece of data as well as maintain the "virtual" position of the piece, so I can easily look it up, independently.
It also divorces the piece from the board, which will make it easier to maintain (IMHO).
Next, I build the board...
setLayout(new GridLayout(8, 8));
int index = 0;
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
JPanel panel = new JPanel(new GridBagLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
};
// Indexed via x/y
cells[col][row] = panel;
if (index % 2 == 0) {
panel.setBackground(Color.WHITE);
} else {
panel.setBackground(Color.BLACK);
}
add(panel);
index++;
}
index++;
}
There's nothing really fancy here, it's just GridLayout with a bunch of color panels laid out on it.
The "fancy" part is the idea that, instead of using something complicated, like JLayeredPane, I'm simply going to add the Pieces directly to each cell.
Which leads us to the heart of the problem, how to randomise the position of the cells. Essentially, I'm going to create a list of numbers from 0 to 63 inclusive, randomise the list and then pop each number of off the list till I'm done.
Now, you could use an array, but filling an array with random numbers is not a simple task (especially if you want to guarantee uniqueness 😉)
// Fill a list of numbers
int totalCells = 8 * 8;
List<Integer> locations = new ArrayList<>(totalCells);
for (int value = 0; value < totalCells; value++) {
locations.add(value);
}
// Randomise the list
Collections.shuffle(locations);
// For al the white pieces, randomise their positions
for (index = 0; index < 16; index++) {
int value = locations.remove(0);
// Virtual coordinates
int row = (value / 8);
int col = (value % 8);
Point cell = new Point(col, row);
Piece piece = new Piece(index);
whitePieces[index] = piece;
piece.setCell(cell);
// Get the component offset by the value (physical)
JPanel cellPane = (JPanel) getComponent(value);
cellPane.add(piece);
}
// Now you can continue with the black pieces, just like above
// and because you've removed the "used" cell indexes from the
// list, you won't end up with duplicate positions
Now you're probably scratching your head over all this. But simply put, when you want to move a "piece", you simply remove it from it's current parent container, calculate the position ((row * 8) + col), get the new parent component (like above) and add it, simple.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Test extends JFrame {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel[][] cells = new JPanel[8][8];
private Piece[] whitePieces = new Piece[16];
public TestPane() {
setLayout(new GridLayout(8, 8));
int index = 0;
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
JPanel panel = new JPanel(new GridBagLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
};
// Indexed via x/y
cells[col][row] = panel;
if (index % 2 == 0) {
panel.setBackground(Color.WHITE);
} else {
panel.setBackground(Color.BLACK);
}
add(panel);
index++;
}
index++;
}
int totalCells = 8 * 8;
List<Integer> locations = new ArrayList<>(totalCells);
for (int value = 0; value < totalCells; value++) {
locations.add(value);
}
Collections.shuffle(locations);
for (index = 0; index < 16; index++) {
int value = locations.remove(0);
int row = (value / 8);
int col = (value % 8);
Point cell = new Point(col, row);
Piece piece = new Piece(index);
whitePieces[index] = piece;
piece.setCell(cell);
JPanel cellPane = (JPanel) getComponent(value);
cellPane.add(piece);
}
}
}
public class Piece extends JPanel {
private JLabel label;
private Point cell;
public Piece(int index) {
setLayout(new GridBagLayout());
label = new JLabel(Integer.toString(index));
label.setForeground(Color.RED);
add(label);
setOpaque(false);
}
public void setCell(Point cell) {
this.cell = cell;
}
public Point getCell() {
return cell;
}
}
}
Oh, and just in case it's important, you can make use of component based drag-n-drop as well
How to make draggable components with ImageIcon
JLayeredPanel layout manager free moving objects
But, needs drives wants 😉
I am visually plotting 100 random points in a Java.awt panel (as far as i know) but it is not working so smoothly. The pane has to be maximized by the user before they show up. Im not sure which command I am missing to make this more fluid
The 100 x,y coordinates are generated randomly and sent to a JFrame in this file.
CC_simplePerceptron.Java
import java.awt.*; // Using AWT's Graphics and Color abstract window toolkit
import java.awt.event.*; // Using AWT event classes and listener interfaces
import javax.swing.*; // Using Swing's components and containers
import javax.swing.*;
import java.awt.geom.Ellipse2D;
import Components.Perceptron;
import Components.Point;
public class CC_SimplePerceptron extends JComponent {
public static final int maxD = 800;
public static Perceptron p = new Perceptron();
public static Point[] points = new Point[100]; //100 element array of type Point to hold data
public static void main(String[] args){
JFrame frame = new JFrame("Draw Ellipse Demo");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new CC_SimplePerceptron());
frame.pack();
frame.setSize(new Dimension(maxD, maxD));
frame.setVisible(true);
System.out.println("Point initialiations");
//initializing 100 random points
for(int i = 0; i < points.length; i++){
points[i] = new Point(); //random point
System.out.println("Point " + i + " =" + points[i].getX() + ", " + points[i].getY());
}
float[] inputs = {-1f,0.5f}; //0.5f to indicate its float not double
int guess = p.guess(inputs);
System.out.println(guess);
return;
}
// Constructor to set up the GUI components and event handlers
public CC_SimplePerceptron() {
System.out.println("Def constructor");
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.RED);
g2.setStroke(new BasicStroke(5.0f));
for(int i = 0; i < points.length; i++){
g2.fill(new Ellipse2D.Double(points[i].getX(), points[i].getY(), 8, 8));
}
}
}
The imported files "Perceptron" & "Point" are not relevant for this question scope, but can be found here if one wants to run the code. Any thoughts on why the pane doesnt display all points right away? Im not exactly sure how my paint method works, and why it is called with a graphics obj, is this the best method to plot my x,y coordinates in a java program on the basis of convience?
Take frame.setVisible(true); and call it last, after you've built all the data you want to display...
public static void main(String[] args){
JFrame frame = new JFrame("Draw Ellipse Demo");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new CC_SimplePerceptron());
frame.pack();
frame.setSize(new Dimension(maxD, maxD));
System.out.println("Point initialiations");
//initializing 100 random points
for(int i = 0; i < points.length; i++){
points[i] = new Point(); //random point
System.out.println("Point " + i + " =" + points[i].getX() + ", " + points[i].getY());
}
float[] inputs = {-1f,0.5f}; //0.5f to indicate its float not double
int guess = p.guess(inputs);
System.out.println(guess);
frame.setVisible(true);
}
If you want to dynamically update the UI, then, in your case, calling repaint on the component you want updated should also work...
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Draw Ellipse Demo");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
CC_SimplePerceptron component = new CC_SimplePerceptron();
frame.getContentPane().add(component);
frame.pack();
frame.setSize(new Dimension(maxD, maxD));
frame.setVisible(true);
System.out.println("Point initialiations");
//initializing 100 random points
for(int i = 0; i < points.length; i++){
points[i] = new Point(); //random point
System.out.println("Point " + i + " =" + points[i].getX() + ", " + points[i].getY());
}
float[] inputs = {-1f,0.5f}; //0.5f to indicate its float not double
int guess = p.guess(inputs);
System.out.println(guess);
component.repaint();
}
})
}
Other considerations
As general recommendation, you should be overriding paintComponent and paint and you should be calling the super paint method before performing any custom painting to ensure that the paint chain remains intact.
You should also override getPreferredSize and return an appropriate size hint, this will provide pack with better information when it calculates the size of the window
First, override paintComponent and put your paint code in there. Don't forget to use super.paintComponent(g) at the beginning so that it clears the panel before painting. If you make your CC_SimplePerceptron extend JPanel, you can set it as the content pane:
frame.setContentPane(new CC_SimplePerceptron)
so it fills the frame. Finally, use setPreferredSize() on the panel before you call frame.pack()
I have a JPanel 7width 9 height board. I can also place my pieces on top of the board. My problem now is how I will call the pieces:
pseudo code:
if(whero1 is on row 0 column 5 then....
code is below:
public class Board extends JPanel{
private static final String imageFolderPath = "src/resources/images/";
Dimension dimension = new Dimension(500, 500);
JPanel board;
JLabel piece;
MovePiece mp = new MovePiece(this);
public Board(){
//set size of panel;
this.setPreferredSize(dimension);
this.addMouseListener(mp);
this.addMouseMotionListener(mp);
//create the Board
board = new JPanel();
board.setLayout(new GridLayout(9,7));
board.setPreferredSize(dimension);
board.setBounds(0, 0, dimension.width, dimension.height);
this.add(board);
for (int i = 0; i < 63; i++) {
JPanel square = new JPanel(new BorderLayout());
square.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
board.add(square);
square.setBackground(new Color(185, 156, 107));
}
JLabel whero1 = new JLabel( new ImageIcon(imageFolderPath+"/pieces/bhero.png") );
JPanel panel = (JPanel)board.getComponent(60);
panel.add(whero1);
//I'm trying this, but i.m going nowhere
int x =whero1.getParent().getX();
int y = whero1.getParent().getY();
System.out.println(x);
System.out.println(y);
/*if(x==0&&y==0){
whero1.setIcon(new ImageIcon(imageFolderPath+"/pieces/bdg.png"));
}*/
}
}
The easiest solution would be to maintain some kind of virtual model of the board. In this way you could simply update the state of the game and request that the UI update itself to reflect the state of the model.
Much simpler then trying to interrogate n-depth contains and convert to/from coordinate systems
nb: This...
int x =whero1.getParent().getX();
int y = whero1.getParent().getY();
Is going to return the pixel x/y position of the whereo1s parent's in relation to it's parent container, not convinced that this would really help at all
I am creating a matching game using Netbeans, but not the GUI editor (it sucks). So, basically, I created a new class, called Card, that extends the JButton class. Upon construction, the button's size is set to 100px by 100px and an icon is set. When I add the button to a JPanel in a GridBagLayout, it is not the intended size.
Here is some of my code:
JFRAME CLASS:
package matchinggame;
... imports ...
public class MatchingGameWindow extends JFrame {
Card[] cards = new Card[16]; //16 game cards
public MatchingGameWindow() {
...
//Create new game panel (for the cards)
JPanel gamePanel = new JPanel(new GridBagLayout());
//gamePanel.setSize(500,500); removed as it is not needed.
...
this.add(gamePanel, BorderLayout.CENTER);
//Create 16 card objects
cards = createCards();
//Create new grid bag constraints
GridBagConstraints gbc = new GridBagConstraints();
//Add the cards to the game panel
int i=0;
for (int y = 0; y < 4; y++) {
gbc.gridy = y;
for (int x = 0; x < 4; x++) {
gbc.gridx = x;
gamePanel.add(cards[i], gbc);
i++;
}
}
}
public final Card[] createCards() {
Card[] newCards = new Card[16];
//New choices array
ArrayList<Integer> choices = new ArrayList();
int[] choiceValues = {0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7};
//Add the initial choice values to the arraylist
for (int i=0; i < choiceValues.length; i++) {
choices.add(choiceValues[i]);
}
//Create 16 cards
for (int i=0; i < 16; i++) {
//Initial value of -1 for error checking
int iconIndex = -1;
//Loop until card is created
while (iconIndex == -1) {
//Get a random number from 0 - 7
Random r = new Random();
int rInt = r.nextInt(8);
//If the random number is one of the choices
if (choices.contains(rInt)) {
//the icon # will be the random number
iconIndex = rInt;
//Get rid of that choice (it is used up)
choices.remove(new Integer(rInt));
//Create a new Card in the Card[]
newCards[i] = new Card(i,iconIndex);
//If all the choices are gone
} else if (choices.isEmpty()){
iconIndex = -1; //done creating this card (breaks out of loop)
}
}
}
//Return the created cards
return newCards;
}
}
CARD CLASS:
package matchinggame;
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class Card extends JButton {
final static ImageIcon defaultIcon = new ImageIcon("cardback.jpg");
ImageIcon secretIcon;
boolean isRevealed = false;
...
public Card(final int cardIndex, int secretIconIndex) {
//Size is 100px by 100px
setSize(100, 100);
//Default icon is card back image
setIcon(defaultIcon);
//Get the secret icon behind the back of the card
secretIcon = icons[secretIconIndex];
}
}
And using this code I get a result of this:
Any ideas as to what I am doing wrong here?
EDIT:
I overrided the getPreferredSize method like Hovercraft Full Of Eels said, and it worked!
I added this code in the Card class:
#Override
public Dimension getPreferredSize() {
return new Dimension(100,100);
}
and got my desired result:
Now I must be doing something wrong with the icons, as they are not showing up as they should.
You should not use setSize(...) in the class's constructor but rather override the class's getPreferredSize() method to return a Dimension(100, 100). And in fact you should have setSize(...) no-where in your program. Instead use decent layout managers, call pack() on the JFrame after adding all components and before setting it visible, and let the layout managers size the GUI appropriately.
I'm building a Tic Tac Toe game in Java with a Swing GUI, and it renders correctly in Ubuntu 10.4 and Windows XP. This is how it looks like in Ubuntu:
When I copied the bin-folder with all the class files and tried to run the program in Windows 7 it looked like this instead:
I just can't understand what's wrong. As I said, it works perfectly in Ubuntu 10.4 and Windows XP.
I would be very happy if someone could help me out! I'll post the code related to the GUI, just in case it is needed to solve the problem.
Here is the code I use to initialize the GUI:
//Initializing GUI.
frame = new JFrame(); //Creating the window.
frame.setTitle("Tic Tac Toe"); //Setting the title of the window.
frame.addMouseListener(this);
frame.getContentPane().add(BorderLayout.CENTER, grid.getPanel()); //Adding the grid panel.
info = new JLabel(" Initializing game..."); //Creating info text.
frame.getContentPane().add(BorderLayout.SOUTH, info); //Adding info text.
//Setting GUI properties.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
The panel with the grid itself is created in my GameGrid class, which have a method "JPanel getPanel()". Here is the initialization of that panel (the code belongs in the constructor of GameGrid):
GridBox temp;
layout = new GridLayout(getHeight(), getWidth());
panel = new JPanel(layout);
panel.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Click in a box to place a marker:"),
BorderFactory.createEmptyBorder(5,5,5,5)));
//Creating a GridBox for each cell, and adding them to the panel in the right order..
for(int i = 0; i < getHeight(); i++) {
for(int j = 0; j < getWidth(); j++) {
temp = new GridBox(j, i);
temp.addMouseListener(listener);
panel.add(temp);
}
}
GridBox is a subclass of JPanel, which I modified to automatically show the contents of the grid at the coordinates specified.
class GridBox extends JPanel {
private static final long serialVersionUID = 1L;
int fontsize, x, y, value, signHeight, signWidth;
char print;
FontMetrics fm;
LineMetrics lm;
public GridBox(int a, int b) {
x = a; //TODO - input control
y = b;
}
public Move getMove() {
Move m = new Move(x, y);
return m;
}
public void paintComponent(Graphics g) {
Border blackline = BorderFactory.createLineBorder(Color.black);
setBorder(blackline);
Dimension size = getSize();
Rectangle2D rect;
fontsize = (int)(size.getHeight()*0.75);
value = getGridValue(x, y);
if(value == EMPTY)
print = ' ';
else if(value == 0)
print = 'X';
else if(value == 1)
print = 'O';
else
print = (char)value;
Font font = new Font("Times New Roman", Font.PLAIN, fontsize);
g.setFont(font);
fm = g.getFontMetrics();
rect = fm.getStringBounds(Character.toString(print), g);
signHeight = (int)rect.getHeight();
signWidth = (int)rect.getWidth();
g.setColor(Color.black);
g.drawString(Character.toString(print), (size.width/2)-(signWidth/2), (size.height/2)-(signHeight/2)+fm.getAscent());
}
}
Thanks in advance!
There's an obvious problem in the you change the border whilst repainting the component. That's going to cause all sorts of problems.
Also, I don't see where you paint the background of the panel. You should have
super.paintComponent(g);
at the top of the method.