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);
});
}
}
Related
i have a problem with a grid i created. the buttons of this smallerGrid will not appear until i hover over them with the mouse.
each smallerGrid consists of a 3x3 grid which fits into a biggerGrid also 3x3 grid that conatins the smaller grids.
that is a brief explanation of how i am constructing the sudoku grid.
You will find the code below.
Thank you in advance.
import java.awt.*;
import javax.swing.*;
public class MyGridLayout {
private int filledFields = 0;
private JFrame mainFrame;
private JPanel panelForSolvingButton;
private JPanel smallerGridPanel;
private boolean solvingButtonAppeared = false;
SudokuCell[][] biggerGrid = new SudokuCell[10][10];
MyGridLayout() {
mainFrame = new JFrame("sudoku-solver");
mainFrame.setLayout(new BorderLayout());
smallerGridPanel = new JPanel(new GridLayout(3, 3));
mainFrame.add(smallerGridPanel, BorderLayout.CENTER);
panelForSolvingButton = new JPanel();
panelForSolvingButton.setLayout(new FlowLayout(FlowLayout.RIGHT));
mainFrame.add(panelForSolvingButton, BorderLayout.SOUTH);
mainFrame.pack();
mainFrame.setSize(600,600);
mainFrame.setLocationRelativeTo(null);
mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainFrame.setResizable(false);
addRegularButtons();
}
void addRegularButtons() {
int currentCol = 0;
int currentRow = 0;
int smallerGridFirstCol = 0;
int firstRowOfLayer = 0;
for (int layerOfGrids = 0; layerOfGrids < 3; ++layerOfGrids) {
for (int gridOfLayer = 0; gridOfLayer < 3; ++gridOfLayer) {
JComponent smallerGrid = new JPanel(new GridLayout(3, 3));
smallerGrid.setBorder(BorderFactory.createLineBorder(Color.BLACK));
smallerGridPanel.add(smallerGrid);
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 3; ++k) {
var cell = new SudokuCell(currentRow, currentCol);
smallerGrid.add(cell);
biggerGrid[currentRow][currentCol++] = cell;
cell.addActionListener(new CellActionListener(this));
}
currentRow++;
currentCol = smallerGridFirstCol;
}
smallerGridPanel.revalidate();
currentRow = firstRowOfLayer;
smallerGridFirstCol += 3;
currentCol = smallerGridFirstCol;
}
firstRowOfLayer += 3;
currentRow = firstRowOfLayer;
smallerGridFirstCol = currentCol = 0;
}
mainFrame.revalidate();
mainFrame.setVisible(true);
}
// checking if the solving process can begin
// (filled fields must at least reach 17)
void makeSolveButtonAppear() {
JButton solveButton = new JButton();
solveButton.setBackground(Color.white);
solveButton.setFont(new Font("Arial", Font.PLAIN, 20));
solveButton.setOpaque(false);
solveButton.setText("Solve ?");
panelForSolvingButton.add(solveButton);
}
public boolean isSolvingButtonAppeared() {
return solvingButtonAppeared;
}
public void setSolvingButtonAppeared(boolean solvingButtonAppeared) {
this.solvingButtonAppeared = solvingButtonAppeared;
}
public int getFilledFields() {
return filledFields;
}
public void setFilledFields(int filledFields) {
this.filledFields = filledFields;
}
public JPanel getPanelForSolvingButton() {
return panelForSolvingButton;
}
public static void main(String[] args) {
var gridLayout = new MyGridLayout();
}
}
package mainFrame;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.MatteBorder;
public class SudokuCell extends JButton {
private int x;
private int y;
public SudokuCell(int x, int y) {
this.x = x;
this.y = y;
setBackground(Color.white);
setOpaque(true);
setFont(new Font("Arial", Font.PLAIN, 20));
}
#Override
public int getX() {
return x;
}
#Override
public int getY() {
return y;
}
}
package mainFrame;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class CellActionListener implements ActionListener {
private int clicked = 0;
MyGridLayout currentGrid;
CellActionListener(MyGridLayout currentGrid) {
this.currentGrid = currentGrid;
}
#Override
public void actionPerformed(ActionEvent e) {
if(clicked ==0) currentGrid.setFilledFields(currentGrid.getFilledFields()+1);
//clicked (number of clicks) must remain between 0 and 9
clicked = (clicked % 9) + 1;
((JButton) e.getSource()).setText(Integer.toString(clicked));
//first click on the button means an entry has been made
//which means one less needed filledField out of 17
if(!currentGrid.isSolvingButtonAppeared() && currentGrid.getFilledFields() >= 17) {
currentGrid.makeSolveButtonAppear();
currentGrid.setSolvingButtonAppeared(true);
}
}
}```
My goal is to create a list with a length controlled by another component where each item's value can be edited.
My attempt uses an editable JComboBox that has a certain number of elements. In my code below, however, the selected index keeps changing to -1, which does not allow me to modify the item. Is there a way to select and edit an item using JComboBox?
//cb is a JComboBox with elements of type ComboItem. idx is defined elsewhere.
cb.addItemListener(new ItemListener() {
#SuppressWarnings("unchecked")
#Override
public void itemStateChanged(ItemEvent e) {
if(e.getStateChange() == ItemEvent.SELECTED)
idx = ((JComboBox<ComboItem>) e.getSource()).getSelectedIndex();
System.out.println("idx:"+idx);
}
});
//Pressing enter should commit changes.
cb.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_ENTER) {
String parse = ((JTextComponent) cb.getEditor().getEditorComponent()).getText();
parse = parse.substring(parse.lastIndexOf(":")+1).replaceAll("[^0-9]+", ""); //Processes edits.
cb.getItemAt(idx).change("Layer "+idx, Integer.parseInt(parse)); //This method should change the
System.out.println("selected item:"+cb.getSelectedItem()); // data for each item.
}
}
});
//Editing the text in the JComboBox and pressing the enter key should update the selected item.
JComboBox is not required, so feel free to suggest a different component if it is a better choice for this task.
After a day's worth of trial and error, I finally made a working solution. Instead of using a JComboBox, which was probably not designed to perform the desired task, I made a JScrollPane that adds a child JPanel every time a button is pressed. Each panel has a text field object that can be customized and a button to delete it. In my case, I added a DocumentFilter that allows positive < 5 digit integers.
I cannot figure out how to remove the spacing between the added panels before the scroll bar appears, so please comment a solution if you have one. Also, if there are any other improvements that can be made, please comment those suggestions as well.
Scroll Panel
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.JLabel;
#SuppressWarnings("serial")
public class TestScrollPane extends JPanel {
private int w,h;
private JPanel content;
private JScrollPane scroll;
private JButton add;
private JLabel getTextLabel;
private JButton getTextBtn;
/**
* Create the panel.
*/
public TestScrollPane(int width, int height) {
setLayout(null);
w = width; h = height;
scroll = new JScrollPane();
scroll.setBounds(0, 0, w, h);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
add(scroll);
content = new JPanel();
scroll.setViewportView(content);
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
add = new JButton("+");
add.setBounds(0, h, 89, 23);
add(add);
getTextLabel = new JLabel("");
getTextLabel.setBounds(10, 425, 215, 14);
add(getTextLabel);
getTextBtn = new JButton("Get Text");
getTextBtn.setBounds(225, 425, 215, 14);
getTextBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String a[] = getText(),
b = "";
b += "[";
for(int i = 0; i < a.length - 1; i++)
b += a[i]+", ";
b += a[a.length-1]+"]";
System.out.println(b);
getTextLabel.setText(b);
}
});
add(getTextBtn);
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("adding "+content.getComponentCount());
content.add(new ScrollItem(content.getComponentCount()));
validate();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(w, h);
}
public String[] getText() {
String out[] = new String[content.getComponentCount()],s;
for(int i = 0; i < out.length; i++)
out[i] = (s = ((ScrollItem) content.getComponent(i)).out) == null ? "0" : s;
return out;
}
private class ScrollItem extends JPanel {
private JTextField text;
private JButton del;
private int idx;
private String out;
public ScrollItem(int id) {
idx = id;
setBounds(0, idx*20, w-5, 20);
setLayout(null);
text = new JTextField();
text.setBounds(0, 0, (w-5)*3/4, 20);
text.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
out = text.getText();
}
});
AbstractDocument d = (AbstractDocument) text.getDocument();
d.setDocumentFilter(new TextFilter(4));
del = new JButton("X");
del.setBounds((w-5)*3/4, 0, (w-5)/4, 20);
del.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
content.remove(idx);
for(int i = idx; i < content.getComponentCount(); i++)
((ScrollItem) content.getComponent(i)).moveUp();
content.validate();
content.repaint();
System.out.println("Removed "+idx);
}
});
add(text);
add(del);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(w-5, 20);
}
public void moveUp() {
idx--;
content.validate();
content.repaint();
}
}
private class TextFilter extends DocumentFilter {
private int max;
public TextFilter(int maxChars) {
max = maxChars;
}
#Override
public void insertString(FilterBypass fb, int offs, String str, AttributeSet a) throws BadLocationException {
System.out.println("insert");
if ((fb.getDocument().getLength() + str.length()) <= max && str.matches("\\d+"))
super.insertString(fb, offs, str, a);
else
showError("Length: "+((fb.getDocument().getLength() + str.length()) <= max)+" | Text: "+str.matches("\\d+")+" | Str: "+str);
}
#Override
public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a) throws BadLocationException {
System.out.println("replace");
if ((fb.getDocument().getLength() + str.length() - length) <= max && str.matches("\\d+"))
super.replace(fb, offs, length, str, a);
else
showError("Length: "+((fb.getDocument().getLength() + str.length() - length) <= max)+" | Text: "+str.matches("\\d+")+" | Str: "+str);
}
private void showError(String cause) {
JOptionPane.showMessageDialog(null, cause);
}
}
}
Test Window
import java.awt.EventQueue;
import javax.swing.JFrame;
public class TestWindow {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestWindow window = new TestWindow();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public TestWindow() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 435, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestScrollPane(430, 247));
}
}
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();
});
}
}
Here is my code. I want to know which l was clicked and then in a new frame, display that ImageIcon.
The e.getSource() is not working...
final JFrame shirts = new JFrame("T-shirts");
JPanel panel = new JPanel(new GridLayout(4, 4, 3, 3));
for (int i = 1; i < 13; i++) {
l = new JLabel(new ImageIcon("T-shirts/"+i+".jpg"), JLabel.CENTER);
l.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
l.setFont(l.getFont().deriveFont(20f));
panel.add(l);
}//end of for loop
panel.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e)
{
sizes = new JFrame("Shopping");
sizes.setVisible(true);
sizes.setSize(500, 500);
sizes.setLocation(100,200);
shirts.dispose();
if(e.getSource()==l){//FIX
sizes.add(l);
}//end of if
}
});
shirts.setContentPane(panel);
shirts.setSize(1000, 1000);
shirts.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
shirts.setVisible(true);
If you add your MouseListener directly to your JLabels, then you can display the pressed label's icon easily in a JOptionPane:
#Override
public void mousePressed(MouseEvent mEvt) {
JLabel label = (JLabel) mEvt.getSource();
Icon icon = label.getIcon();
JOptionPane.showMessageDialog(label, icon);
}
For example:
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;
public class FooMouseListener extends JPanel {
private GetImages getImages;
public FooMouseListener() throws IOException {
getImages = new GetImages();
setLayout(new GridLayout(GetImages.SPRITE_ROWS, GetImages.SPRITE_COLS));
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
for (int i = 0; i < GetImages.SPRITE_CELLS; i++) {
JLabel label = new JLabel(getImages.getIcon(i));
add(label);
label.addMouseListener(myMouseAdapter);
}
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
Icon icon = label.getIcon();
JOptionPane.showMessageDialog(label, icon, "Selected Icon", JOptionPane.PLAIN_MESSAGE);
}
}
private static void createAndShowGui() {
FooMouseListener mainPanel = null;
try {
mainPanel = new FooMouseListener();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("FooMouseListener");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class GetImages {
private static final String SPRITE_PATH = "http://th02.deviantart.net/"
+ "fs70/PRE/i/2011/169/0/8/blue_player_sprite_sheet_by_resetado-d3j7zba.png";
public static final int SPRITE_ROWS = 6;
public static final int SPRITE_COLS = 6;
public static final int SPRITE_CELLS = SPRITE_COLS * SPRITE_ROWS;
private List<Icon> iconList = new ArrayList<>();
public GetImages() throws IOException {
URL imgUrl = new URL(SPRITE_PATH);
BufferedImage mainImage = ImageIO.read(imgUrl);
for (int i = 0; i < SPRITE_CELLS; i++) {
int row = i / SPRITE_COLS;
int col = i % SPRITE_COLS;
int x = (int) (((double) mainImage.getWidth() * col) / SPRITE_COLS);
int y = (int) ((double) (mainImage.getHeight() * row) / SPRITE_ROWS);
int w = (int) ((double) mainImage.getWidth() / SPRITE_COLS);
int h = (int) ((double) mainImage.getHeight() / SPRITE_ROWS);
BufferedImage img = mainImage.getSubimage(x, y, w, h);
ImageIcon icon = new ImageIcon(img);
iconList.add(icon);
}
}
// get the Icon from the List at index position
public Icon getIcon(int index) {
if (index < 0 || index >= iconList.size()) {
throw new ArrayIndexOutOfBoundsException(index);
}
return iconList.get(index);
}
public int getIconListSize() {
return iconList.size();
}
}
Have you tried this?
public void mouseClicked(MouseEvent e)
{
sizes = new JFrame("Shopping");
sizes.add(l);
sizes.setVisible(true);
sizes.setSize(500, 500);
sizes.setLocation(100,200);
shirts.dispose();
//Remove the "e.getSource()" part.
}
It will automatically display the image, because you are assigning the Image Name to it, in the same segment as the Addition to the new JFrame.
Let me know of the outcome
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){}
}