Key action listener in Java Swing - java

I was making a Nokia snake game. I have already made the Frame and everything and already written my algo. Currently I was doing something so that I can perform actions using my keys (such as left, right, up, down).
I made a class named Frame that extends JFrame and implements action listener, somewhere I saw on Stack Overflow that I need to write my code in key released function so as to make my code run while I press the key.
But when I press the keys nothing happened.
Here is my Java code:
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class Frame extends JFrame implements KeyListener {
private static final int BOARD_SIZE = 51;
private static final int FRAME_SIZE = 700;
private static final Color SNAKE = Color.GREEN;
private static final Color BOARD = Color.WHITE;
private static final Color FOOD = Color.BLUE;
private static enum DIRECTION {
right, left, up, down;
}
private static DIRECTION curr = DIRECTION.right;
public Frame() {
super.setTitle("<<SNAKE GAME>>");
super.setSize(this.FRAME_SIZE, this.FRAME_SIZE);
GridLayout layout = new GridLayout(this.BOARD_SIZE, this.BOARD_SIZE);
super.setLayout(layout);
int val = this.randomNum(this.BOARD_SIZE * this.BOARD_SIZE - 7);
int count = 0;
for (int i = 0; i < this.BOARD_SIZE; i++) {
for (int j = 0; j < this.BOARD_SIZE; j++) {
JButton btn = new JButton();
super.add(btn);
this.buttons[i][j] = btn;
if (i == this.BOARD_SIZE / 2 + 1 && j > this.BOARD_SIZE / 2 - 5
&& j <= this.BOARD_SIZE / 2 + 6) {
this.snakeLL.snake.AddFirst(btn);
btn.setBackground(this.SNAKE);
} else if (count == val) {
btn.setBackground(FOOD);
count++;
} else {
btn.setBackground(this.BOARD);
count++;
}
}
}
super.addKeyListener(this);
super.setResizable(false);
super.setVisible(true);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyLocation();
if (key == KeyEvent.VK_UP) {
this.curr = DIRECTION.up;
}
}

, somewhere i saw on stack overflow that i need to write my code in key released function so as to.. make my code run while i press the key...
I don't think you got that advice here.
We always advise that you should be using Key Bindings. Read the section from the Swing tutorial on How to Use Key Bindings for basic information.
You can also check out Motion Using the Keyboard which contains a working example of using Key Bindings to animate a component.

Related

Java Swing JFrame doesn't open (Minesweeper game)?

I made a minesweeper game in Java using Swing, but when i run it, JFrame doesn't pop up? (No errors)
It runs, but no window shows up. Tried to debug with no success.
I really appreciate your help :)
Note that in class GameBoard:
imgs = new Image[13]; //Number of images used
//Loading images, used later
for (int i = 0; i < 13; i++) {
imgs[i] = (new ImageIcon(i + ".png")).getImage();
}
These lines load numbers representing images (13 images), created by me, and these imgs are loadid depending on the mouseadapter.
This is the MineSweeper class with the main method:
package minesweeper;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class MineSweeper extends JFrame {
public JLabel label;
public MineSweeper(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(600, 600);
this.setLocationRelativeTo(null);
this.setTitle("Minesweeper");
label = new JLabel("");
this.add(label, BorderLayout.SOUTH);
GameBoard game = new GameBoard(label);
this.add(game);
setResizable(false);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MineSweeper jf = new MineSweeper();
jf.setVisible(true);
}
});
}
}
An this is the GameBoard class:
package minesweeper;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GameBoard extends JPanel {
//Adding constant variables
private final int SIZE_OF_CELL = 20;
private static final int NUM_OF_ROWS = 20;
private static final int NUM_OF_CL = 20;
private final int SIZE_OF_FIELD = NUM_OF_ROWS * NUM_OF_CL;
//Adding other variables
private int NUM_OF_MINES_LEFT;
private Image[] imgs;
private static int[][] gameboard;
public static int mines = 40;
private int all_cells;
private static JLabel label;
boolean gamestarted = true; // Game is in progress, already started
//Constructor 1 parameter
public GameBoard(JLabel inputlabel) {
this.label = inputlabel;
imgs = new Image[13]; //Number of images used
//Loading images, used later
for (int i = 0; i < 13; i++) {
imgs[i] = (new ImageIcon(i + ".png")).getImage();
}
setDoubleBuffered(true);
addMouseListener(new Manager());
StartNewGame();
}
//Find mines around cell[i][j]
public static int FindMines(int ro, int co){
int cnt=0;
for(int y=-1;y<=1;y++){
for(int x = -1; x<=1;x++){
if(x==0 && y ==0) continue;
if(ro+y<0) continue;
if(co+x<0) continue;
if(gameboard[ro+y][co+x]==19) cnt++;
}
}
return cnt;
}
public static void StartNewGame(){
//NUM_OF_MINES_LEFT = 40; // Default value for number of mines is 30
gameboard = new int[20][20];
int minesleft=mines;
int row,col;
// int mines = NUM_OF_MINES_LEFT;
Random rng=new Random();
label.setText(Integer.toString(minesleft));
//initialize mine field
for(int i=0;i<20;i++){
for (int j=0;j<20;j++){
gameboard[i][j]=10;//default value is 10 -> its covered
}
}
//Set mines in random positions
while (mines>0){
row=rng.nextInt(NUM_OF_ROWS);
col=rng.nextInt(NUM_OF_CL);
if ((gameboard[row][col])!=19){
gameboard[row][col]+=9;;
minesleft--;
}
}
//Set numbers
for(int i=0;i<20;i++){
for (int j=0;j<20;j++){
if(gameboard[i][j]==19) gameboard[i][j]=19;
if(gameboard[i][j]==10){
gameboard[i][j]+= FindMines(i,j);
}
}
}
}
//public int FindEmptyCells(){
//}
#Override
public void paintComponent(Graphics grap){
int gamewon=0;
int[][] temp = new int[20][20];
for(int i=0;i<20;i++){
for(int j=0;j<20;j++){
temp[i][j]=gameboard[i][j];
if(gamestarted && temp[i][j]==9) gamestarted=false;
if(gamestarted == false){
if(temp[i][j]==19){
temp[i][j]= 9;
}else if(temp[i][j]==29){//10+11 for mark
temp[i][j]=11;
}else if(temp[i][j]>9){
temp[i][j]=10;
}
}else{
if (temp[i][j] > 19)
temp[i][j] = 11;
else if (temp[i][j] > 9) {
temp[i][j] = 10;
gamewon=1;
}
}
int toload= temp[i][j];
grap.drawImage(imgs[toload],j*15,i*15,this);
}
}
if(gamestarted==true && gamewon==0){
gamestarted=false;
label.setText("You won!");
}else if(gamestarted==false){
label.setText("You Lost!");
}
}
class Manager extends MouseAdapter{
#Override
public void mousePressed(MouseEvent ev){
boolean newpaint = false;
//Get event coordinates
int x= ev.getX();
int y= ev.getY();
int hit_cl= x / 15;
int hit_row= y/ 15;
if(gamestarted==false){
StartNewGame();
repaint();
}
if( (x < 20 * 15) && (y < 20 * 15) ){
if(ev.getButton() == MouseEvent.BUTTON3){
if(gameboard[hit_cl][hit_row] > 9){
newpaint=true;
if(gameboard[hit_cl][hit_row] <= 19){
if(mines > 0){
mines--;
String show=Integer.toString(mines);
gameboard[hit_cl][hit_row]+=11;
label.setText(show);
}else{
label.setText("Marks: 0");
}
}else{
mines++;
String show=Integer.toString(mines);
label.setText(show);
gameboard[hit_cl][hit_row]-=11;
}
}
}else{
if(gameboard[hit_cl][hit_row] > 19){
return;
}
if((gameboard[hit_cl][hit_row] > 9) && (gameboard[hit_cl]
[hit_row] <29)){
newpaint=true;
gameboard[hit_cl][hit_row]-=10;
if(gameboard[hit_cl][hit_row] == 9) gamestarted=false;
//if(gameboard[hit_cl][hit_row] == 10); //find_empty();
}
}
if(newpaint==true) repaint();
}
}
}
}
What did you do to debug? :)
The most obvious reason for the program to run forever is that it never leaves a loop.
while (mines>0){
row=rng.nextInt(NUM_OF_ROWS);
col=rng.nextInt(NUM_OF_CL);
if ((gameboard[row][col])!=19){
gameboard[row][col]+=9;;
minesleft--;
}
}
This part of your StartNewGame() method (GameBoard class) is causing an endless loop, mines variable is never updated inside the loop.
Giving a fast look to your code i can note some bad practices, for example :
You should not control the game state or setting labels text inside
paintComponent() method. This method is called automatically, and it
should only paint all the components. All the "logic" inside it
could slow down your program, because you can not control how many
times the method gets called (of course you can force the method to
be called with repaint() method, for example).
You should follow java naming conventions
Hope this helps :)

