Bad caret position message - java

Here's a message I am plagued by, which occurs when I attempt to programmatically "select" cells (whether empty or not) in a grid via keypress combo shift-rightarrow or shift-leftarrow:
Exception in thread "AWT-EventQueue-0" javax.swing.text.StateInvariantError:
Bad caret position
(Note that there is NO problem if I "select" via shift-uparrow or shift-downarrow.)
It happens when I attempt to change the font of the "selected" cells:
static Font fontSelected = new Font("Serif", Font.BOLD , POINTSIZE);
static Font fontNormal = new Font("Serif", Font.PLAIN, POINTSIZE);
(If I make the Font.type the SAME (both BOLD, both PLAIN, both ITALIC), no problem.)
The error occurs near code where I push a "selected" JTextField onto a stack (named stack), which is defined like so:
class GenericStack<E>:
public LinkedList <E> stack = new LinkedList<>();
Here's the class declaration where the stack and fonts are used:
public class Grid extends GenericStack<JTextField> implements ActionListener, KeyListener, KeyCodes, Serializable
Here's what's pushed onto stack:
public static JTextField[][] cells = new JTextField[N][N];
Here's how cells are created:
guiFrame.add(textPanel);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
cells[i][j] = addCell(textPanel, i, j);
private JTextField addCell (Container parent, int row, int col) {
JTextField cell;
cell = new JTextField();
cell.setFont(fontNormal); // 'default' font set
cell.setText("x"); // for debugging
String r, c; // 11x11 grid
if(row < N-1) r = "" + row; else r = "A"; // rows r: 0,1,2,...A
if(col < N-1) c = "" + col; else c = "A"; // cols c: 0,1,2,...A
cell.setActionCommand(r + c); // cell rc: 00..0A;10..1A;...A0..AA;
cell.addKeyListener(this);
cell.setHorizontalAlignment(JTextField.CENTER);
parent.add(cell);
return cell;
}
Here's main:
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Grid();
}
});
}
Here's where the font is changed (for any "selected" cell):
if(currentCell.selected){
Grid.cells[currentCell.row][currentCell.col].setBackground(Color.RED);
Grid.cells[currentCell.row][currentCell.col].setFont(fontSelected);
stack.push(Grid.cells[currentCell.row][currentCell.col]);
}
The error occurs in this block of code--if I comment out the setFont line, no problem; if I instead change the font declarations to involve the same font, no problem.
Especially puzzling me is that the stack trace doesn't specify which line of code caused the error.

I'm not sure why your exception is occurring, but it can be solved by queuing the font change on the Swing event thread:
#Override
public void keyPressed(KeyEvent evt) {
final JComponent comp = (JComponent) evt.getSource();
int keyCode = evt.getKeyCode();
boolean shiftIsDown = evt.isShiftDown();
currentCell.selected = ((shiftIsDown & (keyCode == RIGHT | keyCode == UP
| keyCode == LEFT | keyCode == DOWN)));
if (currentCell.selected) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
comp.setFont(fontSelected);
}
});
}
}
Myself, I try to avoid KeyListeners with Swing applications but instead prefer key bindings. For example:
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class SSCCE2 extends JPanel {
private static final int ROW_COUNT = 11;
private static final int colCount = 3;
private static final Font NORMAL_FONT = new Font("Serif", Font.PLAIN, 18);
private static final Font SELECTED_FONT = NORMAL_FONT.deriveFont(Font.BOLD);
private JTextField[][] fields = new JTextField[ROW_COUNT][ROW_COUNT];
public SSCCE2() {
FontAction fontAction = new FontAction();
int condition = WHEN_FOCUSED;
setLayout(new GridLayout(ROW_COUNT, ROW_COUNT));
for (int i = 0; i < fields.length; i++) {
for (int j = 0; j < fields[i].length; j++) {
JTextField cell = new JTextField(colCount);
InputMap inputMap = cell.getInputMap(condition);
ActionMap actionMap = cell.getActionMap();
int[] arrowKeyCodes = {KeyEvent.VK_UP, KeyEvent.VK_DOWN,
KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT};
for (int keyCode : arrowKeyCodes) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
KeyEvent.SHIFT_DOWN_MASK);
inputMap.put(keyStroke, keyStroke.toString());
actionMap.put(keyStroke.toString(), fontAction);
}
cell.setFont(NORMAL_FONT);
cell.setHorizontalAlignment(JTextField.CENTER);
add(cell);
fields[i][j] = cell;
}
}
}
private class FontAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent evt) {
for (JTextField[] row : fields) {
for (JTextField textField : row) {
if (textField.hasFocus()) {
textField.setFont(SELECTED_FONT);
} else {
textField.setFont(NORMAL_FONT);
}
}
}
}
}
private static void createAndShowGui() {
SSCCE2 mainPanel = new SSCCE2();
JFrame frame = new JFrame("SSCCE2");
frame.setDefaultCloseOperation(JFrame.EXIT_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();
}
});
}
}

