How to assign or initiate an integer into JButton? - java

package javaapplication3;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class BattleShip2 extends JFrame implements ActionListener {
JButton[][] array2dtop = new JButton[10][10];
BattleShip2(String title) { //Board Display
super(title);//show Title
setLayout(new FlowLayout()); // button posistion
for(int x = 0; x < array2dtop.length; x++) {
for(int y = 0; y < array2dtop.length; y++) {
array2dtop[x][y] = new JButton(String.valueOf((x * 10) + y)); //display
array2dtop[x][y].setBounds(20,40,140,20);
array2dtop[x][y].setActionCommand("x"); //itself value
array2dtop[x][y].addActionListener(this);
add(array2dtop[x][y]);
}
}
}
public void actionPerformed(ActionEvent evt) { //operation
String cmd = evt.getActionCommand();
if(cmd == "x") {
System.out.println("x");
}
}
}
public class pratice3 {
public static void main(String[] args) {
BattleShip2 board = new BattleShip2("BattleShip");
board.setSize(500, 400);
board.setLocation(250, 250);
board.setDefaultCloseOperation(board.EXIT_ON_CLOSE); //Press x to EXIT
board.setVisible(true);
}
}
Hi, i am creating a battleship game, and my idea is when the user or AI hit the button, it could show ship size
For example, there have 3 ships: ship1(size = 3), ship2(size = 2), ship3(size = 1), and when the user/AI hit the ship1, and they see 3, they will know they hit ship1
but I am having troubles on the setActioncommand
on the code,
array2dtop[x][y].setActionCommand("x")
I want to assign or declare a integer(the size of the ship) into array2dtop[x][y]
but I have no idea but assign a string and how can I use it in the actionPerformed

I want to assign or declare a integer(the size of the ship) into array2dtop[x][y]
You can just parse the string to an integer using Integer.parseInt:
class BattleShip2 extends JFrame implements ActionListener {
JButton[][] array2dtop = new JButton[10][10];
BattleShip2(String title) {
super(title);
setLayout(new GridLayout(10, 10, 5, 5));
for (int x = 0; x < array2dtop.length; x++) {
for (int y = 0; y < array2dtop.length; y++) {
array2dtop[x][y] = new JButton(String.valueOf((x * 10) + y));
array2dtop[x][y].setActionCommand(String.valueOf(3));
array2dtop[x][y].addActionListener(this);
// Force buttons to be square.
Dimension dim = array2dtop[x][y].getPreferredSize();
int buttonSize = Math.max(dim.height, dim.width);
array2dtop[x][y].setPreferredSize(new Dimension(buttonSize, buttonSize));
add(array2dtop[x][y]);
}
}
}
public void actionPerformed(ActionEvent evt) {
int cmd = Integer.parseInt(evt.getActionCommand());
if (cmd == 3) {
System.out.println(3);
}
}
}
public class pratice3 {
public static void main(String[] args) {
BattleShip2 board = new BattleShip2("BattleShip");
board.pack();
board.setLocationRelativeTo(null);
board.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
board.setVisible(true);
}
}
Notes:
I changed the layout to GridLayout with the same vertical and horizontal gaps as FlowLayout's defaults. It will assure that all button have the same size.
Don't use setBounds, let the layout manager take care of that.
I added 3 lines that make the buttons square, you can remove that.
Instead of using setSize for a JFrame, use pack.
Setting the location using board.setLocationRelativeTo(null) (After the call to pack) is usually a better idea since you don't know the size of the screen.
board.EXIT_ON_CLOSE should be accessed in a static way: JFrame.EXIT_ON_CLOSE (for example).
When calling Integer.parseInt, you might want to catch NumberFormatException.
With that being said, there are other ways to achieve this, such as using setClientProperty on the buttons, or just passing the number to the ActionListener's constructor.

Related

repaint() method not calling paintComponent