How to set a dynamic setIcon on a JRadioButton

I've some issues with my code for studies, it's our first time with Java and I don't know how to change the icon of JRadioButtons contents in an array.
package exo_02_01;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JRadioButton;
import javax.swing.JToolBar;
#SuppressWarnings("serial")
public class ControleEtiquette extends JToolBar {
private ImageIcon[] m_iconesBoutons = new ImageIcon[18];
private JRadioButton[] m_boutons = new JRadioButton[6];
private String m_nomsIcones[] = { "bhgauche", "bhcentre", "bhdroite", "bvhaut", "bvcentre", "bvbas" };
private static final int NUMBER_BUTTONS = 6;
public ControleEtiquette() {
super();
chargerIcones();
creerBoutons();
}
private void chargerIcones() {
for (int i = 0; i < NUMBER_BUTTONS; i++) {
m_iconesBoutons[i] = new ImageIcon("RESGRAF/" + m_nomsIcones[i] + ".gif");
m_iconesBoutons[i + NUMBER_BUTTONS] = new ImageIcon("RESGRAF/" + m_nomsIcones[i] + "R.gif");
m_iconesBoutons[i + NUMBER_BUTTONS * 2] = new ImageIcon("RESGRAF/" + m_nomsIcones[i] + "B.gif");
}
}
private void creerBoutons() {
for (int i = 0; i < m_boutons.length; ++i) {
m_boutons[i] = new JRadioButton(m_iconesBoutons[i]);
add(m_boutons[i]);
m_boutons[i].addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e)
{
((JRadioButton) e.getSource()).setIcon(m_iconesBoutons[0]);
}
public void mouseClicked(MouseEvent e) {
((JRadioButton) e.getSource()).setIcon(m_iconesBoutons[NUMBER_BUTTONS * 2 - 1]);
}
public void mouseExited(MouseEvent e) {
((JRadioButton) e.getSource()).setIcon(m_iconesBoutons[5]);
}
});
if (i == 2)
addSeparator();
}
}
My code in my chargerBoutons() method work well, but my aim is to set the icon according to the current button. I tried to do like
((JRadioButton) e.getSource()).setIcon(m_iconesBoutons[i]);
But i is undefined in this scope.
How can I fix it ?
Thanks
Actually, I think you set the icon correctly, but you have to ask for an update of the UI...
So add the call updateUI() at the end of your creerBoutonsmethod. (it apply on the toolbar (ie: your object).
see JToolbar

My way to get X and Y index of buttons inside GridLayout

This is related to How to get X and Y index of element inside GridLayout? post and its answers.
For whatever reason none of them suggested to extend JButton to include its position in the grid and in associated array of buttons.
I have made the following illustration that simply displays button's coordinates when it's clicked.
Extended JButton:
package buttons_array;
import javax.swing.*;
#SuppressWarnings("serial")
public class ButtonWithCoordinates extends JButton {
int coordX;
int coordY;
public ButtonWithCoordinates(String buttonText, int coordX, int coordY) {
super(buttonText);
this.coordX = coordX;
this.coordY = coordY;
}
/**
* #return the coordX
*/
public int getCoordX() {
return coordX;
}
/**
* #return the coordY
*/
public int getCoordY() {
return coordY;
}
}
sample GUI:
package buttons_array;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonsArray implements ActionListener {
private ButtonWithCoordinates buttons[][];
private int nRows;
private int nCols;
private JFrame frame;
private JPanel panel;
public ButtonsArray(int x, int y) {
if (x > 0 && y > 0) {
nRows = x;
nCols = y;
buttons = new ButtonWithCoordinates[nRows][nCols];
for (int i=0; i < nRows; ++i) {
for (int j=0; j < nCols; ++j) {
buttons[i][j] = new ButtonWithCoordinates(" ", i, j);
buttons[i][j].addActionListener(this);
}
}
} else {
throw new IllegalArgumentException("Illegal array dimensions!!!");
}
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
ButtonWithCoordinates button = (ButtonWithCoordinates) e.getSource();
button.setText(button.getCoordX() + ", " + button.getCoordY());
}
public void GUI() {
if (buttons == null) { throw new NullPointerException("Array is not initialized!!!"); }
frame = new JFrame();
panel = new JPanel();
frame.setContentPane(panel);
panel.setLayout(new GridLayout(nRows, nCols));
for (int i=0; i < nRows; ++i) {
for (int j=0; j < nCols; ++j) {
panel.add(buttons[i][j]);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ButtonsArray(3, 5).GUI();
}
});
}
}
Now my questions:
Have I been reinventing the wheel here? I mean, is there a more straightforward way to achieve the same?
Is it in any way inferior to searching through the array each time we need to find the coordinates?
The original version of this example used extension:
GridButton extends JButton
The updated version was predicated on the colloquy seen here. While extension may be appropriate in some contexts, a few alternatives are mentioned here; a client property is particularly convenient. Identifying a button from its grid coordinates is also easy:
private static final int N = 5;
List<JButton> list = new ArrayList<>();
…
private JButton getGridButton(int r, int c) {
int index = r * N + c;
return list.get(index);
}

