I can't get two images to overlap each other in a JFrame. I have tried using one JPanel and two JPanels, and nothing has worked. I have looked into trying out JLayeredPane(s), but to no avail. I have lots of classes that interact so I don't know if showing my code will help, but here is my not-fully-implemented code:
import javax.swing.JLabel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLayeredPane;
import javax.swing.ImageIcon;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
public class MazeDisplay implements KeyListener
{
static JFrame window;
static JPanel backPanel, userPanel;
JLabel user, cpu, maze1, maze2;
User u;
public MazeDisplay(String x, Cell[][] maize1, Cell[][] maize2) throws IOException
{
maze1 = new JLabel(new ImageIcon(createMazeImage(maize1)));
maze2 = new JLabel(new ImageIcon(createMazeImage(maize2)));
user = new JLabel(new ImageIcon("user.png"));
cpu = new JLabel(new ImageIcon("cpu.png"));
u = new User(maize1);
window = new JFrame(x);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(400, 400, 986, 509);
backPanel = (JPanel) window.getContentPane();
backPanel.setLayout(null);
maze1.setBounds(0, 0, 480, 480);
maze2.setBounds(500, 0, 480, 480);
backPanel.add(maze1);
backPanel.add(maze2);
userPanel = new JPanel(null);
user.setBounds(0, 0, 30, 30);
cpu.setBounds(500, 0, 30, 30);
userPanel.add(user);
userPanel.add(cpu);
window.add(userPanel);
window.setResizable(false);
window.setVisible(true);
}
public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public boolean isLegal(int dir)
{
boolean ret = false; Cell[][] maze = u.getMaize();
if(0 <= u.xPos() && u.xPos() < maze.length && 0 <= u.yPos() && u.yPos() < maze.length)
{
if(dir == 0) ret = (maze[u.xPos()][u.yPos()].isOpen(dir) && maze[u.xPos()][u.yPos()-1].isOpenO(dir));
else if(dir == 1) ret = (maze[u.xPos()][u.yPos()].isOpen(dir) && maze[u.xPos()+1][u.yPos()].isOpenO(dir));
else if(dir == 2) ret = (maze[u.xPos()][u.yPos()].isOpen(dir) && maze[u.xPos()][u.yPos()+1].isOpenO(dir));
else if(dir == 3) ret = (maze[u.xPos()][u.yPos()].isOpen(dir) && maze[u.xPos()-1][u.yPos()].isOpenO(dir));
}
return ret;
}
public static BufferedImage createMazeImage(Cell[][] maze) throws IOException
{
BufferedImage[][] iMaze = new BufferedImage[maze.length][maze.length];
for(int i = 0; i < iMaze.length; i++)
for(int j = 0; j < iMaze.length; j++)
{
String cellDir = maze[i][j] + ".png";
iMaze[i][j] = new BufferedImage(30, 30, BufferedImage.TYPE_INT_RGB);
Graphics2D icon = iMaze[i][j].createGraphics();
icon.drawImage(ImageIO.read(new File(cellDir)), 0, 0, null);
}
int xOff = 0, yOff = 0;
BufferedImage mazeImage = new BufferedImage(maze.length * 30, maze.length * 30, BufferedImage.TYPE_INT_RGB);
Graphics2D g = mazeImage.createGraphics();
for(int r = 0; r < iMaze.length; r++)
{
for(int c = 0; c < iMaze.length; c++)
{
g.drawImage(iMaze[r][c], xOff, yOff, null);
yOff += 30;
}
yOff = 0;
xOff += 30;
}
return mazeImage;
}
public static void main(String[] args) throws IOException
{
MazeGen g = new MazeGen(16);
MazeGen h = new MazeGen(16);
MazeDisplay m = new MazeDisplay("Maze Game", g.getMaze(), h.getMaze());
}
}
Edit: My apologies for being so vague, I was extremely tired and irritated at the time. Even though I was vague, the help was very much appreciated, as I got it working using GridBagLayout as MadProgrammer suggested.
So, this is a really quick example.
It makes use of a GridBagLayout to allow two components to occupy the same space at the same time. It then uses a simple Timer to update the location of the player, to demonstrate that the basic concept works.
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private JLabel player;
public TestPane() throws IOException {
BufferedImage tileImg = ImageIO.read(getClass().getResource("Tile.jpg"));
BufferedImage playerImg = ImageIO.read(getClass().getResource("Mario.png"));
Icon tileIcon = new ImageIcon(tileImg);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
player = new JLabel(new ImageIcon(playerImg));
gbc.gridx = 0;
gbc.gridy = 0;
add(player, gbc);
for (int row = 0; row < 10; row++) {
for (int col = 0; col < 10; col++) {
gbc.gridx = col;
gbc.gridy = row;
add(new JLabel(tileIcon), gbc);
}
}
Timer timer = new Timer(500, new ActionListener() {
private int x = 0;
private int y = 0;
#Override
public void actionPerformed(ActionEvent e) {
x++;
if (x > 9) {
x = 0;
y++;
}
if (y > 9) {
y = 0;
}
GridBagLayout layout = (GridBagLayout) getLayout();
GridBagConstraints gbc = layout.getConstraints(player);
gbc.gridx = x;
gbc.gridy = y;
layout.setConstraints(player, gbc);
revalidate();
}
});
timer.start();
}
}
}
Because of the way that the API works, components are displayed in LIFO order, this means that the player component must be added first.
You could also combine this with a JLayeredPane which would give you control over the z-ordering of the components
Disclaimer...
While this solution does work, it's not very easy to maintain or manage. A better solution would be to follow a custom painting route, this will give you much more control over what is painted and when and where.
Take a look at Performing Custom Painting for some basic details
Layered panes should work fine if well-implemented, here is more information about it, how to use it and some examples and codes for it:
https://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html
Related
I'm new to Java Programming. Recently I'm developing a mini game with JSWing. However, after coding for awhile the in-game FPS dropped terribly. When I tracked it on Task Manager I had result like this:
Can someone tell me what's wrong? I only used loops, JLabel with icons, Paint Graphics methods, mouseMotionEvent in my code.
Here is the code in the main game
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Game extends JPanel {
int numb = 2;
int pts = 5;
Kitty[] Kitties = new Kitty[4];
public Game() {
for (int i = 0; i < Kitties.length; i++)
Kitties[i] = new Kitty();
}
#Override
public void paint(Graphics graphics) {
BufferedImage img = null;
try {
img = ImageIO.read(getClass().getResourceAsStream("city.jpg"));
} catch (IOException e) {
System.out.println("java io");
}
graphics.drawImage(img, 0, 0, null);
//paints square objects to the screen
for (int i = 0; i < numb;i++) {
Kitties[i].paint(graphics);
}
}
public void update(TheJPanel frame) {
if (frame.a >= 0 && frame.a < 500) numb = 2;
if (frame.a>= 500) numb = 3;
for (int i = 0; i< numb; i++) {
int disty = 500 - Kitties[i].squareYLocation;
int distx = Kitties[i].squareXLocation - frame.x;
if ( Kitties[i].squareYLocation < 600 && disty <= 5 && disty >= -80 && distx < 260 && distx > -100){
frame.a +=pts;
if (Kitties[i].kittype == 6) frame.a += pts;
if (frame.a >= 500) {
Kitties[i].fallSpeed = Kitties[i].FallSpeedlvl2();
pts = 10;
}
Kitties[i].squareYLocation = -200;
Kitties[i].generateKittype();
Kitties[i].generateRandomXLocation();
Kitties[i].generateRandomFallSpeed();
frame.point.setText("Point:" + String.valueOf(frame.a));
frame.lives.setText("Lives:" + String.valueOf(frame.count));
}
if(Kitties[i].squareYLocation > 610){
frame.count--;
Kitties[i].generateKittype();
Kitties[i].generateRandomXLocation();
Kitties[i].generateRandomFallSpeed();
Kitties[i].squareYLocation = -200;
}
if (Kitties[i].squareYLocation >=605) frame.catFall(Kitties[i].squareXLocation);
if(Kitties[i].squareYLocation <= 610){
Kitties[i].squareYLocation += Kitties[i].fallSpeed;
}
}
}
public static void main(String[] args) throws InterruptedException {
Game game = new Game();
TheJPanel frame = new TheJPanel();
frame.add(game);
frame.setVisible(true);
frame.setSize(1000, 1000);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setTitle("Saving kitties");
frame.setResizable(false);
frame.setLocationRelativeTo(null);
while (frame.count>0) {
game.update(frame);
game.repaint();
Thread.sleep(4);
}
if (frame.count == 0) {
JOptionPane.showMessageDialog(null, "You lost!", "Game over!", JOptionPane.ERROR_MESSAGE);
game.setVisible(false);
frame.getContentPane().removeAll();
frame.getContentPane().repaint();
frame.bask.setVisible(false);
frame.background.setVisible(false);
}
}
}
Here is the code for the main Jframe
package game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
/**
*
* #author Imba Store
*/
public class TheJPanel extends JFrame implements MouseMotionListener {
protected int x;
protected int a = 0;
protected int count = 20;
protected JLabel bask = new JLabel();
protected JLabel background = new JLabel();
protected JLabel point = new JLabel();
protected JLabel lives = new JLabel();
Timer fall;
protected int time =0;
public TheJPanel() {
this.addMouseMotionListener(this);
InitContent();
}
protected void InitContent() {
Icon img = new ImageIcon(getClass().getResource("basket.png"));
bask.setIcon(img);
Icon themes = new ImageIcon(getClass().getResource("city2.png"));
background.setIcon(themes);
background.setBounds(0, 699, 1000, 300);
point.setFont(new java.awt.Font("Trebuchet MS", 1, 35));
point.setText("Point:" + String.valueOf(a));
point.setBounds(20,908,240,50);
point.setForeground(Color.white);
lives.setBounds(800, 908,200,50);
lives.setFont(new java.awt.Font("Trebuchet MS", 1, 35));
lives.setForeground(Color.white);
lives.setText("Point:" + String.valueOf(count));
point.setOpaque(false);
add(point);
add(lives);
add(bask);
add(background);
bask.setSize(400,148);
}
#Override
public void mouseMoved (MouseEvent me)
{
x = me.getX();
background.setBounds(0, 699, 1000, 300);
bask.setBounds(x, 700, 400, 148);
}
#Override
public void mouseDragged (MouseEvent me)
{
}
public void catFall(int getX){
Icon fell = new ImageIcon(getClass().getResource("kitty-fall.png"));
JLabel fellcat = new JLabel();
fellcat.setIcon(fell);
fellcat.setBounds(getX, 760, 220, 220);
add(fellcat);
add(background);
fall = new Timer(1500, new ActionListener(){
#Override
public void actionPerformed(ActionEvent ae) {
getContentPane().remove(fellcat);
}
});
fall.setRepeats(false);
fall.start();
}
}
And this is the class for the falling cats
package game;
/**
*
* #author Imba Store
*/
import javax.swing.*;
import java.awt.*;
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.Random;
import javax.imageio.ImageIO;
public final class Kitty extends JLabel {
protected int squareXLocation;
protected int squareYLocation = -200;
protected int fallSpeed = 1;
protected int kittype;
Random rand = new Random();
public int generateRandomXLocation(){
return squareXLocation = rand.nextInt(800);
}
public int generateRandomFallSpeed(){
return fallSpeed = rand.ints(3, 4).findFirst().getAsInt();
}
public int FallSpeedlvl2() {
return fallSpeed = rand.ints(3,7).findFirst().getAsInt();
}
public int generateKittype() {
return kittype = rand.ints(1,8).findFirst().getAsInt();
}
#Override
public void paint(Graphics g) {
BufferedImage img = null;
BufferedImage thugcat = null;
try {
img = ImageIO.read(getClass().getResourceAsStream("kitty.png"));
thugcat = ImageIO.read(getClass().getResourceAsStream("thug-kitty.png"));
} catch (IOException e) {
System.out.println("Java IO");
}
if (kittype == 6) {
g.drawImage(thugcat, squareXLocation, squareYLocation, null);
}
else g.drawImage(img, squareXLocation,squareYLocation,null);
}
public Kitty(){
generateRandomXLocation();
generateRandomFallSpeed();
generateKittype();
}
public void paint(Graphics graphics) {
BufferedImage img = null;
try {
img = ImageIO.read(getClass().getResourceAsStream("city.jpg"));
Custom painting is done by overriding paintComponent(...) not paint(). The first statement should then be super.paintComponent().
A painting method is for painting only. Don't do I/O in the painting method. This will cause the image to be read every time you repaint the panel.
Thread.sleep(4);
Sleeping for 4ms is not enough. That will attempt to repaint 250 times a second which is too often. There is no need for the frame rate to be that fast.
Kitty[] Kitties = new Kitty[4];
Variable names should not start with an upper case character. Most of your names are correct. Be consistent!
point.setBounds(20,908,240,50);
Don't use setBounds(). Swing was designed to be used with layout managers. Set a layout manager for you background and then add the components.
public int FallSpeedlvl2() {
Methods should NOT start with an upper case character. Again, most are correct. Be Consistent!!!
I am referencing this link, however this won't ultimately fix my problem (i.e I run my program from someone else's computer). How to deal with "java.lang.OutOfMemoryError: Java heap space" error (64MB heap size). Right now, I have a game board that has 10x10 squares, but I need to increase this to 100x100, but when I do, I get this error. What is the best way to increase my game board size while avoiding this error? Current output is below, Code should compile and run. Thanks!
GameBoard Class:
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.*;
public class GameBoard {
private final JPanel board = new JPanel(new BorderLayout(3, 3));
private JButton[][] c1squares = new JButton[10][10];
private JPanel c1Board, c2Board;
private final JLabel messagec1 = new JLabel("Player 1 Board");
JToolBar tool = new JToolBar();
Insets Margin = new Insets(0,0,0,0);
int squares = 10;
int space = 100;
ImageIcon icon = new ImageIcon(new BufferedImage(space, space, BufferedImage.TYPE_INT_ARGB));
GameBoard() {
initializeGui();
}
public final void initializeGui() {
board.setBorder(new EmptyBorder(5, 5, 5, 5));
tool.setFloatable(false);
board.add(tool, BorderLayout.PAGE_START);
tool.add(messagec1);
c1Board = new JPanel(new GridLayout(0, 10));
c1Board.setBorder(new LineBorder(Color.BLACK));
board.add(c1Board);
for (int i = 1; i < c1squares.length; i++) {
for (int j = 0; j < c1squares[i].length; j++) {
JButton b = new JButton();
b.setMargin(Margin);
b.setIcon(icon);
if ((j % 2 == 1 && i % 2 == 1) || (j % 2 == 0 && i % 2 == 0)) {
b.setBackground(Color.WHITE);
} else {
b.setBackground(Color.BLACK);
}
c1squares[j][i] = b;
}
}
for (int i = 1; i < squares; i++) {
for (int j = 0; j < squares; j++) {
c1Board.add(c1squares[j][i]);
}
}
public final JComponent getGui() {
return board;
}
public final JComponent getGui2() {
return board2;
}
}
BattleShipFinal Class:
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class BattleshipFinal {
public static void main(String[] args) {
GameBoard gb = new GameBoard();
JFrame frame = new JFrame("Battleship - Server");
frame.add(gb.getGui());
frame.setLocationByPlatform(true);
frame.setMinimumSize(frame.getSize());
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(900,900));
frame.setMinimumSize(new Dimension(900,900));
frame.setLocation(50,50);
frame.pack();
frame.setVisible(true);
}
}
In case you are curious, and to illustrate what people said in the comments, you could have a custom JPanel painting the squares.
If you ever need to respond to mouse events, add a MouseListener to your panel, which will take care of the selected square (haven't added that part, but the selected field inside the Square class is a hint).
I removed stuff from your code, just to demonstrate this painting part.
GameBoard :
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class GameBoard {
private JPanel board;
private final Square[][] c1squares = new Square[10][10];
GameBoard() {
initializeGui();
}
public final void initializeGui() {
for (int i = 0; i < c1squares.length; i++) {
for (int j = 0; j < c1squares[i].length; j++) {
Square square = new Square();
if ((j % 2 == 1 && i % 2 == 1) || (j % 2 == 0 && i % 2 == 0)) {
square.setBackground(Color.WHITE);
} else {
square.setBackground(Color.BLACK);
}
c1squares[i][j] = square;
}
}
board = new BoardPanel(c1squares);
board.setBorder(new EmptyBorder(5, 5, 5, 5));
}
public final JComponent getGui() {
return board;
}
private class BoardPanel extends JPanel {
Square[][] squares;
public BoardPanel(final Square[][] squares) {
this.squares = squares;
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
for (int i = 0; i < squares.length; i++) {
for (int j = 0; j < squares[i].length; j++) {
Square currentSquare = squares[i][j];
System.out.println("Managing square " + i + " " + j);
g.setColor(currentSquare.getBackground());
g.fillRect(i * width / squares.length, j * height / squares.length, width / squares.length,
height / squares.length);
}
}
}
}
private class Square {
boolean isSelected;
Color background;
public boolean isSelected() {
return isSelected;
}
public void setSelected(final boolean isSelected) {
this.isSelected = isSelected;
}
public Color getBackground() {
return background;
}
public void setBackground(final Color background) {
this.background = background;
}
}
}
BattleshipFinal :
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class BattleshipFinal {
public static void main(final String[] args) {
GameBoard gb = new GameBoard();
JFrame frame = new JFrame("Battleship - Server");
JComponent board = gb.getGui();
frame.add(board);
frame.setLocationByPlatform(true);
//frame.setMinimumSize(frame.getSize());
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
//frame.setPreferredSize(new Dimension(100, 100));
board.setMinimumSize(new Dimension(100, 100));
board.setPreferredSize(new Dimension(100, 100));
frame.setMinimumSize(new Dimension(100, 100));
frame.setLocation(50, 50);
frame.pack();
frame.setVisible(true);
}
}
Okay, I figured out the solution for this. Here is the output:
The way I fixed this was by changing these lines of code:
private JButton[][] c1squares = new JButton[100][100];
int squares = 100;
int space = 1000;
c1Board = new JPanel(new GridLayout(0, 100));
for(int i = 0; i < sizex/10; i++)
{
for(int u = 0; u < sizey/10; u++)
{
JPanel temp = new JPanel();
//temp.setSize(10, 10);
temp.setBounds(i*10,u*10, 10, 10);
//temp.setLocation(i*10, u*10);
Random r = new Random();
int rand = r.nextInt(4-0);
if(rand == 0)
{
temp.setBackground(Color.GREEN);
}
else if(rand == 1)
{
temp.setBackground(Color.BLUE);
}
else if(rand == 2)
{
temp.setBackground(Color.RED);
}
else if(rand == 3)
{
temp.setBackground(Color.MAGENTA);
}
frame.add(temp);
}
}
In my code here the logic behind it works(in my head) and this code works if I instead divide sizex and y by 100 and making the size of the box 100 instead of 10.
In the case that it is currently in it produces boxes which seem to be the right size but there are only a few down the side of the application instead of a full screen.
Here is a pic of the app:
You could...
Use a GridLayout...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RandomCells {
public static void main(String[] args) {
new RandomCells();
}
public RandomCells() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridLayout(10, 10, 0, 0));
Random rnd = new Random();
Color[] colors = new Color[]{Color.GREEN, Color.BLUE, Color.RED, Color.MAGENTA};
for (int col = 0; col < 10; col++) {
for (int row = 0; row < 10; row++) {
JPanel cell = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
};
int color = rnd.nextInt(4);
cell.setBackground(colors[color]);
add(cell);
}
}
}
}
}
See How to Use GridLayout for more details
Or you could...
Use a GridBagLayout...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RandomCells {
public static void main(String[] args) {
new RandomCells();
}
public RandomCells() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
Random rnd = new Random();
Color[] colors = new Color[]{Color.GREEN, Color.BLUE, Color.RED, Color.MAGENTA};
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
for (int col = 0; col < 10; col++) {
gbc.gridx = 0;
for (int row = 0; row < 10; row++) {
JPanel cell = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
};
int color = rnd.nextInt(4);
cell.setBackground(colors[color]);
add(cell, gbc);
gbc.gridx++;
}
gbc.gridy++;
}
}
}
}
See How to Use GridBagLayout for more details.
What's the difference?
GridLayout will always size it's components evenly so that they fill the available space, so if your resize the window for example, all the panels will change size in an attempt to fill the available space.
GridBagLayout (in the configuration I've shown) will continue to honor the preferredSize of the panels, so as you resize the window, the panels won't change size
You can achieve this without using GridLayout and simply by redefining the paintComponent method of JPanel component and filling it with random colors in sequential rectangles like this :
Here's the code used to achieve the above image :
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
#SuppressWarnings("serial")
public class RandomPaint extends JFrame
{
private JPanel panel;
private int dx, dy;
private Random random;
private Color color;
public RandomPaint()
{
dx = 50;
dy = 50;
random = new Random();
setBounds(100, 100, 500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel()
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < getHeight(); i = i + dx)
{
for (int j = 0; j < getWidth(); j = j + dy)
{
color = new Color(random.nextInt(255),
random.nextInt(255), random.nextInt(255));
g2d.setColor(color);
g2d.fillRect(j, i, dx, dy);
}
}
}
};
add(panel);
setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
}
catch (ClassNotFoundException | InstantiationException
| IllegalAccessException
| UnsupportedLookAndFeelException e)
{
}
new RandomPaint();
}
});
}
}
Code:
JFrame frame = new JFrame("example");
frame.setSize(100,100);
frame.setLayout(new GridLayout(10,10));
for(int i = 0; i < 100/10; i++)
{
for(int u = 0; u < 100/10; u++)
{
JPanel temp = new JPanel();
temp.setBounds(i*10,u*10, 10, 10);
Random r = new Random();
int rand = r.nextInt(4-0);
if(rand == 0)
{
temp.setBackground(Color.GREEN);
}
else if(rand == 1)
{
temp.setBackground(Color.BLUE);
}
else if(rand == 2)
{
temp.setBackground(Color.RED);
}
else if(rand == 3)
{
temp.setBackground(Color.MAGENTA);
}
frame.add(temp);
}
frame.setVisible(true);
}
I found this source code from http://zetcode.com/tutorials/javagamestutorial/puzzle/
And I wanted to play around with it. It's a picture puzzle game, however when I run the program, it doesn't show up scrambled and I was wondering how will I go about this to get it scrambled? I'm still new to programming and I just wanted to play around with this to learn a bit. Thank you!
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Puzzle extends JFrame implements ActionListener {
private JPanel centerPanel;
private JButton button;
private JLabel label;
private Image source;
private Image image;
int[][] pos;
int width, height;
public Puzzle() {
pos = new int[][] {
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
{9, 10, 11}
};
centerPanel = new JPanel();
centerPanel.setLayout(new GridLayout(4, 4, 0, 0));
ImageIcon sid = new ImageIcon(Puzzle.class.getResource("icesid.jpg"));
source = sid.getImage();
width = sid.getIconWidth();
height = sid.getIconHeight();
add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH);
add(centerPanel, BorderLayout.CENTER);
for ( int i = 0; i < 4; i++) {
for ( int j = 0; j < 3; j++) {
if ( j == 2 && i == 3) {
label = new JLabel("");
centerPanel.add(label);
} else {
button = new JButton();
button.addActionListener(this);
centerPanel.add(button);
image = createImage(new FilteredImageSource(source.getSource(),
new CropImageFilter(j*width/3, i*height/4,
(width/3)+1, height/4)));
button.setIcon(new ImageIcon(image));
}
}
}
setSize(325, 275);
setTitle("Puzzle");
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new Puzzle();
}
public void actionPerformed(ActionEvent e) {
JButton button = (JButton) e.getSource();
Dimension size = button.getSize();
int labelX = label.getX();
int labelY = label.getY();
int buttonX = button.getX();
int buttonY = button.getY();
int buttonPosX = buttonX / size.width;
int buttonPosY = buttonY / size.height;
int buttonIndex = pos[buttonPosY][buttonPosX];
if (labelX == buttonX && (labelY - buttonY) == size.height ) {
int labelIndex = buttonIndex + 3;
centerPanel.remove(buttonIndex);
centerPanel.add(label, buttonIndex);
centerPanel.add(button,labelIndex);
centerPanel.validate();
}
if (labelX == buttonX && (labelY - buttonY) == -size.height ) {
int labelIndex = buttonIndex - 3;
centerPanel.remove(labelIndex);
centerPanel.add(button,labelIndex);
centerPanel.add(label, buttonIndex);
centerPanel.validate();
}
if (labelY == buttonY && (labelX - buttonX) == size.width ) {
int labelIndex = buttonIndex + 1;
centerPanel.remove(buttonIndex);
centerPanel.add(label, buttonIndex);
centerPanel.add(button,labelIndex);
centerPanel.validate();
}
if (labelY == buttonY && (labelX - buttonX) == -size.width ) {
int labelIndex = buttonIndex - 1;
centerPanel.remove(buttonIndex);
centerPanel.add(label, labelIndex);
centerPanel.add(button,labelIndex);
centerPanel.validate();
}
}
}
Create each Icon and add the Icon to an ArrayList.
Then you can use the Collections.shuffle(...) method
Then iterate through the shuffled ArrayList and create your buttons add add the Icons to each button.
Edit:
Simple example showing the concept:
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class SSCCE extends JPanel
{
public SSCCE()
{
setLayout( new GridLayout(3, 4) );
ArrayList<Integer> numbers = new ArrayList<Integer>();
for (int i = 0; i < 12; i++)
numbers.add( new Integer(i) );
Collections.shuffle(numbers);
for (int i = 0; i < 12; i++)
add( new JLabel( "" + numbers.get(i) ) );
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SSCCE());
frame.setLocationByPlatform( true );
frame.setSize(300, 300);
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
I'm working on a memory game and I want to set it up so I click the first "card", then the second and if they are not the same the second card shows for a few seconds then they return to the "non-flipped" position.
I tried using SwingWorker, Thread.sleep and SwingTimer but I cant get it to work. With Thread.sleep the second card wont "flip" if it's a duplicate it waits the amount of sleep time and disappears. If its not a match it waits "face down" and after the sleep timer the first card does flip back. This happens regardless of where I place the Thread.sleep.
With Swing Timer it only appears to "change the timer" while I'm interacting with the cards so I end up flipping 8 cards before it activates.
I've had no luck with SwingWorker and I'm not even sure it will work for what I'm looking for.
This is the code I have:
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
for(int index = 0; index < arraySize; index++)
{
if(button[index] == e.getSource())
{
button[index].setText(String.valueOf(cards.get(index)));
button[index].setEnabled(false);
number[counter]=cards.get(index);
if (counter == 0)
{
counter++;
}
else if (counter == 1)
{
if (number[0] == number[1])
{
for(int i = 0; i < arraySize; i++)
{
if(!button[i].isEnabled())
{
button[i].setVisible(false);
}
}
}
else
{
for(int i = 0; i < arraySize; i++)
{
if(!button[i].isEnabled())
{
button[i].setEnabled(true);
button[i].setText("Card");
}
}
}
counter = 0;
}
}
}
}
}
Basically what I need is for this code to execute when the counter == 1, and the card is not a match:
button[index].setText(String.valueOf(cards.get(index)));
button[index].setEnabled(false);
Then a pause so the card is revealed for that time, and finally it resumes to returning the card to its face down position.
This is what I tried with Thread.sleep():
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
for(int index = 0; index < arraySize; index++)
{
if(button[index] == e.getSource())
{
button[index].setText(String.valueOf(cards.get(index)));
button[index].setEnabled(false);
number[counter]=cards.get(index);
if (counter == 0)
{
counter++;
}
else if (counter == 1)
{
if (number[0] == number[1])
{
for(int i = 0; i < arraySize; i++)
{
if(!button[i].isEnabled())
{
button[i].setVisible(false);
}
}
}
else
{
try
{
Thread.sleep(800);
}
catch (InterruptedException e1)
{
e1.printStackTrace();
}
for(int i = 0; i < arraySize; i++)
{
if(!button[i].isEnabled())
{
button[i].setEnabled(true);
button[i].setText("Card");
}
}
}
counter = 0;
}
}
}
}
}
Thanks in advance for any advice
Use javax.swing.Timer to schedule a future event to trigger. This will allow you to make changes to the UI safely, as the timer is triggered within the context of the Event Dispatching Thread.
The problem with been able to flip multiple cards simultaneously has more to do with you not setting up a state that prevents the user from flipping cards then the use of timers.
The following example basically only allows one card to be flipped per group at a time.
Once a card has been flipped in both groups, the timer is started. When it is triggered, the cards are reset.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LinearGradientPaint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class FlipCards {
public static void main(String[] args) {
new FlipCards();
}
public FlipCards() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Card extends JPanel {
private BufferedImage image;
private boolean flipped = false;
private Dimension prefSize;
public Card(BufferedImage image, Dimension prefSize) {
setBorder(new LineBorder(Color.DARK_GRAY));
this.image = image;
this.prefSize = prefSize;
}
#Override
public Dimension getPreferredSize() {
return prefSize;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
LinearGradientPaint lgp = new LinearGradientPaint(
new Point(0, 0),
new Point(0, getHeight()),
new float[]{0f, 1f},
new Color[]{Color.WHITE, Color.GRAY});
g2d.setPaint(lgp);
g2d.fill(new Rectangle(0, 0, getWidth(), getHeight()));
if (flipped && image != null) {
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g2d.drawImage(image, x, y, this);
}
g2d.dispose();
}
public void setFlipped(boolean flipped) {
this.flipped = flipped;
repaint();
}
}
public class CardsPane extends JPanel {
private Card flippedCard = null;
public CardsPane(List<BufferedImage> images, Dimension prefSize) {
setLayout(new GridBagLayout());
MouseAdapter mouseHandler = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (flippedCard == null) {
Card card = (Card) e.getComponent();
card.setFlipped(true);
flippedCard = card;
firePropertyChange("flippedCard", null, card);
}
}
};
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(4, 4, 4, 4);
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 0.25f;
for (BufferedImage img : images) {
Card card = new Card(img, prefSize);
card.addMouseListener(mouseHandler);
add(card, gbc);
}
}
public Card getFlippedCard() {
return flippedCard;
}
public void reset() {
if (flippedCard != null) {
flippedCard.setFlipped(false);
flippedCard = null;
}
}
}
public class TestPane extends JPanel {
private CardsPane topCards;
private CardsPane bottomCards;
private Timer resetTimer;
public TestPane() {
resetTimer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
topCards.reset();
bottomCards.reset();
}
});
resetTimer.setRepeats(false);
resetTimer.setCoalesce(true);
PropertyChangeListener propertyChangeHandler = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
Card top = topCards.getFlippedCard();
Card bottom = bottomCards.getFlippedCard();
if (top != null && bottom != null) {
resetTimer.start();
}
}
};
BufferedImage[] images = new BufferedImage[4];
try {
images[0] = ImageIO.read(new File("./Card01.png"));
images[1] = ImageIO.read(new File("./Card02.jpg"));
images[2] = ImageIO.read(new File("./Card03.jpg"));
images[3] = ImageIO.read(new File("./Card04.png"));
Dimension prefSize = getMaxBounds(images);
List<BufferedImage> topImages = new ArrayList<>(Arrays.asList(images));
Random rnd = new Random(System.currentTimeMillis());
int rotate = (int) Math.round((rnd.nextFloat() * 200) - 50);
Collections.rotate(topImages, rotate);
topCards = new CardsPane(topImages, prefSize);
topCards.addPropertyChangeListener("flippedCard", propertyChangeHandler);
List<BufferedImage> botImages = new ArrayList<>(Arrays.asList(images));
int botRotate = (int) Math.round((rnd.nextFloat() * 200) - 50);
Collections.rotate(botImages, botRotate);
bottomCards = new CardsPane(botImages, prefSize);
bottomCards.addPropertyChangeListener("flippedCard", propertyChangeHandler);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(4, 4, 4, 4);
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(topCards, gbc);
add(bottomCards, gbc);
} catch (Exception e) {
e.printStackTrace();
}
}
protected Dimension getMaxBounds(BufferedImage[] images) {
int width = 0;
int height = 0;
for (BufferedImage img : images) {
width = Math.max(width, img.getWidth());
height = Math.max(height, img.getHeight());
}
return new Dimension(width, height);
}
}
}