I'm trying to write a program that visualizes simple sorting algorithms using Java Swing.
I have a menu with buttons that let the user choose which sorting algorithm they would like to see.
My problem is that repaint() is not calling paintComponent after every index swap, so we don't get to see the array being sorted. Instead, the program just displays the array once the panel is made visible, showing the already sorted array.
I've tried adding frame.revalidate() but it doesn't do anything since I'm not adjusting any frames or panels, just the array.
What am I missing?
Thank you!
Here is my main class and a sorting class (they're all similar).
import java.awt.Color;
import java.awt.Dimension;
import java.awt.CardLayout;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
public class AlgVisualiser implements ActionListener {
static final int N = 100;
static Integer[] arr;
static final int CONTENT_WIDTH = 800;
static final int CONTENT_HEIGHT = 800;
static JFrame frame = new JFrame("Sorting Algorithms");
static JPanel buttonPanel = new JPanel();
static JPanel arrPanel = new JPanel();
static JButton bubbleButton;
static JButton insertionButton;
static JButton selectionButton;
static Bubble bubbleSort;
static Insertion insertSort;
static Selection selectionSort;
public static void main(String[] args) {
initializeVars();
setFrame();
}
public static void setFrame() {
frame.setLayout(new CardLayout());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
frame.setLocationRelativeTo(null);
buttonPanel.setVisible(true);
frame.add(buttonPanel);
frame.add(arrPanel);
frame.pack();
}
public static void initializeVars() {
arr = new Integer[N];
arr = fillArr(arr);
arr = shuffleArr(arr);
bubbleSort = new Bubble(arr);
bubbleSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
insertSort = new Insertion(arr);
insertSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
selectionSort = new Selection(arr);
selectionSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
AlgVisualiser alg = new AlgVisualiser();
bubbleButton = new JButton("Bubble Sort");
bubbleButton.setPreferredSize(new Dimension(200, 200));
bubbleButton.addActionListener(alg);
selectionButton = new JButton("Selection Sort");
selectionButton.setPreferredSize(new Dimension(200, 200));
selectionButton.addActionListener(alg);
insertionButton = new JButton("Insertion Sort");
insertionButton.setPreferredSize(new Dimension(200, 200));
insertionButton.addActionListener(alg);
bubbleButton.setBackground(Color.WHITE);
selectionButton.setBackground(Color.WHITE);
insertionButton.setBackground(Color.WHITE);
buttonPanel.setBackground(Color.DARK_GRAY);
buttonPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
buttonPanel.add(bubbleButton);
buttonPanel.add(selectionButton);
buttonPanel.add(insertionButton);
arrPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
arrPanel.add(bubbleSort);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource() == bubbleButton) {
buttonPanel.setVisible(false);
arrPanel.setVisible(true);
bubbleSort.sort();
} else if (event.getSource() == selectionButton) {
buttonPanel.setVisible(false);
arrPanel.setVisible(true);
insertSort.sort();
} else if (event.getSource() == insertionButton) {
buttonPanel.setVisible(false);
arrPanel.setVisible(true);
selectionSort.sort();
}
}
public static Integer[] shuffleArr(Integer[] arr) {
arr = fillArr(arr);
List<Integer> list = Arrays.asList(arr);
Collections.shuffle(list);
arr = list.toArray(arr);
return arr;
}
public static Integer[] fillArr(Integer[] arr) {
for (int i = 0; i < N; i++) {
arr[i] = i + 1;
}
return arr;
}
}
import java.awt.*;
import javax.swing.*;
public class Bubble extends JComponent {
private static int checkedIndex1;
private static int checkedIndex2;
private static final long serialVersionUID = 1L;
private Integer[] arr;
public Bubble(Integer[] arr) {
this.arr = arr;
}
public void sort() {
for (int i = 0; i < AlgVisualiser.N - 1; i++) {
for (int j = 0; j < AlgVisualiser.N - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
checkedIndex1 = i;
checkedIndex2 = j;
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
AlgVisualiser.frame.revalidate();
AlgVisualiser.frame.repaint();
}
}
}
checkedIndex1 = -1;
checkedIndex2 = -1;
AlgVisualiser.frame.revalidate();
AlgVisualiser.frame.repaint();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D graphics2d = (Graphics2D) g;
graphics2d.setColor(Color.DARK_GRAY);
graphics2d.fillRect(0, 0, AlgVisualiser.CONTENT_WIDTH, AlgVisualiser.CONTENT_HEIGHT);
for (int i = 0; i < arr.length; i++) {
int width = (int) (AlgVisualiser.CONTENT_WIDTH / (double) AlgVisualiser.N);
int height = arr[i] * (AlgVisualiser.CONTENT_HEIGHT / AlgVisualiser.N);
int x = i * width;
int y = AlgVisualiser.CONTENT_HEIGHT - height;
if (i == checkedIndex1 || i == checkedIndex2) {
graphics2d.setColor(Color.RED);
} else if (checkedIndex1 == -1) {
graphics2d.setColor(Color.GREEN);
} else {
graphics2d.setColor(Color.WHITE);
}
graphics2d.fillRect(x, y, width, height);
}
}
}
There are several problems with the posted code:
frame.getContentPane().setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
...
frame.add(buttonPanel);
frame.add(arrPanel);
and
bubbleSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
...
arrPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
arrPanel.add(bubbleSort);
You set 3 different components to the same size. They can't all be the same size, since the content pane of the frame contains multiple components.
Don't keep setting the preferred size of components. It is the responsibility of each component to determine its own preferred size.
With components like buttons, if you want the button bigger, you can use the setMargins(...) method.
When doing custom painting, you would override the getPreferredSize() of the class, since your custom painting code knows best what the size should be.
static JPanel buttonPanel = new JPanel();
static JPanel arrPanel = new JPanel();
static JButton bubbleButton;
static JButton insertionButton;
static JButton selectionButton;
static Bubble bubbleSort;
static Insertion insertSort;
static Selection selectionSort;
Don't use static variable everywhere. This indicates improper design.
public static void initializeVars() {
Again, don't use static methods. Also improper design.
You need to create a class with instance variables and method that can access these instance variables.
graphics2d.fillRect(0, 0, AlgVisualiser.CONTENT_WIDTH, AlgVisualiser.CONTENT_HEIGHT);
Don't access variable from another class when doing painting. Instead you would use the getWidth() and getHeight() methods to determine the current size of the component so you can fil in the background of the panel.
AlgVisualiser.frame.revalidate();
AlgVisualiser.frame.repaint();
Don't repaint the entire frame. You are only changing your custom component you you only need to repaint the component, not the entire frame. There is also no need for the reavalidate(). The revalidate() method is used to invoke the layout manager. Your are not adding or removing components from the panel.
the program just displays the array once the panel is made visible, showing the already sorted array.
Now the hard part.
The repaint() method just adds a paint request to the RepaintManager. The RepaintManager will then consolidate requests and add a paint request to the Event Dispatch Thread (EDT) which will repaint the frame.
The problem is your looping code executes so fast that you don't see the individual steps. So you need to use a Thread.sleep(...) so slow down processing to give the GUI a chance to paint each step.
Now you have another problem. If you use Thread.sleep() on the EDT, the GUI still can't repaint() itself until the loop is finished executing.
So you need to execute the soring code on a separate Thread. Then you can tell the sorting Thread to sleep and tell the GUI to repaint itself. One way to do this is to use a SwingWorker.
Read the section from the Swing tutorial on Concurrency for more information on the EDT and the SwingWorker.

How to change position of a JLabel?

I'm trying to use a timer to change the position of a JLabel, from one spot on my JPanel to another. I'm not sure if I could use say .getLocation(), then change only the horizontal x value, and finally use .setLocation() to effectively modify the JLabel. I've also used .getBounds and .setBounds, but am still unsure how I can obtain the old horizontal x value to change and reapply to the new x value.
The code I tried looks something like this, but neither is a valid way to change the position of the JLabel.
// mPos is an arraylist of JLabels to be moved.
for(int m = 0; m < mPos.size(); m++){
mPos.get(m).setLocation(getLocation()-100);
}
or
for(int m = 0; m < mPos.size(); m++){
mPos.get(m).setBounds(mPos.get(m).getBounds()-100);
}
If I could just get the position of the horizontal x value I can change the position of the label.
Try with Swing Timer if you are looking for some animation.
Please have a look at How to Use Swing Timers
Here is the sample code:
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
Find a Sample code here
Sample code: (Move Hello World message 10px horizontally left to right at interval of 200 ms)
private int x = 10;
...
final JPanel panel = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Hello World", x, 10);
}
};
int delay = 200; // milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
x += 10;
if (x > 100) {
x = 10;
}
panel.repaint();
}
};
new Timer(delay, taskPerformer).start();
I made a similar example just so you can get the basic jest of it, try copy pasting this in a new class called "LabelPlay" and it should work fine.
import java.awt.EventQueue;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class LabelPlay {
private JFrame frame;
private JLabel label;
private Random rand;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
LabelPlay window = new LabelPlay();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public LabelPlay() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 659, 518);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
label = new JLabel("YEEEHAH!");
label.setBounds(101, 62, 54, 21);
frame.getContentPane().add(label);
JButton btnAction = new JButton("Action!");
rand = new Random();
btnAction.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
int a = rand.nextInt(90)+10;
int b = rand.nextInt(90)+10;
int c = rand.nextInt(640)+10;
int d = rand.nextInt(500)+10;
label.setBounds(a, b, c, d);
}
});
btnAction.setBounds(524, 427, 89, 23);
frame.getContentPane().add(btnAction);
}
}
If you want this to happen in a loop at certain times, you can just put it in a loop and use Thread.sleep(amount of miliseconds) in the loop before you run the code.
Why don't you create a JLabel at position a, set it to visible, and another one at position b, setting it to not visible? After the timer is up, hide the first and show the second.
Are you planning on creating some moving imageIcon for some type of game? Or some label that moves pretty much everywhere ?
I would use an Absolute Layout and set Location manually everytime.
myPanel.setLayout(null);
// an initial point
int x = 100;
int y =100;
while (
//some moving pattern
x++; // 1 pixel per loop
y+=2; // 2 pixels per loop
myLabel.setLocation(x,y);
}