JButton will not change update image when using .setIcon(ICON);

I already checked this duplicate question and other similar ones and it didn't help. I am trying to add an png to a button when it is clicked. The program is a variable sized tic-tac-toe game for school.
Right now I have:
private ImageIcon X_MARK = new ImageIcon("x.png");
private ImageIcon O_MARK = new ImageIcon("o.gif");
private JButton[][] cells;
...
cells = new JButton[size][size];
JPanel board = new JPanel(new GridLayout(size, size));
board.setBorder(new LineBorder(Color.BLACK, 1));
ButtonListener listener = new ButtonListener();
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++) {
cells[i][j] = new JButton();
cells[i][j].addActionListener(listener);
board.add(cells[i][j]);
}
JFrame ttt = new JFrame();
ttt.add(board);
ttt.setTitle("Show GUI Components");
ttt.setSize(60*size, 60*size);
ttt.setLocation(0, 0);
ttt.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ttt.setVisible(true);
...
class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int i, j;
for (i = 0; i < size; i++)
for (j = 0; j < size; j++)
if (e.getSource() == cells[i][j]) {
if ((i + j) % 2 == 0) {
cells[i][j].setBackground(Color.GREEN);
cells[i][j].setIcon(X_MARK);
} else {
cells[i][j].setBackground(Color.CYAN);
cells[i][j].setIcon(O_MARK);
}
}
}
}
That is all the relevant code I think. I am using Eclipse and I have x.png and o.png in the src folder and the bin folder of the the project. I have also tried a couple of variants I have seen on SO and google searches like, new ImageIcon("C:/Users/BigBoy/workspace_1/EventDriven/src/x.png");, new ImageIcon("src/x.png");, and some other ones involving getClass().getResource among other things. I don't know what else to try. I know I've done this in the past and didn't have this much trouble.
I added .setBackground(Color.GREEN); just to make sure my clicks were registering properly and they are, the problem to me seems to be with the declaring/initializing of the ImageIcon.
NOTE: Right now my button listener just makes the checker board pattern, I will get to actually putting each player's mark after I figure out this icon problem.
You need to understand resources which is what you will want to use. They are located relative to the class files. If the images are with the class files, then
get your image as a resource
Create an ImageIcon from the Image.
i.e., something like:
package whateverpackeyouareusing;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
public class DefaultFoo {
public static void main(String[] args) throws IOException {
String resource = "x.png";
URL url = Class.class.getResource(resource);
BufferedImage img = ImageIO.read(url);
Icon icon = new ImageIcon(img);
JOptionPane.showMessageDialog(null, icon);
}
}
Edit: A better example per Andrew Thompson:
package some.package;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
public class PlayWithImages {
public static final String X_RESOURCE = "x.png";
private Icon xIcon;
public PlayWithImages() throws IOException {
URL xImgUrl = getClass().getResource(X_RESOURCE);
Image xImg = ImageIO.read(xImgUrl);
xIcon = new ImageIcon(xImg);
}
public Icon getXIcon() {
return xIcon;
}
public static void main(String[] args) {
try {
PlayWithImages playWithImages = new PlayWithImages();
Icon xIcon = playWithImages.getXIcon();
JOptionPane.showMessageDialog(null, xIcon);
} catch (IOException e) {
e.printStackTrace();
}
}
}