I removed everything, including cursor movement code, that didn't affect getting the message, so when this is run, a grid appears, and to get the error, hold the shift key down and press right arrow key. (Is this what I should've done??) (P.S.--I even took out all the generic linked list (stack) stuff, which, it turns out, had nothing to do with the problem.)
package sscce;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
class Cell
{
int row, col;
boolean selected;
Cell(int row, int col){
this.row = row;
this.col = col;
}
}
public class SSCCE implements ActionListener, KeyListener
{
public static final int LEFT = 37, UP = 38, RIGHT = 39, DOWN = 40;
public static final int N = 11;
JFrame guiFrame;
JPanel textPanel;
public static JTextField[][] cells = new JTextField[N][N];
public static Cell currentCell = new Cell(0,0);
static Font fontSelected = new Font("Serif", Font.BOLD , 12);
static Font fontNormal = new Font("Serif", Font.PLAIN, 12);
public SSCCE(){
textPanel = new JPanel();
textPanel.setLayout(new GridLayout(N, N));
guiFrame = new JFrame();
guiFrame.setMinimumSize(new Dimension(400, 400));
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
guiFrame.setLocationRelativeTo(null);
guiFrame.add(textPanel);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
cells[i][j] = addCell(textPanel, i, j);
guiFrame.setVisible(true);
}
private JTextField addCell (Container parent, int row, int col) {
JTextField cell;
cell = new JTextField();
cell.setFont(fontNormal);
cell.addKeyListener((KeyListener) this);
cell.setHorizontalAlignment(JTextField.CENTER);
parent.add(cell);
return cell;
}
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SSCCE();
}
});
}
#Override
public void keyPressed(KeyEvent evt) {
int keyCode = evt.getKeyCode();
boolean shiftIsDown = evt.isShiftDown();
currentCell.selected = ((shiftIsDown & (keyCode == RIGHT | keyCode == UP | keyCode == LEFT | keyCode == DOWN)));
if(currentCell.selected ){
SSCCE.cells[currentCell.row][currentCell.col].setFont(fontSelected);
}
}
#Override
public void keyTyped(KeyEvent e){ }
#Override
public void keyReleased(KeyEvent e){ }
#Override
public void actionPerformed(ActionEvent e){}
}

Related

Inputting data to JTextField by pressing JButton

