Ok, so I'm trying to add an array of 64 JButtons to a JFrame with and 8 by 8 grid layout (chess board type thing). Here's the relevant code section:
public class othello implements ActionListener{
int [][] board = new int[8][8];
JFrame window = new JFrame();
JButton[] buttons = new JButton[64];
public othello(){
window.setSize(400,400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLayout(new GridLayout(8,8));
window.setVisible(true);
for (int i=0;i<64;i++){
buttons[i] = new JButton("");
buttons[i].addActionListener(this);
window.add(buttons[i]);
}
for (int i=0;i<8;i++){
for (int j=0;j<8;j++){
board[i][j]=2;
}
}
board[3][3]=0;board[4][4]=0;
board[3][4]=1;board[4][3]=1;
}
public void actionPerformed(ActionEvent e){
for (int i=0;i<8;i++){
for (int j=0;j<8;j++){
if(e.getSource()==buttons[i]){
buttons[i].setEnabled(false);
board[i][j]=1;
check();
}
}
}
}
public static void main (String[] args){
new othello();
}
}
What this code results in is a seemingly random number of buttons actually being added. Occasionally it adds all 64, more commonly it adds perhaps half or so, it always starts properly but stops at an arbitrary point (I tested by having the button labels count up).
I added some println's to see if the loop itself was actually completing, no problem there, it's going round the loop all 64 times, it just stops adding buttons at some point.
I'm something of a beginner at Java so I'm sure it's something really simple and stupid, but I currently have no idea what's going wrong. Can anyone help?
Edited for more code.
Have you initializated the array? like
JButton[] buttons = new JButtons[x];
make some prints to check in what number the loop stops.
You should be adding to a ContentPane not directly to a JFrame.add()
Like all other JFC/Swing top-level containers, a JFrame contains a
JRootPane as its only child. The content pane provided by the root
pane should, as a rule, contain all the non-menu components displayed
by the JFrame.
Are you setting the layout manager to something other than the default?
The default content pane will have a BorderLayout manager set on it.
Refer to RootPaneContainer for details on adding, removing and setting
the LayoutManager of a JFrame.
the proper idiomatic code to add contents is as follows:
window.getContentPane().add(child);
Refer to the Javadoc for details on proper use.
Maybe you are not using the event dispatch thread to manipulate the UI?
From the Swing Tutorial:
The event dispatch thread, where all event-handling code is executed.
Most code that interacts with the Swing framework must also execute on
this thread.
Try to run your UI construction code using SwingUtilities.invokeAndWait().
Edit: the corrected source code would be:
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class othello implements ActionListener
{
int[][] board = new int[8][8];
JFrame window = new JFrame();
JButton[] buttons = new JButton[64];
public othello()
{
try
{
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
window.setSize(400, 400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLayout(new GridLayout(8, 8));
window.setVisible(true);
for (int i = 0; i < 64; i++)
{
buttons[i] = new JButton("");
buttons[i].addActionListener(othello.this);
window.getContentPane().add(buttons[i]);
}
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
board[i][j] = 2;
}
}
board[3][3] = 0;
board[4][4] = 0;
board[3][4] = 1;
board[4][3] = 1;
}
});
}
catch (Exception e)
{
// TODO Handle exception
e.printStackTrace();
}
}
public void actionPerformed(ActionEvent e)
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (e.getSource() == buttons[i])
{
buttons[i].setEnabled(false);
board[i][j] = 1;
// check();
}
}
}
}
public static void main(String[] args)
{
new othello();
}
}
Related
I'm not really keen on Java GUI, but am learning as I go. I am in the process of making a very simple and basic Sudoku puzzle. Right now I am just on the basic layout of it.
I wanted to see if there was a simple way of adding a text field to each little rectangle that I have drawn out (81 total rectangles - 9x9 puzzle). So that a user can type something in there.
I am delving into it, but wanted to get the code up here to see if anyone had any tips, cause truth be told, I am mega lost with this.
Here is what I have so far...
import java.awt.*;
import javax.swing.*;
class MyCanvas extends JComponent {
public void paint(Graphics g) {
int coordinateX = 0;
int coordinateY = 0;
// maximum 9 rows
int rows = 9;
int columns = 1;
// make a 9x9 puzzle
while (columns <= rows) {
//for loop to increment the boxes
for (int j = 1; j <= rows; j++) {
// add and assign coordinte x... equivalent to x = x + 30
coordinateX += 30;
// where x and y determine start of the box and 30 determines the size
g.drawRect(coordinateX, coordinateY, 30, 30);
} //end of for loop
//reset the value of x to start a new row
coordinateX = 0;
coordinateY += 30;
columns++;
} //end of while loop
} //end of void paint
} //end of class
public class DrawRect {
public static void main(String[] a) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(100, 100, 500, 500);
window.getContentPane().add(new MyCanvas());
window.setVisible(true);
} // end of void main
} // end of class
Hopefully someone has some pointers that could help me out, cause boy oh boy do I need it. Was kinda thrown into the lion's den without prior knowledge or practice, but I'm trying hard.
Thanks guys!!
You could use a GridLayout(9,9) and an array of arrays of JTextField's
This is, of course, just an example of how I would do it. There are other ways to do this.
Find below a generic example.
Solution
public static void main(String[] args) {
JTextField[][] boxes = new JTextField[9][9];
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(9,9));
frame.setSize(500, 500);
for (int i = 0 ; i < 9 ; i++){
for (int j = 0 ; j < 9 ; j++){
boxes[i][j] = new JTextField("0");
frame.add(boxes[i][j]);
}
}
frame.setVisible(true);
}
Output
My program is consist of two classes(test and paintClass) in different files. In the paintClass class I draw a 5x5 square board by using paintComponent method. I want to add buttons in each small square in the big square. When I run the code I don't get any buttons. I want to have 25(5x5) buttons by using jpanel on a shape drawn by paintComponent. Is this possible? If it is, how I can do it?
EDIT : The problem was the loop. Number had a default value of 0 so the loop didn't work. I defined number at the beginning. It solved the problem. Also one of the invervals were wrong. I changed j = 0 with j = 1.
import javax.swing.*;
import java.awt.*;
public class test
{
public static void main(String[] args)
{
JFrame frame = new JFrame("buttons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(250,250);
PaintClass paint = new PaintClass();
paint.repaint();
f1.getContentPane().add(paint);
frame.pack();
frame.setVisible(true);
}
}
import javax.swing.*;
import java.awt.*;
public class PaintClass extends JPanel
{
private Graphics g;
private int interval,side,number;
private JButton button;
public PaintClass()
{
number = 5;
button = new JButton();
setLayout(new GridLayout(5,5));
for(int i = 0; i <= number - 1; i++)
{
for(int j = 1; j <= number - 1; j++)
{
button = new JButton();//ADDED
button.setBounds(i * interval, 0, interval, interval);
add(button);
}
button = new JButton();//ADDED
button.setBounds(0, i * interval, interval, interval);
add(button);
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
this.repaint();
side = 250;
number = 5;
interval = side / number;
g.drawRect(0,0, side, side);
for(int i = 0; i <= number - 1; i++)
{
for(int j = 0; j <= number - 1; j++)
{
g.drawLine(i * interval, 0, i * interval, side);
}
g.drawLine(0, i * interval, side, i * interval);
}
}
}
Choose one or the other: either add the buttons using the GridLayout, or paint the buttons using paintComponent. If the former, you should a) define the loop constraint (right now it is 0) b) create a new JButton for every loop (your code currently reuses the instance) and c) register the appropriate ActionListener to respond to events. If the latter, you need to register the appropriate listener (like MouseListener) to respond to user generated events.
private int interval,side,number;
Number has a default value of 0.
for(int i = 0; i <= number - 1; i++)
Since number is 0, your loop will never execute.
Once you do this the buttons will be added to the panel but they will cover your custom painting. To see background lines you just need to set the background of the panel to Color.BLACK and then create your GridLayout with a gap between the components. Read the API for the method to use.
so im trying to create a program in java which will create a 10 by 10 matrix, with each element displaying either a 1 or a 0 randomly. Here is what i have so far:
package random.matrix;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
class ex2 extends JFrame {
class Random {
GridLayout setLayout= new GridLayout(10, 10);
for (int i = 0; i < 10; i++) {
int number = (int) (Math.random() * 2);
String str = Integer.toString(number);
add(new JLabel(str, JLabel.CENTER));
}
}
public static void main(String[] args) {
JFrame frame = new ex2();
frame.setTitle("RandomMatrix");
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
As far as I can tell, this program should run perfectly. However, every time I try, it says something along the lines of "illegal start of type," referring specifically to the for loop line. Can anyone help me troubleshoot this? I've never encountered an error quite like this one.
You need to place your code in a code block such as a method or constructor rather than the class block of an inner class
/**
* TODO: Refactor later NOT to extend from JFrame
*/
class MyFrame extends JFrame {
void initComponents() {
GridLayout setLayout = new GridLayout(10, 10);
for (int i = 0; i < 10; i++) {
...
}
}
...
}
You can't have arbitrary statements inside a class definition. Perhaps you want to put it in the constructor?
class Random {
public Random() {
GridLayout setLayout = new GridLayout(10, 10);
for (int i = 0; i < 10; i++)
{
int number = (int) (Math.random() * 2);
String str = Integer.toString(number);
setLayout.add(new JLabel(str, JLabel.CENTER));
}
}
}
Or, you could just create another method and place it in there.
I am currently working on a Java class that produces a simple JFrame/JButton layout of Tic-Tac-Toe. Implementing ActionListener, I intended on having the selected JButton set its title to "X" or "O" (based on a boolean statement of whether or not it is X's turn to pick a JButton) and become disabled (so it cannot be played on top of in following turns). The current application I have created does this, but it will sometimes not change the JButton text or disable the button until I click another button. There does not seem to be any kind of cohesive order at which this happens when I click one of the JButtons. I have spent hours trying to fix this issue with no avail. Is there an issue with how I coded my actionPerformed method or how I added it to my JButtons?
Here is the code to my class:
import javax.swing.*;
import java.awt.event.*;
import javax.swing.*;
public class TTT extends JFrame implements ActionListener{
// private fields
private JButton[] buttonArray;
private JLabel prompt;
private boolean turnX;
private String letter;
public TTT() {
// Instantiates JFrame window and adds lines to board
super.setSize(235, 280);
super.setTitle("Tic-Tac-Toe");
// Instantiates JButton array
buttonArray = new JButton[9];
// Loop that creates the JButton squares
for(int y = 30; y <= 140; y += 55) {
for(int x = 30; x <= 140; x += 55) {
for(int index = 0; index < buttonArray.length; index++) {
buttonArray[index] = new JButton();
buttonArray[index].setSize(50, 50);
buttonArray[index].setLocation(x, y);
buttonArray[index].addActionListener(this);
super.add(buttonArray[index]);
}
}
}
prompt = new javax.swing.JLabel("X's TURN");
prompt.setVerticalAlignment(JLabel.BOTTOM);
super.add(prompt);
turnX = true;
super.setVisible(true);
}
public void actionPerformed(java.awt.event.ActionEvent a) {
// Calculate whose turn it is
if(turnX){
letter = "X";
prompt.setText("O's TURN");
turnX = false;
} else if(!turnX){
letter = "O";
prompt.setText("X's TURN");
turnX = true;
}
JButton pressedButton = (JButton)a.getSource();
pressedButton.setText(letter);
pressedButton.setEnabled(false);
super.repaint();
}
public static void main(String[] args) {
new TTT();
}
}
With this code:
for(int y = 30; y <= 140; y += 55) {
for(int x = 30; x <= 140; x += 55) {
for(int index = 0; index < buttonArray.length; index++) {
You are adding 82(!) JButtons (one on top of the other in groups of 9). So, what was actually happening was that one was changed (disabled etc), but on calling repaint, the order of painting them might change, so the one whose ActionListener was triggered was painted underneath one of the 8 that were still enabled and blank.
You can correct it like this:
...
int index = 0; // <-- Add this line
for(int y = 30; y <= 140; y += 55) {
for(int x = 30; x <= 140; x += 55) {
// <-- remove the third for-loop
buttonArray[index] = new JButton();
buttonArray[index].setSize(50, 50);
buttonArray[index].setLocation(x, y);
buttonArray[index].addActionListener(this);
super.add(buttonArray[index]);
index++; // <-- increment 'index'
}
}
...
You could also remove the super.repaint() line, since it is redundant.
Not directly related to your problem, but Swing related stuff should (for various reasons) be called from the Event Dispatching Thread (EDT). One way to achieve this, is by using SwingUtilities.invokeLater(), so it might be a good idea to replace new TTT(); with this:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TTT();
}
});
I'm having a problem where I can't properly access my instance Point data.
I create an multi-dimensional array of GridPanels, and instantiate each with a Point.
When first created, everything works as expected.
pic1 http://img.skitch.com/20100218-fciwr7t73ci2gajafmfxa2yf9q.jpg
When I click on a GridPanel however, the Listener class always receives the Point from the last GridPanel that was created ( (3, 3) in this case.)
When I pass an int instead of a Point however, the int for the GridPanel that was clicked is shown (like you'd expect).
Anyone know what's going on here?
Thanks
import javax.swing.JFrame;
/**
* Driver class.
*/
public class Test {
/**
* The main method.
* #param args Command line arguments.
*/
public static void main(String[] args) {
JFrame frame = new JFrame("TEST");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TestPanel panel = new TestPanel();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
import java.awt.GridLayout;
import java.awt.Point;
import javax.swing.JPanel;
/**
* Creates a 4 by 4 grid of GridPanels.
*/
public class TestPanel extends JPanel {
static final int ROW_SIZE = 4;
static final int COL_SIZE = 4;
private GridPanel[][] g = new GridPanel[ROW_SIZE][COL_SIZE];
public TestPanel() {
Point coords = new Point();
setLayout(new GridLayout(ROW_SIZE, COL_SIZE));
for (int i = 0; i < ROW_SIZE; i++) {
for (int j = 0; j < COL_SIZE; j++) {
coords.setLocation(i, j);
g[i][j] = new GridPanel(coords);
add(g[i][j]);
}
}
}
}
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* Contains the MouseListener.
*/
public class GridPanel extends JPanel {
private JLabel label;
private Point p;
public GridPanel(Point p) {
this.p = p;
label = new JLabel("" + p);
add(label);
setBackground(Color.WHITE);
setPreferredSize(new Dimension(200, 50));
addMouseListener(new SelectListener());
}
private class SelectListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
label.setText("" + p);
}
}
}
The problem is that you are re-using the same point, stored in coords. You need to create a new point for each grid element. It looks to you as if each panel has a different point value stored in it because each panel has a different label. But in the line
label = new JLabel("" + p);
you are creating a String that contains the current value of p. But p can change later, and the label won't change with it.
So the easiest fix for your problem is to change the line
this.p = p;
to
this.p = new Point(p); // Create a defensive copy.
It looks like you may be currently somewhat confused about the difference between objects and fields. For example,
Point p = new Point(3, 4);
Point p2 = p;
p.x = 7;
System.out.println(p2.x);
will yield 7, as there is only one point being manipulated, though it's pointed to by two fields. Using = doesn't create a copy of the point.
(Apologies if I'm explaining things you already know!)
The point is and object so it is passed by reference. This means all your panels reference the same Point. Since you are changing the location on it all the time - the last will be shown.
You have to create new Point every time in the loop:
public TestPanel() {
setLayout(new GridLayout(ROW_SIZE, COL_SIZE));
for (int i = 0; i < ROW_SIZE; i++) {
for (int j = 0; j < COL_SIZE; j++) {
g[i][j] = new GridPanel(new Point(i, j));
add(g[i][j]);
}
}
}
i change setPreferredSize(new Dimension(w, h)); this is done.
but in my program i need change my frame size every time. so how can fit gridpanel in that case.... if frame size (1200,800) or (1170,920) i am not using JLabel here.
thankyou for answering
in place of this frame.pack(); i use frame.setSize(W,H);
in a gridpanel add
setPreferredSize(new Dimension(x,y));
setBorder(BorderFactory.createLineBorder(Color.red));
i remove JLabel
where x = w / col_size; y = h / row_size;
now when i run Test.java grid are not fitted in my frame;