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 :)
Related
I am trying to code a board game in Java.
I have 11 classes including Main. Board class which extends JPanel and draws the board image as well as the dice image. The class Player which extends JCoponent and implements Runnable(Thread). Every player instance is a pawn-animation that it is moving across the board. The player class draws the pawn on the board.
Pattern
How the code it looks like :
Board b=new Board();
Player p=new Player();
b.add(p);
JPanel panel=new JPanel();
panel.add(b);
add(panel); //adding the panel to the frame.
The problem is that I can't have more than one pawn simultaneously on the board. I have already tried to re-paint all the players (as non-animation) in another class but it didn't work. I also tried JLayeredPane but maybe I am doing something wrong. Unfortunately, I can't change the above pattern so don't make this suggestion.
Thank you in advance for your help.
P.S: I can't post any code because its huge.
P.P.S: more clarifications will be given, if you ask me.
EDIT: I reform my question. Is it possible to have two animations simultaneously on the same panel? if the answer is a yes ..how I can do that?
It's most likely possible to have many components moving all at once. Either use javax.swing.Timer ou SwingWorker for this to work.
Here is a quick example showing you this. It puts 16 pawns on a board and moves them randomly from one place to another.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestAnimation {
private static final String PAWN_URL = "http://files.chesskidfiles.com/images_users/tiny_mce/BoundingOwl/bishop_happywhite.png";
private Image pawn;
private Map<Location, Pawn> pawnLocations = new HashMap<>();
private Board board;
private Timer timer;
private JLayeredPane glassPane;
public TestAnimation() {
try {
pawn = new ImageIcon(new URL(PAWN_URL)).getImage();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
private static class Location {
public final int row;
public final int col;
public Location(int row, int col) {
super();
this.row = row;
this.col = col;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + col;
result = prime * result + row;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Location other = (Location) obj;
return (col == other.col && row == other.row);
}
}
private static class Cell extends JPanel {
private final Location location;
public Cell(Location location) {
super(new BorderLayout());
this.location = location;
setOpaque(true);
setBackground(((location.row + location.col) % 2) == 0 ? Color.WHITE : Color.BLACK);
}
#Override
protected void addImpl(Component comp, Object constraints, int index) {
while (getComponentCount() > 0) {
remove(0);
}
super.addImpl(comp, constraints, index);
}
}
private static class Board extends JPanel {
private Map<Location, Cell> cells = new HashMap<>();
public Board() {
super(new GridLayout(8, 8));
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
Cell cell = new Cell(new Location(i, j));
add(cell);
cells.put(new Location(i, j), cell);
}
}
}
public void add(Pawn pawn, Location location) {
cells.get(location).add(pawn);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public Cell getCell(Location location) {
return cells.get(location);
}
}
private class Pawn extends JComponent {
public Pawn() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(pawn, 0, 0, getWidth(), getHeight(), this);
}
}
protected void initUI() {
JFrame frame = new JFrame(TestAnimation.class.getSimpleName());
board = new Board();
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 2; j++) {
Location location = new Location(i, j);
Pawn aPawn = new Pawn();
board.add(aPawn, location);
pawnLocations.put(location, aPawn);
}
}
for (int i = 0; i < 8; i++) {
for (int j = 6; j < 8; j++) {
Location location = new Location(i, j);
Pawn aPawn = new Pawn();
board.add(aPawn, location);
pawnLocations.put(location, aPawn);
}
}
timer = new Timer(7000, new Animation());
timer.setInitialDelay(0);
timer.setRepeats(true);
timer.setCoalesce(false);
glassPane = new JLayeredPane();
glassPane.setOpaque(false);
frame.add(board);
frame.setGlassPane(glassPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
timer.start();
glassPane.setVisible(true);
}
public class Animation implements ActionListener {
private Map<Location, Pawn> futureLocations;
private Random random = new Random();
private Timer subTimer;
private List<Pawn> movingPawns;
private Map<Pawn, Point> originalCoordinates = new HashMap<>();
private Map<Pawn, Point> futureCoordinates = new HashMap<>();
#Override
public void actionPerformed(ActionEvent e) {
futureLocations = new HashMap<>();
movingPawns = new ArrayList<>();
for (Pawn p : pawnLocations.values()) {
int row = random.nextInt(8);
int col = random.nextInt(8);
Location location;
while (futureLocations.containsKey((location = new Location(row, col)))) {
row = random.nextInt(8);
col = random.nextInt(8);
}
futureLocations.put(location, p);
Cell futureCell = board.getCell(location);
futureCoordinates.put(p, SwingUtilities.convertPoint(futureCell, 0, 0, glassPane));
movingPawns.add(p);
}
for (Pawn p : movingPawns) {
Point locationInGlassPane = SwingUtilities.convertPoint(p.getParent(), 0, 0, glassPane);
glassPane.add(p);
p.setLocation(locationInGlassPane);
originalCoordinates.put(p, locationInGlassPane);
}
subTimer = new Timer(50, new AnimationSteps());
subTimer.setInitialDelay(0);
subTimer.setCoalesce(true);
subTimer.setRepeats(true);
subTimer.start();
}
public class AnimationSteps implements ActionListener {
private int step = 0;
#Override
public void actionPerformed(ActionEvent e1) {
if (step < 50 + 1) {
for (Pawn p : movingPawns) {
Point p1 = originalCoordinates.get(p);
Point p2 = futureCoordinates.get(p);
int x = (int) (p1.x + ((p2.x - p1.x) * (double) step / 50));
int y = (int) (p1.y + ((p2.y - p1.y) * (double) step / 50));
p.setLocation(x, y);
}
} else {
for (Entry<Location, Pawn> e : futureLocations.entrySet()) {
board.add(e.getValue(), e.getKey());
}
board.revalidate();
subTimer.stop();
pawnLocations = futureLocations;
}
step++;
}
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestAnimation().initUI();
}
});
}
}
Maybe your problem is trying to develop a program with a thread for each object, most popular games run with a single thread, two at most. The reason: It will be very complex to synchronize threads with each other, not to mention that your performance will be poor. Even the graphics engine in Java is single threaded and that means you won't have two threads drawing at the same time.
I have an int a; in the main class and i'd like to use it in the Action Listner class. I've checked for answers but didnt really understand what i had to do since I'm still just a begginer in any kind of programing so if its possible i'd really appreciate a simple solution.
here is my code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Start {
public static void main(String[] args){
JFrame okno = new JFrame("Nonogram");
okno.setVisible(true);
okno.setSize(700, 700);
okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
okno.add(panel);
JButton [][] gumbi = new JButton[15][15];
JPanel polje = new JPanel(new GridLayout(15, 15));
panel.add(polje, BorderLayout.CENTER);
int a = 1;
int b = 1;
for(int i = 0; 0 < 15; i++){
for(int j = 0; j < 15; j++){
if(i < 5 && j < 5){
gumbi[i][j] = new JButton();
gumbi[i][j].setBackground(Color.RED);
//gumbi[i][j].addActionListener(new Listener(gumbi));
polje.add(gumbi[i][j]);
}else if(i < 5 || j < 5){
gumbi[i][j] = new JButton();
gumbi[i][j].setBackground(Color.YELLOW);
//gumbi[i][j].addActionListener(new Listener(gumbi));
polje.add(gumbi[i][j]);
gumbi[i][j].setEnabled(false);
}else{
if(Math.random() <= 0.6){
gumbi[i][j] = new JButton();
gumbi[i][j].setBackground(Color.WHITE);
gumbi[i][j].addActionListener(new Listener(gumbi));
gumbi[i][j].setText("3");
polje.add(gumbi[i][j]);
}else {
gumbi[i][j] = new JButton();
gumbi[i][j].setBackground(Color.WHITE);
gumbi[i][j].addActionListener(new Listener(gumbi));
gumbi[i][j].setText("4");
polje.add(gumbi[i][j]);
}
}
if(gumbi[i][j].getText() == "3"){
a += 1;
}
if(i == 14 && j == 14){
gumbi[i][j].setText("" + a);
}
}
}
}
}
and this is what i have in Action Listener
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
public class Listener implements ActionListener {
JButton[][] gumbi;
public Listener(JButton[][] gumbi) {
this.gumbi = gumbi;
}
public void actionPerformed(ActionEvent e){
JButton gumb = (JButton) e.getSource();
if( gumb.getBackground() == Color.WHITE){
gumb.setBackground(Color.BLACK);
} else if (gumb.getBackground() == Color.BLACK){
gumb.setBackground(Color.WHITE);
}
}
}
Thanks for your time.
Define that int a as static with public access and remove it's definition from within your main method as below:
public class Start {
public static int a = 1;//static since you want to use in static method
public static void main(String[] args){
And then in your listener call, you could use .variable like below:
int number = Start.a;
in your current implementation int a is defined in main method and anything declared in a method will be local to the method, you really have to get the basics right , for now u can define the int a public static int a outside the main method , and then access the variable with the class name reference as shown below
public class Start {
public static int a ;
public static void main() {
//your logic
}
}
public class Listener implements ActionListener {
int temp = Start.a;
}
While I agree with another answer that you can make a static in the main class, there might be a neater solution, even though that it is not exactly what was asked.
Instead of attempting to get the value of a from the main, it makes more sense to send along the value of a in the constructor of Listener.
public class Listener implements ActionListener {
JButton[][] gumbi;
final int a;
public Listener(JButton[][] gumbi, int a) {
this.gumbi = gumbi;
this.a = a;
}
...
In the main method simply change the method that adds the listener as follows:
gumbi[i][j].addActionListener(new Listener(gumbi, a));
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);
}
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.
I have a simple puzzle game. There is an image consisting of 16 tiles (randomly placed). Images are stored in an array and when game is launched they're added to main JPanel.
Game works in this way : Each image has atributes 'place' and 'number'. 'Place' is the current place on grid (either correct or not) and 'number' is the desired place for the image. When a user clicks image their 'place' and 'number' attributes are checked. If they match nothing happens. If not game checks if any image is currently in memory. If there is none, then this image's 'place' and 'number' are stored. If there is some image in memory, then the currently clicked image's 'plac'e is checked with stored image's 'number'. When they match - their places are exchanged. This part works properly. But now, I'm calling addComponent method on my JPanel with updated images and simply nothing happens. Shouldn't the new images be added to JPanel replacing the old ones ?
package Bonus;
import javax.swing.*;
import java.util.Random;
import java.awt.event.*;
import java.awt.*;
class Puzzle extends JPanel implements ActionListener {
private int selected_nr=-1;
private int selected_pl=-1;
private boolean memory=false;
private static Img[] images;
public Puzzle(){
JFrame f = new JFrame("Smile");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.setSize(252,252);
f.setVisible(true);
setLayout(new GridLayout(4, 4));
images = new Img[16];
int[] buttons = new int[16];
for(int i=0; i<16; i++){
buttons[i] = i;
}
int rand;
int temp;
Random random;
random = new Random(System.currentTimeMillis());
for (int i = 0; i < buttons.length; i++) {
rand = (random.nextInt() & 0x7FFFFFFF) % buttons.length;
temp = buttons[i];
buttons[i] = buttons[rand];
buttons[rand] = temp;
}
for (int i = 0; i < 16; i++) {
images[i] = new Img(i, buttons[i]);
}
addComponents(images);
}
public void addComponents(Img[] im){
this.removeAll();
for(int i=0; i<16; i++){
im[i].addActionListener(this);
im[i].setPreferredSize(new Dimension(53,53));
add(im[i]);
}
this.validate();
}
public void actionPerformed(ActionEvent e) {
Img b = (Img)(e.getSource());
int num = b.getNumber();
int pl = b.getPlace();
if(!(b.rightPlace())){
if(memory){
if(pl == selected_nr){
images[pl].setPlace(selected_pl);
images[selected_pl].setPlace(selected_nr);
selected_nr = -1;
selected_pl = -1;
memory = false;
addComponents(images);
}
else{
System.out.println("Try other image");
}
}
else{
memory = true;
selected_nr = num;
selected_pl = pl;
}
}
else{
System.out.println("OK !");
}
}
public static void main(String args[]) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Puzzle();
}
});
}
}
class Img extends JButton {
int number;
int place;
ImageIcon img;
public Img(int p, int n){
number = n;
place = p;
img = new ImageIcon("u"+number+".jpg", BorderLayout.CENTER);
setIcon(img);
}
public boolean rightPlace(){
boolean correct=false;
if(number == place){
correct = true;
}
return correct;
}
public void setPlace(int i){
place = i;
}
public int getNumber(){
return number;
}
public int getPlace(){
return place;
}
}
EDIT: Changed the code to use the answers, but still no luck. addComponents() gets updated images[] but doesn't revalidate them.
Rather than relying on precut image files, here's an example of slicing an existing image and shuffling the resulting pieces. It combines the helpful (+1) suggestions of both #Frederick and #akf.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ImageLabelPanel extends JPanel implements ActionListener {
private static final int N = 4;
private final List<JLabel> list = new ArrayList<JLabel>();
private final Timer timer = new Timer(1000, this);
ImageLabelPanel() {
this.setLayout(new GridLayout(N, N));
BufferedImage bi = null;
try {
bi = ImageIO.read(new File("image.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
int w = bi.getWidth() / N;
int h = bi.getHeight() / N;
BufferedImage b = bi.getSubimage(c * w, r * h, w, h);
list.add(new JLabel(new ImageIcon(b)));
}
}
createPane();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setVisible(true);
timer.start();
}
private void createPane() {
this.removeAll();
for (JLabel label : list) add(label);
this.validate();
}
#Override
public void actionPerformed(ActionEvent e) {
Collections.shuffle(list);
createPane();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ImageLabelPanel();
}
});
}
}
You are adding all of your components again to your JPanel without actually removing any of them. In your addComponents() method, I would first call removeAll(). You might want to rename that method to highlight the side-effects, as it no longer would only be adding components. Perhaps, resetComponents() would be better.
After changing the components, you need to 'refresh' the Swing component by calling invalidate() or revalidate().