I wrote a program that creates a sudoku board according to the difficulty level chosen by the user. There's a basic GUI using JFrame and JPanel.
The board itself is built using a 2D array of JTextFields to allow for editing by the user and I made a table of JButtons representing digits 1-9.
I'm trying to make it so when I press a digit button while my cursor is on the relevant text field, it'll input that number to the field.
I think there's a problem with how I defined the buttons but would love a hand.
/*Java Program to solve Sudoku problem using Backtracking*/
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.*;
public class Solver extends Board {
Solver(int N, int K) {
super(N, K);
}
private static void createWindow() {
JFrame frame = new JFrame("Sudoku");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
createUI(frame);
frame.setSize(250, 80);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static void createUI(final JFrame frame) {
JPanel panel = new JPanel();
LayoutManager layout = new FlowLayout();
panel.setLayout(layout);
JButton button = new JButton("Play");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String result = (String) JOptionPane.showInputDialog(
frame,
"Difficulty Glossary:\n\n Hard - 50/81 blank spaces\n Medium - 35/81 blank spaces\n Easy - 20/81 blank spaces\n\nChoose your desired difficulty:\n\tHard: 1\n\tMedium: 2\n\tEasy: 3\nIf your input doesn't match one of these digits, the board generated will be on easy mode.",
"Difficulty Glossary",
JOptionPane.PLAIN_MESSAGE,
null,
null,
"3"
);
optionBoard();
play(Integer.parseInt(result));
}
});
panel.add(button);
frame.getContentPane().add(panel, BorderLayout.CENTER);
}
public static void optionBoard(){
}
public static void play(int level) {
int N = 9, K = 0;
switch (level) {
case 1:
K = 50;
break;
case 2:
K = 35;
break;
default:
K = 20;
break;
}
Solver sudoku = new Solver(N, K);
sudoku.fillValues();
createBoard(sudoku.puzzle);
}
public static void createBoard(int[][] puzzle) {
final Border fieldBorder = BorderFactory.createLineBorder(Color.BLACK);
final JPanel grid = new JPanel(new GridLayout(9, 9));
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
final JTextField field = new JTextField(2);
if (puzzle[i][j] != 0) {
field.setText(puzzle[i][j] + "");
} else {
field.setText("");
}
field.setHorizontalAlignment(JTextField.CENTER); //Center text horizontally in the text field.
field.setBorder(fieldBorder); //Add the colored border.
grid.add(field);
}
}
final JPanel digits = new JPanel(new GridLayout(3, 3));
int num=1;
for (int i = 1; i < 4; i++) {
for (int j = 1; j < 4; j++) {
final JButton digit = new JButton(num+"");
num++;
digits.add(digit);
}
}
final JPanel centeredGrid = new JPanel(new GridBagLayout());
centeredGrid.add(digits);
centeredGrid.add(grid);
final JFrame frame = new JFrame("Sudoku Board");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(centeredGrid);
frame.setSize(400,400);
frame.setVisible(true);
JButton button = new JButton("Check");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
}
});
// centeredGrid.add(button);
}
// Driver code
public static void main(String[] args) {
createWindow();
}
}
I've only provided the relevant GUI class since the mathematical logic part of building the board is solid and works fine. That's what the Board class is.
One way to do this could be to:
Put the JTextFields in an ArrayList<JTextField> so that you can get them later as you will likely need access to them during the game
Give the class a field, private JTextField selectedField = null;, set initially to null. This will point to the last JTextField that has been clicked (the last one to gain focus)
Give the JTextFields FocusListeners when created. If any field gains focus, set the selectedField to refer to it
Or as per Rob Camick, use a TextAction as the button's ActionListener, which will track the last text component that holds the focus.
In your number JButton's ActionListener, check that selectedField is not null
If not null, then set the button's actionCommand (its text) to be the selectedField's text.
Also, if you need more help, consider creating and posting a valid MRE. Your current code does not compile for us.
Proof of concept (and example of a MRE):
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class Solver2 extends JPanel {
private static final float FONT_SIZE = 24f;
private int gridEdgeSize = 9;
private List<JTextField> fieldList = new ArrayList<>();
private JTextField selectedField = null;
public Solver2() {
JPanel gridPanel = new JPanel(new GridLayout(gridEdgeSize, gridEdgeSize, 2, 2));
gridPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
gridPanel.setBackground(Color.BLACK);
for (int i = 0; i < gridEdgeSize; i++) {
for (int j = 0; j < gridEdgeSize; j++) {
JTextField textField = new JTextField(2);
textField.setHorizontalAlignment(SwingConstants.CENTER);
textField.setFont(textField.getFont().deriveFont(Font.BOLD, FONT_SIZE));
textField.setBorder(null);
textField.addFocusListener(new TextFieldFocusListener());
gridPanel.add(textField);
fieldList.add(textField);
}
}
JPanel numberPanel = new JPanel(new GridLayout(0, 3));
for (int i = 1; i < 10; i++) {
String text = String.valueOf(i);
JButton button = new JButton(text);
button.setFont(button.getFont().deriveFont(Font.BOLD, FONT_SIZE));
button.addActionListener(e -> buttonListener(e));
numberPanel.add(button);
}
setLayout(new GridLayout(1, 2));
add(gridPanel);
add(numberPanel);
}
private void buttonListener(ActionEvent e) {
String text = e.getActionCommand();
if (selectedField != null) {
selectedField.setText(text);
}
}
private class TextFieldFocusListener extends FocusAdapter {
#Override
public void focusGained(FocusEvent e) {
selectedField = (JTextField) e.getComponent();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Solver2 mainPanel = new Solver2();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Side Note
If you want to restrict the JTextField from allowing keyboard or copy/paste entry, then one way could be to use a DocumentFilter added to each JTextField's Document.
You could give the class a private boolean field, say,
public class Solver2 extends JPanel {
// .....
private boolean blockTextInput = true;
and a private nested class that extends DocumentFilter. This class will prevent the document from allowing String inserts, removals or replacements as long as blockTextInput is true:
private class MyDocFilter extends DocumentFilter {
#Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
throws BadLocationException {
if (blockTextInput) {
return;
}
super.insertString(fb, offset, string, attr);
}
#Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
if (blockTextInput) {
return;
}
super.remove(fb, offset, length);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
if (blockTextInput) {
return;
}
super.replace(fb, offset, length, text, attrs);
}
}
then in the JTextField creation loop, set the filter:
for (int i = 0; i < gridEdgeSize; i++) {
for (int j = 0; j < gridEdgeSize; j++) {
JTextField textField = new JTextField(2);
textField.setHorizontalAlignment(SwingConstants.CENTER);
textField.setFont(textField.getFont().deriveFont(Font.BOLD, FONT_SIZE));
textField.setBorder(null);
textField.addFocusListener(new TextFieldFocusListener());
// ***** here ****
((PlainDocument) textField.getDocument()).setDocumentFilter(new MyDocFilter());
gridPanel.add(textField);
fieldList.add(textField);
}
}
Then change the number JButton's ActionListeners to change blockTextInput before and after setting the JTextField's text:
private void buttonListener(ActionEvent e) {
String text = e.getActionCommand();
if (selectedField != null) {
blockTextInput = false;
selectedField.setText(text);
blockTextInput = true;
}
}
Done
The latest iteration of the program that uses a single FocusListener and a DocumentFilter and that incorporates Rob Camick's suggestion to use TextAction for the button's ActionListener:
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;
import javax.swing.text.TextAction;
#SuppressWarnings("serial")
public class Solver3 extends JPanel {
private static final float FONT_SIZE = 24f;
private int gridEdgeSize = 9;
private List<JTextField> fieldList = new ArrayList<>();
private boolean blockTextInput = true;
private MyTextAction myTextAction = new MyTextAction("");
private JPanel outerGridPanel = new JPanel(new GridLayout(3, 3, 3, 3));
private JPanel[][] innerGridPanels = new JPanel[3][3];
public Solver3() {
outerGridPanel.setBackground(Color.BLACK);
outerGridPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 4));
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
innerGridPanels[i][j] = new JPanel(new GridLayout(3, 3, 1, 1));
innerGridPanels[i][j].setBackground(Color.BLACK);
outerGridPanel.add(innerGridPanels[i][j]);
}
}
for (int i = 0; i < gridEdgeSize; i++) {
for (int j = 0; j < gridEdgeSize; j++) {
JTextField textField = new JTextField(2);
textField.setHorizontalAlignment(SwingConstants.CENTER);
textField.setFont(textField.getFont().deriveFont(Font.BOLD, FONT_SIZE));
textField.setBorder(null);
((PlainDocument) textField.getDocument()).setDocumentFilter(new MyDocFilter());
innerGridPanels[i / 3][j / 3].add(textField);
fieldList.add(textField);
}
}
JPanel numberPanel = new JPanel(new GridLayout(0, 3));
for (int i = 1; i < 10; i++) {
String text = String.valueOf(i);
JButton button = new JButton(text);
button.setFont(button.getFont().deriveFont(Font.BOLD, 2 * FONT_SIZE));
button.addActionListener(myTextAction);
numberPanel.add(button);
}
setLayout(new GridLayout(1, 2));
add(outerGridPanel);
add(numberPanel);
}
private class MyTextAction extends TextAction {
public MyTextAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
String text = e.getActionCommand();
JTextComponent selectedComponent = getFocusedComponent();
if (selectedComponent != null) {
blockTextInput = false;
selectedComponent.setText(text);
blockTextInput = true;
}
}
}
private class MyDocFilter extends DocumentFilter {
#Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
throws BadLocationException {
if (blockTextInput) {
return;
}
super.insertString(fb, offset, string, attr);
}
#Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
if (blockTextInput) {
return;
}
super.remove(fb, offset, length);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
if (blockTextInput) {
return;
}
super.replace(fb, offset, length, text, attrs);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Solver3 mainPanel = new Solver3();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}