how to refresh the colors of button contained in a JFrame?

I am coding a little game in which i have taken a grid of JButtons in a JFrame and i want to refresh the colors of the buttons contained in a JFrame,which is already visible.As explained below
void foo(){
mainFrame.setVisible(true);//mainFrame is defined at class level.
//color update code for the buttons.
mainFrame.setVisible(true);
}
Result i am getting is not as expected and my screen gets freeze .Isn't it the right way to achieve what i wanted?
EDIT
ok i am explaining it in detail what i want to achieve.i have a class,as:-
import javax.swing.*;
import java.awt.*;
import java.util.*;
class Brick extends JButton{
public void setRandomColors(){
int random = (int) (Math.random()*50);
if(random%13==0){
this.setBackground(Color.MAGENTA);
}
else if(random%10==0){
this.setBackground(Color.red);
}
else if(random%9==0){
this.setBackground(Color.yellow);
}
else if(random%7==0){
this.setBackground(Color.orange);
}
else if(random%2==0){
this.setBackground(Color.cyan);
}
else{
this.setBackground(Color.PINK);
}
}
public void setBlackColor(){
this.setBackground(Color.black);
}
}
class Grid {
JFrame mainGrid = new JFrame();
ArrayList<Brick> bunchOfBricks = new ArrayList<>();
int gridLength = 8;//gridlenth is equals to gridweight as i have considered a Square grid.
int totalBricks = gridLength*gridLength;
public void formBunchOfBricks(){
for(int i=0;i<totalBricks;i++){
bunchOfBricks.add(new Brick());
}
}
public void formColoredGrid(){
Brick aBrick;
mainGrid.setLayout(new GridLayout(8,8));
for(int i=0;i<totalBricks;++i){
aBrick = (bunchOfBricks.get(i));
aBrick.setRandomColors();
mainGrid.add(aBrick);
}
mainGrid.setVisible(true);//its ok upto here iam getting randomly colored Frame of Bricks or so called JButtons.
delay(15);//Sorry for this one,i warn you not to laugh after looking its defination.
}
/*
I want following function to do following things:-
1.it should firstly display the Grid whose all buttons are black Colored.
2.After some time the original colored,first Row of grid formed by formColoredGrid should be displayed and all the rest Rows should be black.
3.Then second row turns colored and all other rows should be black......and so on upto last row of Grid.
*/
public void movingRows(){
setGridBlack();
delay(1);//see in upper method,for this horrible thing.
for(int i=0;i<gridLength;++i){
setGridBlack();
for (int j=0;j<gridLength;++j){
Brick aBrick = bunchOfBricks.get((gridLength*i)+j);
aBrick.setRandomColors();//Bricks are colored Row by Row.
}
delay(5);//already commented this nonsense.
mainGrid.setVisible(true);//using setVisible again,although this frame is already visible,when i called formColoredGrid.
setGridBlack();
}
//oh! disappointing,i have almost broken my arm slamming it on table that why the function result in a screen full of black buttons.
}
public void setGridBlack(){
for(int i=0;i<totalBricks;i++){
bunchOfBricks.get(i).setBlackColor();
}
}
public void delay(int a){
for ( int i=0;i<90000000;++i){
for(int j=0;j<a;++j){
}
}
}
public static void main(String args[]){
Grid g1 = new Grid();
g1.formBunchOfBricks();
g1.formColoredGrid();
g1.movingRows();
}
}
Please Help me what is the way out?
Your problem is in code not shown here:
//color update code for the buttons.
You're likely running a loop that never ends on the Swing event thread, possibly a never-ending while loop that polls the state of something(a guess), freezing your GUI. Solution: don't do this; don't use a continuous polling loop. Instead, change the colors based on responses to events as Swing is event-driven.
For better more specific help, please show the offending code and tell us more about your program.
Edit
If you're trying to show colored rows, one by one marching down the board, then my guess is right, you'll want to use a Swing Timer, one that uses an int index to indicate which row is being displayed in color. You'd increment the index inside of the Timer's ActionPerformed class, and then when all rows have been displayed stop the Timer. For example something like so:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyGrid extends JPanel {
private static final int GRID_LENGTH = 8;
private static final Color BTN_BACKGROUND = Color.BLACK;
private static final Color[] COLORS = { Color.MAGENTA, Color.CYAN,
Color.RED, Color.YELLOW, Color.ORANGE, Color.PINK, Color.BLUE,
Color.GREEN };
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 800;
private JButton[][] buttonGrid = new JButton[GRID_LENGTH][GRID_LENGTH];
private Map<JButton, Color> btnColorMap = new HashMap<>();
private Random random = new Random();
public MyGrid() {
setLayout(new GridLayout(GRID_LENGTH, GRID_LENGTH));
for (int row = 0; row < buttonGrid.length; row++) {
for (int col = 0; col < buttonGrid[row].length; col++) {
JButton btn = new JButton();
btn.setBackground(BTN_BACKGROUND);
// !! add action listener here?
add(btn);
buttonGrid[row][col] = btn;
}
}
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void resetAllBtns() {
for (JButton[] row : buttonGrid) {
for (JButton btn : row) {
btn.setBackground(BTN_BACKGROUND);
}
}
}
private class TimerListener implements ActionListener {
private int row = 0;
#Override
public void actionPerformed(ActionEvent e) {
resetAllBtns(); // make all buttons black
if (row != buttonGrid.length) {
for (int c = 0; c < buttonGrid[row].length; c++) {
int colorIndex = random.nextInt(COLORS.length);
Color randomColor = COLORS[colorIndex];
buttonGrid[row][c].setBackground(randomColor);
// !! not sure if you need this
btnColorMap.put(buttonGrid[row][c], randomColor);
}
row++;
} else {
// else we've run out of rows -- stop the timer
((Timer) e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
MyGrid mainPanel = new MyGrid();
JFrame frame = new JFrame("MyGrid");
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();
}
});
}
}
Please have a look at the Swing Timer Tutorial as well.
Edit 2
You ask:
but what is the reason of failure of this program,is it the useless delay function?
Your delay method does nothing but busy calculations on the Swing event thread:
public void delay(int a) {
for (int i = 0; i < 90000000; ++i) {
for (int j = 0; j < a; ++j) {
}
}
}
It's little different from a crude attempt at calling Thread.sleep(...), and is crude because you can't explicitly control how long it will run as you can with thread sleep. Again, the problem is that you're making these calls on the Swing event dispatch thread or EDT, the single thread that is responsible for all Swing drawing and user interactions. Blocking this thread will block your program making it non-running or frozen.

Dynamically initializing JButtons with unique ImageIcons

This is my first attempt at using a GUI layout in Java. I am trying to create a simple memory card game, where the user flips over two cards, and tries to get a match, and if there's no match they flip back over, if there's a match they stay flipped until you get all the matches. I might have made it hard on myself though in that I made the whole game dynamic to the constructor variables that define the number of columns and rows of cards. I thought this was better than hard-coding a certain value in, but now I'm having trouble putting the images in my img folder onto my cards.
I understand that variables of variables are not permitted in Java & this is really hard for me to adapt to as a ColdFusion developer. Can you help me think of ways to accomplish this this the proper way in Java?
Here is a simplified version of my code.
import javax.swing.JFrame;
public class MemoryApp
{
public static void main(String args[])
{
//Creates a new game with 3 columns and 4 rows
final CardGame myGame = new CardGame(3, 4);
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI(myGame.getCols(), myGame.getRows());
}
});
}
private static void createAndShowGUI(int c, int r) {
//Create and set up the window.
Window frame = new Window("GridLayoutDemo", c, r);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set up the content pane.
frame.addComponentsToPane(frame.getContentPane());
//Display the window.
frame.pack();
frame.setVisible(true);
}
}
Card game class:
public class CardGame
{
private int cols, rows;
public CardGame(int c, int r)
{
cols = c;
rows = r;
}
public int getCols(){
return cols;
}
public int getRows(){
return rows;
}
}
The window with the grid layout:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;
public class Window extends JFrame
{
private int cols, rows;
GridLayout windowGrid = new GridLayout(1,1);
public Window(String t, int c, int r)
{
super(t);
cols = c;
rows = r;
windowGrid.setColumns(c);
windowGrid.setRows(r);
}
public void addComponentsToPane(final Container pane)
{
final JPanel compsToExperiment = new JPanel();
compsToExperiment.setLayout(windowGrid);
JPanel controls = new JPanel();
controls.setLayout(new GridLayout(cols,rows));
int countCard = (cols * rows) / 2;
/**
* Add buttons to experiment with Grid Layout.
* This is where I'd like to be able to loop through
* countCard and create the required number of buttons
* as well as put images on the buttons like so:
*
* ImageIcon image1 = new ImageIcon(getClass().getResource("card1.png"));
* through
* ImageIcon image1 = new ImageIcon(getClass().getResource("card" & cardCount & ".png"));
*
* Below is how I would attempt this in ColdFusion- I know
* the variable of variables syntax is invalid, it is just
* to show you what I mean.
*/
// for(int i = 0; i < countCard; i++;)
// {
// compsToExperiment.add(new JButton("../img/card" & i & ".png"));
// ImageIcon variables["image" & i] = new ImageIcon(getClass().getResource("card" & i & ".png"));
// imageButton.setIcon(variables["image" & i]);
// etc. with ButtonClickEventHandler, haven't gotten that far yet
// }
pane.add(compsToExperiment, BorderLayout.NORTH);
pane.add(new JSeparator(), BorderLayout.CENTER);
}
}
Based on the code commented-out code that you posted so far, one approach could be like this:
public class Window extends JFrame
{
...
// A java.util.List that stores all the buttons, so
// that their icons may be changed later
private List<JButton> buttons = new ArrayList<JButton>();
// A java.util.List that stores all the ImageIcons that
// may be placed on the buttons
private List<ImageIcon> imageIcons = new ArrayList<ImageIcon>();
public void addComponentsToPane(final Container pane)
{
...
for(int i = 0; i < countCard; i++;)
{
// String concatenation is done with "+" in Java, not with "&"
String fileName = "card" + i + ".png";
// Create the icon and the button containing the icon
ImageIcon imageIcon = new ImageIcon(getClass().getResource(fileName));
JButton button = new JButton(imageIcon);
// Add the button to the main panel
compsToExperiment.add(button);
// Store the button and the icon in the lists
// for later retrieval
imageIcons.add(imageIcon);
buttons.add(button);
// Attach an ActionListener to the button that will
// be informed when the button was clicked.
button.addActionListener(createActionListener(i));
}
}
private ActionListener createActionListener(final int cardIndex)
{
return new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
clickedCardButton(cardIndex);
}
};
}
private void clickedCardButton(int cardIndex)
{
System.out.println("Pressed button for card "+cardIndex);
// Here you can change the icons of the buttons or so...
JButton button = buttons.get(cardIndex);
ImageIcon imageIcon = imageIcons.get(cardIndex);
....
}
You mentioned that this is your first attempt to build a GUI with Java. So I assume that this is only intended for "practicing". If your intention was to build a "real application", you should rather consider some Model-View-Controller (MVC) approach for this.