addActionListener for a button array?

Hello I'm trying to do a ratsuk game that is chess but only with the knight.
import javax.swing.JButton;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.Icon;
import javax.swing.ImageIcon;
public class Knight {
private Icon image;
private int w;
private int k;
private Random rand;
public Knight() {
image = new ImageIcon(getClass().getResource("redKnight.gif"));
w = rand.nextInt(9);
k = rand.nextInt(9);
}
public void Caballo(JButton[][] matriz, int i, int j) {
matriz[i][j].setIcon(image);
matriz[i][j].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//Execute when button is pressed
matriz[i][j].setBackground(Color.RED);
}
});
}
}
So I was trying to do a recursive method which I am not really sure will work.
But the problem is that inside the addActionListener, netbeans tells me that the variables have to be final which I don't really get why. Once I run it the image doesn't show at all
Here is the rest of the code
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.*;
import javax.swing.JPanel;
import java.util.Random;
public class Tablero {
private JButton[][] mesa;
private Random rad;
public Tablero() {
mesa = new JButton[8][8];
}
public void cuadriculado(JFrame ventana) {
JPanel panel = new JPanel(new GridLayout(8, 8, 0, 0));
for (int i = 0; i < mesa.length; i++) {
for (int j = 0; j < mesa[0].length; j++) {
mesa[i][j] = new JButton();
mesa[i][j].setPreferredSize(new Dimension(40, 40));
panel.add(mesa[i][j]);
}
}
for (int r = 0; r < mesa.length; r++) {
for (int t = 0; t < mesa[0].length; t++) {
if (r % 2 == 0 || r == 0) {
if (t % 2 == 0 || t == 0) {
mesa[r][t].setBackground(Color.BLACK);
} else {
mesa[r][t].setBackground(Color.WHITE);
}
} else {
if (t % 2 == 0 || t == 0) {
mesa[r][t].setBackground(Color.WHITE);
} else {
mesa[r][t].setBackground(Color.BLACK);
}
}
}
}
ventana.setContentPane(panel);
ventana.setSize(500, 500);
ventana.setVisible(true);
Knight kn =new Knight();
kn.Caballo(mesa, rad.nextInt(9), rad.nextInt(9));
}
}
Any help will greatly appreciated. I am really new to Java and none of this was explained to me, so I have been struggling a lot.
matriz[i][j].setBackground(Color.RED);
You are trying to access "matriz" from an annonymous inner class so the variable needs to either be a class variable or a final variable.
I would question why you made this method belong to the Knight class. This method should be part of the Tablero class since that is where you define the array as a class variable. Then you won't have the compiler problem.
But if you really want to keep the method in the Knight class then the code should be:
public void Caballo(**final** JButton[][] matriz, int i, int j) {
Once i run it the image doesnt show at all
image = new ImageIcon(getClass().getResource("redKnight.gif"));
You just create an Icon. You need to add it to a label and then add the label to the GUI.
i am really new to java and nothing of this was explained to me
Start by reading the Swing tutorial. Maybe the section on How to Use Icons would be a good place to start.
Try changing this
public void Caballo(JButton[][] matriz, int i, int j)
to this
public void Caballo(final JButton[][] matriz, final int i, final int j)
The reason the variables need to final is because they are accessed inside the ActionListener's actionPerformed. Java needs to know that these variables arent going to change before it gets to use them. It goes back to a Java Garbage collector thing that I would prefer not ramble on about, but that's the jest of it.

Categories