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);
}
Related
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
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 have a JFrame in which I am drawing a lot of images in a large grid. My window is not big enough to contain the entire grid, so I want to use a JScrollPane that will allow me to show certain parts of the grid in the window.
My problem is that I cannot scroll. When I made it so the scrollbars always show up, they appear to already cover the entire area.
As you can see, the scrollbars give the impression that the entire area is shown in the window. However, if I resize it, you'll notice that is not the case at all!
And the scrollbars continue to say they cover the entire area.
(Note that the numbers in the grid are actually 16 by 16 px images but in order to make a Minimal, Complete, and Verifiable example, I replaced the images with coordinate strings.)
Here is my code for the Minimal, Complete and Verifiable example:
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main {
public static int[][] tiles;
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
}
class Foo {
public void run(){
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Panel panel = new Panel();
panel.setPreferredSize(new Dimension(512,448));
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(scrollPane);
frame.setVisible(true);
frame.pack();
Main.tiles = new int[32][32];
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
panel.repaint();
}
}
}
class Panel extends JPanel {
public void paintComponent(Graphics g){
paintScreen(g);
}
private void paintScreen(Graphics g){
int x =0, y =0;
for (int i = 0; i < Main.tiles.length; i++) {
for (int j = 0; j < Main.tiles[i].length; j++){
g.drawString(i + " " + j, x*32, y*32);
y++;
}
y=0;
x++;
}
}
}
So my question is:
Q: Why is the JScrollPane not allowing me to scroll over the JPanel?
Your problem looks to be here:
panel.setPreferredSize(new Dimension(512,448));
where you constrain the JPanel held by the JScrollPane to a specific size, not allowing it to expand. Never do this. Instead constrain the JScrollPane or its view port.
e.g.,
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main {
public static int[][] tiles;
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
}
class Foo {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Panel panel = new Panel();
// !! panel.setPreferredSize(new Dimension(512,448));
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.getViewport().setPreferredSize(new Dimension(512, 448));
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(scrollPane);
frame.setVisible(true);
frame.pack();
Main.tiles = new int[32][32];
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
panel.repaint();
}
}
}
class Panel extends JPanel {
private static final int PREF_W = 1200;
private static final int PREF_H = 1200;
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintScreen(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private void paintScreen(Graphics g) {
int x = 0, y = 0;
for (int i = 0; i < Main.tiles.length; i++) {
for (int j = 0; j < Main.tiles[i].length; j++) {
g.drawString(i + " " + j, x * 32, y * 32);
y++;
}
y = 0;
x++;
}
}
}
Edit
Attempt at improving version based on MadProgrammer's recommendation:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
public class Main {
public static int[][] tiles;
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
}
class Foo {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
NumberPanel panel = new NumberPanel();
// !! panel.setPreferredSize(new Dimension(512,448));
JScrollPane scrollPane = new JScrollPane(panel);
// !! scrollPane.getViewport().setPreferredSize(new Dimension(512, 448));
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(scrollPane);
frame.setVisible(true);
frame.pack();
Main.tiles = new int[32][32];
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
panel.repaint();
}
}
}
class NumberPanel extends JPanel implements Scrollable {
private static final int PREF_W = 1200;
private static final int PREF_H = 1200;
private static final int VP_WIDTH = 512;
private static final int VP_HEIGHT = 448;
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintScreen(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private void paintScreen(Graphics g) {
int x = 0, y = 0;
for (int i = 0; i < Main.tiles.length; i++) {
for (int j = 0; j < Main.tiles[i].length; j++) {
g.drawString(i + " " + j, x * 32, y * 32);
y++;
}
y = 0;
x++;
}
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(VP_WIDTH, VP_HEIGHT);
}
#Override
public int getScrollableBlockIncrement(Rectangle arg0, int arg1, int arg2) {
// TODO Consider improving
return 0;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
#Override
public int getScrollableUnitIncrement(Rectangle arg0, int arg1, int arg2) {
// TODO Consider improving
return 0;
}
}
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);
}
}
}
I have a JFrame in FlowLayout with multiple JLabels added to it, but when I call repaint on the JLabels, their paintComponent is not being called. If I remove the FlowLayout, only the last JLabel added shows up and repaints properly. I tried to use a panel but it didn't work. I'm not sure I'm using it properly though.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class RacingLetters {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
final JFrame jframe = new JFrame();
jframe.setTitle("Racing letters");
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//jframe.setExtendedState(Frame.MAXIMIZED_BOTH);
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
int x = (int) ((dimension.getWidth() - jframe.getWidth()) / 2);
int y = (int) ((dimension.getHeight() - jframe.getHeight()) / 2);
jframe.setLocation(x, y);
jframe.setMinimumSize(new Dimension(500, 200));
FlowLayout fl = new FlowLayout();
jframe.setLayout(fl);
//jframe.setLayout(null);
jframe.setVisible(true);
StringBuffer[] stringBufferArray = new StringBuffer[20];
char ch = 'A';
int yy = 20;
for (int i = 0; i < 5; i++) {
stringBufferArray[i] = new StringBuffer("");
BufferThread bt = new BufferThread(stringBufferArray[i], ch, 10, yy);
//pane.add(bt);
jframe.add(bt);
new Thread(bt).start();
ch++;
yy += 20;
}
}
});
}
}
..
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
public class BufferThread extends JLabel implements Runnable {
char ch;
StringBuffer sb;
int x,y;
BufferThread(StringBuffer sb, char ch,int x, int y) {
this.sb = sb;
this.ch = ch;
this.x = x;
this.y = y;
}
#Override
public void run() {
Random rand = new Random();
for (int i = 0; i < 5; i++) {
sb.append(ch);
System.out.println(x + " " + y + " " + ch);
repaint();
try {
Thread.sleep(rand.nextInt(500));
} catch (InterruptedException ex) {
Logger.getLogger(BufferThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public void paintComponent(Graphics g) {
//System.out.println(x + " " + y + " " + ch);
//System.out.println("aaaa");
//stem.out.println(sb);
Graphics2D g2 = (Graphics2D) g;
Font f = new Font("Serif", Font.PLAIN, 24);
//if (sb.toString().indexOf("E") < 0)
g2.drawString(sb.toString(), x, y);
}
}
The core problem is that the JLabel isn't providing any information back to the frame's layout manager about how big it would like to be. Nor is it actually telling the frame's layout manager that it's updated and needs to be resized.
Why you're trying to paint text on a label is beyond me, seen as that's what a label does by design.
You should avoid using Thread when dealing with Swing components and should use javax.swing.Timer and SwingWorker where possible.
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Ticker {
public static void main(String[] args) {
new Ticker();
}
public Ticker() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(new TickerLabel());
frame.setSize(100, 100);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TickerLabel extends JLabel {
private int counter;
public TickerLabel() {
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (counter > 4) {
((Timer)e.getSource()).stop();
} else {
String text = getText();
text += (char)(((int)'A') + counter);
setText(text);
}
counter++;
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
}
}