Cant change java button color after resetting button array

Im creating a program in which I must from time to time reset a button Array and display it on a jPanel. The function below adds the jButtons to my panel and displays them perfectly the first time that it is called, but from then on, every time I call it (after emptying the jButton array and applying .removeAll() to the panel) it wont let me change the background color of the jButton. Some assistance to help me find out why this is would be great, thanks.
import java.awt.*;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import javafx.scene.layout.Border;
import javax.swing.*;
/**
*
* #author Luis
*/
public class MineSweeper extends JFrame implements ActionListener {
int int_dim = 11;
int int_cellsShown = 0;
JButton[][] arr_btnField = new JButton[int_dim][int_dim];
int[][] arr_solution = new int[int_dim][int_dim];
Color[] clr_palette = {Color.white, new Color(0X00, 0X94, 0XFF), new Color(0X00, 0X26, 0XFF), new Color(0X00, 0XAA, 0X0A), Color.red, Color.MAGENTA, new Color(0XFF, 0X00, 0X00), new Color(0X9B, 0X00, 0X00)};
boolean bool_change = false;
boolean bool_won = false;
boolean bool_firstround = false;
javax.swing.border.Border border = BorderFactory.createLineBorder(Color.darkGray, 1, true);
MenuBar menu_bar;
Menu menu;
MenuItem optionNew;
//boolean[][] arr_boolShowed=new boolean[int_dim][int_dim];
int int_mines = 8;
ArrayList<Integer> arl_field = new ArrayList<Integer>();
JPanel jpanel = new JPanel();
JPanel jpanel2 = new JPanel();
//ArrayList<Boolean> arl_boolShowed = new ArrayList<Boolean>();
/**
* #param args the command line arguments
*/
public MineSweeper() throws FontFormatException, IOException {
resetGame();
//JFrame frame = new JFrame("");
this.getContentPane().add(jpanel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(true);
this.setTitle("Minesweeper");
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
this.setSize(500, 500);
menu_bar = new MenuBar();
menu = new Menu("File");
optionNew = new MenuItem("Win");
optionNew.addActionListener(this);
menu.add(optionNew);
menu_bar.add(menu);
this.setMenuBar(menu_bar);
}
public void resetGame() {
jpanel.removeAll();
arl_field.clear();
arr_btnField = new JButton[int_dim][int_dim];
arr_solution = new int[int_dim][int_dim];
bool_change = false;
bool_won = false;
//arl_field = new ArrayList<Integer>();
for (int i = 0; i < arr_solution.length; i++) {
for (int j = 0; j < arr_solution[i].length; j++) {
arr_solution[i][j] = 1;
}
}
jpanel.setLayout(new GridLayout(0, int_dim));//if(bool_firstround==false)jpanel.setLayout(new GridLayout(0,int_dim));
for (int i = 0; i < arr_btnField.length; i++) {
for (int j = 0; j < arr_btnField[i].length; j++) {
arr_btnField[i][j] = new JButton();////if(bool_firstround==false)arr_btnField[i][j] = new JButton();//arl_field.get(i*int_dim+j)+"");
arr_btnField[i][j].setText("");
arr_btnField[i][j].setBackground(new Color(0X00, 0X94, 0XFF));
arr_btnField[i][j].setBorder(border);
arr_btnField[i][j].setForeground(clr_palette[1]);
arr_btnField[i][j].addMouseListener(listener);
arr_btnField[i][j].setFocusable(false);
jpanel.add(arr_btnField[i][j]);
}
}
jpanel.revalidate();
jpanel.repaint();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new MineSweeper();
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
}
});
}
MouseListener listener = new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
outerloop:
for (int i = 0; i < arr_btnField.length; i++) {
for (int j = 0; j < arr_btnField[i].length; j++) {
if (e.getSource() == arr_btnField[i][j]) {
if (SwingUtilities.isLeftMouseButton(e)) {
labelText(i, j);
}
if (SwingUtilities.isRightMouseButton(e)) {
arr_btnField[i][j].setBackground(Color.red);
}
//bool_won=false;
break outerloop;
}
}
}
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
if (bool_won == true)
gameWon();
}
#Override
public void mouseExited(MouseEvent e) {
}
};
public void labelText(int i, int j) {
if (bool_won == false) {
arr_btnField[i][j].setText("1");
arr_btnField[i][j].setBackground(Color.white);
if (arr_btnField[i][j].getBorder() == border) {
int_cellsShown++;
System.out.println("Cells shown: " + int_cellsShown);
if (int_cellsShown >= (int_dim * int_dim - int_mines)) {
bool_won = true;
}
}
if (bool_won == false)
arr_btnField[i][j].setBorder(BorderFactory.createLineBorder(Color.darkGray, 1, true));
}
}
public void gameWon() {
int dialogResult = JOptionPane.showConfirmDialog(null, "You Won! Do you want to start a new game?", "Congratulations!", JOptionPane.YES_NO_OPTION);
if (dialogResult == JOptionPane.YES_OPTION) {
bool_won = false;
int_cellsShown = 0;
resetGame();
}
}
#Override
public void actionPerformed(ActionEvent e) {
int_cellsShown = 0;
int_dim++;
resetGame();
for (int i = 0; i < arr_btnField.length; i++) {
for (int j = 0; j < arr_btnField[i].length; j++) {
arr_btnField[i][j].setBackground(Color.red);
}
}
}
}
Display after the first time:
Display after the second time:
I invoke .revalidate in the fourth line of the method.
That doesn't do anything since there on no components added to the panel. The revalidate() needs to be done AFTER the components have been added.
The displayed panel is not jpanel, the displayed panel is jpanel2, thats why I assign jpanel2 to the value of jpanel in the end of the method.
You can't just change a reference and expect the components to be moved from one panel to another.
The components need to be added to the panel that is added to the GUI.
Edit:
First of all Swing components start with "J". Don't use AWT components (MenuBar, Menu, MenuItem) in a Swing application.
The problem is your LAF:
new MineSweeper();
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
The components are created with the current LAF. When you first create the game, the default LAF is used to create all the buttons (and other components). This LAF allows you to change the background color of the buttons.
However, then you change the LAF. So when you reset the game board the buttons are now created with the System LAF. This LAF apparently does not allow you to change the background color of the button.
This should be easy to test. Create a GUI:
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JButton button = new JButton("testing");
button.setBackground(Color.RED);
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( button );
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
First test the code as above to see if the background of the button changes.
Then uncomment the LAF change and retest.
A possible solution so you are not dependent on the LAF is to use an Icon to represent the background color of the button. Then you can center any text on top of the Icon. Something like:
import java.awt.*;
import javax.swing.*;
public class ColorIcon implements Icon
{
private Color color;
private int width;
private int height;
public ColorIcon(Color color, int width, int height)
{
this.color = color;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(color);
g.fillRect(x, y, width, height);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI()
{
JPanel panel = new JPanel( new GridLayout(2, 2) );
for (int i = 0; i < 4; i++)
{
Icon icon = new ColorIcon(Color.RED, 50, 50);
JLabel label = new JLabel( icon );
label.setText("" + i);
label.setHorizontalTextPosition(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.CENTER);
panel.add(label);
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.setSize(200, 200);
f.setLocationRelativeTo( null );
f.setVisible(true);
}
}
Yep, I too just noticed, that the problem is here:
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new MineSweeper();
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
}
});
}
If you comment out the UIManager line, your code works. This line is only valid after the GUI has been created, and so doesn't take effect until new components are created. Note that I was working on minimizing your code to discover this, and was cutting out code to see what caused the problem until this was all that was left.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.UIManager;
#SuppressWarnings("serial")
public class MineSweeper extends JFrame implements ActionListener {
private static final Color BTN_COLOR = new Color(0X00, 0X94, 0XFF);
int int_dim = 11;
JButton[][] arr_btnField = new JButton[int_dim][int_dim];
JMenuBar menu_bar;
JMenu menu;
JMenuItem optionNew;
JPanel jpanel = new JPanel();
public MineSweeper() {
resetGame();
this.getContentPane().add(jpanel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(true);
this.setTitle("Minesweeper");
menu_bar = new JMenuBar();
menu = new JMenu("File");
menu.setMnemonic(KeyEvent.VK_F);
optionNew = new JMenuItem("Win");
optionNew.setMnemonic(KeyEvent.VK_W);
optionNew.addActionListener(this);
menu.add(optionNew);
menu_bar.add(menu);
this.setJMenuBar(menu_bar);
this.setPreferredSize(new Dimension(500, 500));
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public void resetGame() {
jpanel.removeAll();
arr_btnField = new JButton[int_dim][int_dim];
jpanel.setLayout(new GridLayout(0, int_dim));
for (int i = 0; i < arr_btnField.length; i++) {
for (int j = 0; j < arr_btnField[i].length; j++) {
arr_btnField[i][j] = new JButton();
arr_btnField[i][j].setBackground(BTN_COLOR);
jpanel.add(arr_btnField[i][j]);
}
}
jpanel.revalidate();
jpanel.repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
resetGame();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new MineSweeper();
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
}
});
}
}