Making a TicTacToe game; how can I get the tictactoe buttons to alternate between X and O?

I just need to get it to alternate between "X" and "O" for the turns but it's only giving me X's.
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class tictactoe {
public static final int FRAME_WIDTH = 700;
public static final int FRAME_HEIGHT = 200;
public static void main(String[] args)
{
int slots = 9;
final JButton[] gameButton = new JButton[9];
JPanel ticTacToeBoard = new JPanel();
ticTacToeBoard.setLayout(new GridLayout(3, 3));
JButton clearButtons = new JButton("New Game");
for (int i = 0; i < slots; i++)
{
gameButton[i] = new JButton();
ticTacToeBoard.add(gameButton[i]);
final int countTurns = i;
gameButton[i].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Object clicked = e.getSource();
int turns = 1;
for (int p = 0; p < 9; p++)
{
if(clicked == gameButton[countTurns] && turns < 10)
{
if (!(turns % 2 == 0))
{
((JButton)e.getSource()).setText("X");
turns++;
}
else
{
((JButton)e.getSource()).setText("O");
turns++;
}
}
}
}
});
final int integerHack = i;
clearButtons.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
gameButton[integerHack].setText("");
}
});
}
JButton exit = new JButton("Exit");
exit.setActionCommand("EXIT");
exit.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd == "EXIT")
{
System.exit(FRAME_WIDTH);
}
}
});
JPanel rightPanel = new JPanel();
JLabel wonLabel = new JLabel();
rightPanel.setLayout(new GridLayout(1, 3));
rightPanel.add(wonLabel);
rightPanel.add(clearButtons);
rightPanel.add(exit);
JFrame mainFrame = new JFrame();
mainFrame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
mainFrame.setVisible(true);
mainFrame.setLayout(new GridLayout(1,2));
mainFrame.add(ticTacToeBoard);
mainFrame.add(rightPanel);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Look at your actionPerformed method. Every time a click happens, you set turns to 1. As a result, !(turns % 2 == 0) will always evaluate true, and you'll always draw an X.
Just by quickly looking at your code, I would guess it is because your turn variable is local in all the ActionListener objects. Therefore, the turn variable is always 1 and you always run into the first if case. So the turn variable is always recreated on the stack as soon as you get a callback in the actionPerformed method. For a quick fix and test, try to put the turn variable in the tictactoe class and see if that helps.
The trouble is with the turns variable in your actionPerformed. If you want to keep a track of it you should move that out from your method.
Firstly, as the other people have also said, you want "turns" to be initialized outside all your loops.
Secondly, I would like to point out a typo:
if(clicked == gameButton[countTurns] && turns < 10)
should be
if(clicked == gameButton[p] && turns < 10)
...unless you really want to change each button's text from X to O nine times each time a button is clicked.
But even fixing that typo is pointless. Why loop through to find a specific button when it doesn't matter which specific button it is? All the buttons have the same action handler, anyway, just nine separate copies of it, because you are creating nine identical copies each time around.
Instead, because all the buttons do the same thing, you should have one action handler for all the buttons. If you're not sure how that's done, you create it outside your button making loop and assign it to a variable, and then you put in that variable when you assign each button's action handler, e.g.
gameButton[i].addActionListener(myActionListener);

Categories

Resources