FIXED: Java Memory Game - Java Will not display other images

UPDATE: I just coded each button manually. Thanks Anyways.
I am trying to make a Memory Game written in Java. For some reason Java renders all the images as the same. It appears to render the latest image to all buttons when clicked.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.ArrayList;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
/**
* #author Steven
*
*/
public class Memory extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int GRIDSIZE = 4;
private PicButton[][] liteBut = new PicButton[GRIDSIZE][GRIDSIZE];
private Random rand = new Random();
private ClassLoader cl = this.getClass().getClassLoader();
private String[] imagelist = {"images/image01.jpg", "images/image02.jpg", "images/image03.jpg", "images/image04.jpg"};
private ArrayList<String> images = new ArrayList<String>();
private volatile String icon = "";
public Memory() {
initGUI();
setTitle("Memory");
setResizable(false);
pack();
setLocationRelativeTo(null);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void initGUI() {
assignimages();
TitleLabel framedTitle = new TitleLabel("Memory");
add(framedTitle, BorderLayout.PAGE_START);
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new GridLayout(GRIDSIZE, GRIDSIZE));
add(centerPanel, BorderLayout.CENTER);
for (int row = 0; row < GRIDSIZE; row++) {
for (int col = 0; col < GRIDSIZE; col++) {
liteBut[row][col] = new PicButton(row, col);
if (row == 0) {
if (col == 0) {
icon = images.get(0);
System.out.println(icon);
} else if (col == 1) {
icon = images.get(1);
System.out.println(icon);
} else if (col == 2) {
icon = images.get(2);
System.out.println(icon);
} else if (col == 3) {
icon = images.get(3);
System.out.println(icon);
}
} else if (row == 1) {
if (col == 0) {
icon = images.get(0);
System.out.println(icon);
} else if (col == 1) {
icon = images.get(1);
System.out.println(icon);
} else if (col == 2) {
icon = images.get(2);
System.out.println(icon);
} else if (col == 3) {
icon = images.get(3);
System.out.println(icon);
}
}
liteBut[row][col].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PicButton button = (PicButton) e.getSource();
int row = button.getRow();
int col = button.getCol();
String id = button.getID();
System.out.println("Hi from " + id);
liteBut[row][col].setIcon(new ImageIcon(cl.getResource(icon)));
}
});
centerPanel.add(liteBut[row][col]);
}
}
}
private void assignimages() {
for (int x = 0; x < 4; x++) {
int i = rand.nextInt(GRIDSIZE);
images.add(imagelist[i]);
}
for (int x = 0; x < images.size(); x++) {
System.out.println(images.get(x));
}
}
public static void main(String[] args) {
try {
String className = UIManager.getCrossPlatformLookAndFeelClassName();
UIManager.setLookAndFeel(className);
} catch (Exception e) {
}
EventQueue.invokeLater(new Runnable() {
public void run() {
new Memory();
}
});
}
public class PicButton extends JButton {
private static final long serialVersionUID = 1L;
private static final int MAXSIZE = 150;
private int row = 0;
private int col = 0;
private String id = "";
//private Boolean hasPic;
public PicButton(int row, int col) {
this.row = row;
this.col = col;
id = Integer.toString(row) + Integer.toString(col);
System.out.println(id);
setBackground(Color.BLACK);
Dimension size = new Dimension(MAXSIZE, MAXSIZE);
setPreferredSize(size);
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
public String getID() {
return id;
}
public void setImage() {
setBackground(Color.RED);
//hasPic = true;
}
public void clearImage() {
setBackground(Color.BLACK);
//hasPic = false;
}
}
public class TitleLabel extends JLabel {
private static final long serialVersionUID = 1L;
public TitleLabel(String title) {
Font titleFont = new Font(Font.SERIF, Font.BOLD, 32);
setFont(titleFont);
setHorizontalAlignment(JLabel.CENTER);
setText(title);
setBackground(Color.BLACK);
setForeground(Color.WHITE);
setOpaque(true);
}
}
}
liteBut[row][col].setIcon(new ImageIcon(cl.getResource(icon)));
is assigning the last known value of icon as the image to the button (this is an instance field, so it remembers), this is not the "value" assigned to the button, but the last value assigned to icon by the for-loop in which you create the buttons, so basically, ALL the buttons will get the last icon, instead, supply the icon value the PictureButton itself so you can "update" the button when it's clicked.
This functionality could be self contained to the button itself, further making it easier to manage

Why do the panels disappear when i press UP?

This is my code for a game im making. At the moment im not worried about how the game functions I've been more so worried about the fact that each time I hit the UP button the panels disappear and sometimes when i hit the LEFT button as well. Is there an explanation to this can anyone help me understand why this happens?? I have a feeling it has something to do with my if statements but im not really sure. also, im messing around with the key listener and if you could give me some advice on key listeners like some dos and donts I really appreciate the help!!
import javax.swing.*;
import java.applet.*;
import java.awt.event.*;
import java.awt.*;
public class Game extends Applet implements ActionListener,KeyListener {
Image image;
MediaTracker tr;
JLabel label,computerLabel;
JPanel panel,computerPanel;
Button start,up,down;
Label result;
Dimension SIZE = new Dimension(50,50);
int x = 0;
int y = 0;
int w = 100;
int q = 100;
int WIDTH = 50;
int HEIGHT = 50;
//Player Integers
int zeroPosX,zeroPosY,xLeft,xUp;
//Computer integers
int compZeroPosX,compZeroPosY,compXLeft,compXUp;
//--------------------------------------
public void init() {
setLayout(new FlowLayout());
start = new Button("Start");
up = new Button("UP");
down = new Button("LEFT");
//PlayerPiece stuff
ImageIcon icon = new ImageIcon("playerpiece.png");
label = new JLabel(icon);
panel = new JPanel();
label.setVisible(true);
panel.add(label);
panel.setPreferredSize(SIZE);
//ComputerPiece Stuff
ImageIcon computerIcon = new ImageIcon("computerPiece.png");
computerPanel = new JPanel();
computerLabel = new JLabel(computerIcon);
computerLabel.setVisible(true);
computerPanel.add(computerLabel);
computerPanel.setPreferredSize(SIZE);
//===============================================
result = new Label("=========");
addKeyListener(this);
setSize(650,650);
up.addActionListener(this);
down.addActionListener(this);
start.addActionListener(this);
label.setSize(WIDTH,HEIGHT);
label.setLocation(0,0);
add(computerPanel);
add(panel);
add(start);
add(up);
add(down);
add(result);
}
//--------------------------------------
public void paint(Graphics g) {
Graphics2D firstLayer = (Graphics2D)g;
Graphics2D secondLayer = (Graphics2D)g;
Graphics2D thirdLayer = (Graphics2D)g;
secondLayer.setColor(Color.BLACK);
for(x=100; x<=500; x+=100)
for(y=100; y <=500; y+=100)
{
firstLayer.fillRect(x,y,WIDTH,HEIGHT);
}
for(w=150; w<=500; w+=100)
for(q=150; q <=500; q+=100)
{
secondLayer.fillRect(w,q,WIDTH,HEIGHT);
}
}
//--------------------------------------
public void actionPerformed(ActionEvent ae) {
int [] range = {50,0,0,0,0};
int selection = (int)Math.random()*5 ;
//~~~~~~~~~~~~~~~~~~~~~~~~~
//PlayerPositioning
zeroPosX = panel.getX();
zeroPosY = panel.getY();
xLeft = zeroPosX - 50;
xUp = zeroPosY - 50;
//ComputerPositioning
compZeroPosX = computerPanel.getX();
compZeroPosY = computerPanel.getY();
compXLeft = compZeroPosX - range[selection];
compXUp = compZeroPosY - range[selection];
//~~~~~~~~~~~~~~~~~~~~~~~~~~
Button user = (Button)ae.getSource();
//Starting the game
if(user.getLabel() == "Start") {
result.setText("=========");
//playersetup
label.setLocation(0,0);
panel.setLocation(300,500);
//============================
//npc setup
computerLabel.setLocation(0,0);
computerPanel.setLocation(500,300);
}
if(compZeroPosX >= 150) {
if(compZeroPosY >= 150) {
if(zeroPosX >= 150) {
if(zeroPosY >=150) {
if(user.getLabel() == "UP") {
panel.setLocation(zeroPosX,xUp);
}
else
computerPanel.setLocation(compZeroPosX,compXUp);
if(user.getLabel() == "LEFT") {
panel.setLocation(xLeft,zeroPosY);
}
else
computerPanel.setLocation(compXLeft,compZeroPosY);
if(panel.getX() < 150)
result.setText("GAME-OVER");
if(panel.getY() < 150)
result.setText("GAME-OVER");
}
}
}
}
}
#Override
public void keyPressed(KeyEvent kp) {
int keycode = kp.getKeyCode();
switch (keycode) {
case KeyEvent.VK_W:
panel.setLocation(xLeft,zeroPosY);
break;
}
}
#Override
public void keyReleased(KeyEvent kr) {
}
#Override
public void keyTyped(KeyEvent kt) {
}
}
Issues and suggestions:
You're mixing AWT (e.g., Applet, Button, Label) with Swing (e.g., JPanel, JLabel) dangerously and without need. Stick with Swing and get rid of all vestiges of AWT.
You're painting directly in a top-level window, here the Applet, a dangerous thing to do. Don't. Follow the Swing graphics tutorials and do your drawing in a JPanel's paintComponent method.
You're not calling the super method within your painting method override, another dangerous thing to do, and another indication that you're trying to do this without reading the important relevant tutorials.
Don't compare Strings using == or !=. Use the equals(...) or the equalsIgnoreCase(...) method instead. Understand that == checks if the two object references are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here.
You're trying to directly set the location of a component such as a JPanel without regard for the layout managers. Don't do this. Instead move logical (non-component) entities and display the movement in your graphics.
You can find links to the Swing tutorials and to other Swing resources here: Swing Info
Later we can talk why you should avoid applets of all flavors...
Myself, I'd move ImageIcons around a grid of JLabels and not directly use a painting method at all. For example,
To see, run the following code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class Game2 extends JPanel {
private static final String CPU_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/"
+ "Gorilla-thinclient.svg/50px-Gorilla-thinclient.svg.png";
private static final String PERSON_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d8/"
+ "Emblem-person-blue.svg/50px-Emblem-person-blue.svg.png";
private static final int SQR_WIDTH = 50;
private static final int SIDES = 10;
private static final Dimension SQR_SIZE = new Dimension(SQR_WIDTH, SQR_WIDTH);
private static final Color DARK = new Color(149, 69, 53);
private static final Color LIGHT = new Color(240, 220, 130);
private JLabel[][] labelGrid = new JLabel[SIDES][SIDES];
private Icon playerIcon;
private Icon computerIcon;
public Game2() throws IOException {
// would use images instead
playerIcon = createIcon(PERSON_PATH);
computerIcon = createIcon(CPU_PATH);
JPanel buttonPanel = new JPanel();
buttonPanel.add(new JButton(new StartAction("Start", KeyEvent.VK_S)));
buttonPanel.add(new JButton(new UpAction("Up", KeyEvent.VK_U)));
buttonPanel.add(new JButton(new LeftAction("Left", KeyEvent.VK_L)));
JPanel gameBrd = new JPanel(new GridLayout(SIDES, SIDES));
gameBrd.setBorder(BorderFactory.createLineBorder(Color.BLACK));
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
JLabel label = new JLabel();
label.setPreferredSize(SQR_SIZE);
label.setOpaque(true);
Color c = i % 2 == j % 2 ? DARK : LIGHT;
label.setBackground(c);
gameBrd.add(label);
labelGrid[i][j] = label;
}
}
setLayout(new BorderLayout());
add(buttonPanel, BorderLayout.PAGE_START);
add(gameBrd);
// random placement, just for example
labelGrid[4][4].setIcon(computerIcon);
labelGrid[5][5].setIcon(playerIcon);
}
private Icon createIcon(String path) throws IOException {
URL url = new URL(path);
BufferedImage img = ImageIO.read(url);
return new ImageIcon(img);
}
private abstract class MyAction extends AbstractAction {
public MyAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
}
private class StartAction extends MyAction {
public StartAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO start game code
}
}
// move all icons up
private class UpAction extends MyAction {
public UpAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// collection to hold label that needs to be moved
Map<JLabel, Icon> labelMap = new HashMap<>();
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
Icon icon = labelGrid[i][j].getIcon();
if (icon != null) {
int newI = i == 0 ? labelGrid.length - 1 : i - 1;
labelGrid[i][j].setIcon(null);
labelMap.put(labelGrid[newI][j], icon);
}
}
}
// move the icon after the iteration complete so as not to move it twice
for (JLabel label : labelMap.keySet()) {
label.setIcon(labelMap.get(label));
}
}
}
// move all icons left
private class LeftAction extends MyAction {
public LeftAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Map<JLabel, Icon> labelMap = new HashMap<>();
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
Icon icon = labelGrid[i][j].getIcon();
if (icon != null) {
int newJ = j == 0 ? labelGrid[i].length - 1 : j - 1;
labelGrid[i][j].setIcon(null);
labelMap.put(labelGrid[i][newJ], icon);
}
}
}
for (JLabel label : labelMap.keySet()) {
label.setIcon(labelMap.get(label));
}
}
}
private static void createAndShowGui() {
Game2 mainPanel = null;
try {
mainPanel = new Game2();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("Game2");
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(() -> {
createAndShowGui();
});
}
}

JPanel doesn't react to MouseEvents?

I'm trying to create a "Tic Tac Toe" game. I've chosen to create a variation of JPanel to represent each square. The class beneath represents one of 9 panels that together make up my game board.
Now the problem I'm having is that when I click the panel a 'X' should be displayed inside of the panel, but nothing happens. I'd very much appreciate it if someone steered me in the right direction.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TicTacToePanel extends JPanel implements MouseListener {
private boolean isPlayer1Turn = true;
private boolean isUsed = false;
private JLabel ticTacLbl = new JLabel();
public TicTacToePanel() {
setBorder(BorderFactory.createLineBorder(Color.BLACK));
addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
if (!isUsed) {
if (isPlayer1Turn) {
ticTacLbl.setForeground(Color.red);
ticTacLbl.setText("X");
add(ticTacLbl, 0);
isUsed = true;
} else {
ticTacLbl.setForeground(Color.blue);
ticTacLbl.setText("O");
add(ticTacLbl, 0);
isUsed = true;
}
} else {
}
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, new TicTacToePanel());
}
}
EDIT:
I simply added my label component in the constructor of my TicTacToePanel so that I no longer have to call revalidate() and I'm not adding components during runtime.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TicTacToePanel extends JPanel implements MouseListener{
private boolean isPlayer1Turn = true;
private boolean isUsed = false;
private JLabel ticTacLbl = new JLabel();
public TicTacToePanel(){
add(ticTacLbl, 0);
setBorder(BorderFactory.createLineBorder(Color.BLACK));
addMouseListener(this);
}
public void mouseClicked(MouseEvent e){
}
public void mousePressed(MouseEvent e){
if (!isUsed) {
if (isPlayer1Turn) {
ticTacLbl.setForeground(Color.red);
ticTacLbl.setText("X");
isUsed = true;
} else {
ticTacLbl.setForeground(Color.blue);
ticTacLbl.setText("O");
isUsed = true;
}
}
else{
}
}
public void mouseReleased(MouseEvent e){
}
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
public static void main(String[] args){
JOptionPane.showMessageDialog(null, new TicTacToePanel());
}
}
The GUI constructor:
public TicTacToeGUI(int gameMode){
if(gameMode == 0){
amountOfPanels = 9;
TicTacToePanel[] panelArr = new TicTacToePanel[amountOfPanels];
add(gamePanel, new GridLayout(3, 3));
setPreferredSize(new Dimension(100, 100));
for(int i = 0; i < amountOfPanels; i++){
panelArr[i] = new TicTacToePanel();
gamePanel.add(panelArr[i]);
}
}
else if(gameMode == 1){
amountOfPanels = 225;
TicTacToePanel[] panelArr = new TicTacToePanel[amountOfPanels];
add(gamePanel, new GridLayout(15, 15));
setPreferredSize(new Dimension(500, 500));
for(int i = 0; i < amountOfPanels; i++){
panelArr[i] = new TicTacToePanel();
gamePanel.add(panelArr[i]);
}
}
}
public static void main(String[] args){
JOptionPane.showMessageDialog(null, new TicTacToeGUI(0));
}
}
When you add/remove components at runtime, always call revalidate() afterwards. revalidate() makes the component refresh/relayout.
So just call revalidate() after you add the label and it will work.
If you're goal is to create a Tic Tac Toe game, then you may wish to re-think your current strategy of adding components to the GUI on the fly. Much better would be to create a grid of components, say of JLabel, and place them on the JPanel at program start up. This way you can change the pressed JLabel's text and color, and even its Icon if you want to be fancy during program run without having to add or remove components. For example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
#SuppressWarnings("serial")
public class TicTacToePanel extends JPanel {
private static final int ROWS = 3;
private static final int MY_C = 240;
private static final Color BG = new Color(MY_C, MY_C, MY_C);
private static final int PTS = 60;
private static final Font FONT = new Font(Font.SANS_SERIF, Font.BOLD, PTS);
public static final Color X_COLOR = Color.BLUE;
public static final Color O_COLOR = Color.RED;
private JLabel[][] labels = new JLabel[ROWS][ROWS];
private boolean xTurn = true;
public TicTacToePanel() {
setLayout(new GridLayout(ROWS, ROWS, 2, 2));
setBackground(Color.black);
MyMouse myMouse = new MyMouse();
for (int row = 0; row < labels.length; row++) {
for (int col = 0; col < labels[row].length; col++) {
JLabel label = new JLabel(" ", SwingConstants.CENTER);
label.setOpaque(true);
label.setBackground(BG);
label.setFont(FONT);
add(label);
label.addMouseListener(myMouse);
}
}
}
private class MyMouse extends MouseAdapter {
#Override // override mousePressed not mouseClicked
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
String text = label.getText().trim();
if (!text.isEmpty()) {
return;
}
if (xTurn) {
label.setForeground(X_COLOR);
label.setText("X");
} else {
label.setForeground(O_COLOR);
label.setText("O");
}
// information to help check for win
int chosenX = -1;
int chosenY = -1;
for (int x = 0; x < labels.length; x++) {
for (int y = 0; y < labels[x].length; y++) {
if (labels[x][y] == label) {
chosenX = x;
chosenY = y;
}
}
}
// TODO: check for win here
xTurn = !xTurn;
}
}
private static void createAndShowGui() {
TicTacToePanel mainPanel = new TicTacToePanel();
JFrame frame = new JFrame("Tic Tac Toe");
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();
}
});
}
}

Categories