Awt/Swing - Frame doesn't repaint during move process - java

I'm trying to create an example with some graphics for a ´Stack´.
I want the Wagons to "Spawn" and move in one track which are the ´stacks´.
The moving process is made by the ´Wagon´ itself. I already tried to give the Wagon a reference to my ´Frame´ called ´Window´ to repaint it in every loop but it still doesn't show up until it reached it's stop.
Wagon:
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class Window extends JFrame {
private Stack stack1 = new Stack(1);
public Window() {
setSize(800, 400);
setResizable(false);
setLayout(null); //Not perfect, but it works for those little things
setVisible(true);
createTracks(); //Tracksgraphic made out of gray rects
}
public void addWagon(Wagon wagon) { //Is called when adding a Wagon
this.add(wagon);
stack1.addWagon(wagon);
}
public static void main(String[] args) { //main
new Window();
}
}
Stack:
public class Stack {
private int gleis; //gleis = german for track
private Wagon first = null;
public Stack(int g) {
gleis = g;
}
public void addWagon(Wagon wagon) {
if (first != null) {
wagon.setNext(first);
}
first = wagon;
first.moveRight(gleis);
}
public void removeWagon(int id, Stack nextStack) {
Wagon speicherFirst = first;
first.moveLeft(null);
first = first.getNext();
while (speicherFirst.getID() != id) {
speicherFirst.setNext(first);
first.moveLeft(speicherFirst);
speicherFirst = first;
first = first.getNext();
}
nextStack.addWagon(speicherFirst);
if (speicherFirst.getNext() != null) {
speicherFirst = speicherFirst.getNext();
while (speicherFirst!= null) {
speicherFirst.moveRight(gleis);
speicherFirst = speicherFirst.getNext();
}
}
}
public boolean hasID(int id) {
return first.isID(id);
}
}
Wagon:
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class Wagon extends JPanel { //Entspricht einem Canvas aus .awt
private Wagon next;
private int id;
public Wagon(int i) {
id = i;
setSize(50, 20);
Random r = new Random();
setBackground(new Color(r.nextFloat(), r.nextFloat(), r.nextFloat()));
setVisible(true);
}
public boolean isID(int i) {
if (id == i) {
return true;
} else if (next == null) {
return false;
} else {
return next.isID(i);
}
}
public void setNext(Wagon n) {
next = n;
}
public void moveRight(int gleis) {
setLocation(getX(), gleis * 100);
if (next != null) {
while (next.getX() - getX() < 70) {
next.moveRight(gleis);
move(3);
}
} else {
while (getX() < 700) {
move(3);
}
}
}
public Wagon getNext() {
return next;
}
public int getID() {
return id;
}
public void moveLeft(Wagon previous) {
if (previous == null) {
while (getX() > 50) {
move(-3);
}
} else {
while (getX() - previous.getX() > 50) {
move(-3);
}
}
}
public void move(int dir) {
this.setLocation(getX() + dir, getY());
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
}
}

it still doesn't show up until it reached it's stop.
Your code is executing on the Event Dispatch Thread (EDT), which is the thread that is responsible for handling GUI events and repainting the GUI. The Thread.sleep() prevents the GUI from repainting itself until the while/loop is finished executing so you only see the painting at the end. Read the section from the Swing tutorial on Concurrency for more information.
The solution is to use a Swing Timer to provide the animation, not Thread.sleep().

It looks like you don't actually have the replaint(); call anywhere in your code. I would have that in both the moveLeft() and moveRight() commands.
Another thing, you're using JPanels as your wagons. I wouldn't do this, it'll take more resources and is bad practice. Unless there's a specific reason, I'd use a base sprite image, and load it to an int[] array, and paint that array to that screen using BufferedImage, instead of having x number of JPanel's running around.

Related

Java Jpanel Repaint/update probleme

i have a problem with the method Repaint of my jpanel.
I'm trying to make a "Racing game" with the Java Swing and by following the MVC architecture :
i have 5 classes :
Main : runs the MVC
public class Main {
private static Model model;
private static View view;
private static Controller controller;
public static void main(String[] args) {
model = new Model();
view = new View();
controller = new Controller();
model.addObserver(view);
controller.addModule(model);
controller.addView(view);
view.addContoller(controller);
}
}
Model :
import java.util.ArrayList;
import java.util.Observable;
public class Model extends Observable{
private ArrayList<Car> cars;// List of cars
public Model() {
cars = new ArrayList<Car>();
cars.add(new Car(this,0, 50));
cars.add(new Car(this,0, 50));
cars.add(new Car(this,0, 50));
}
public void startCar(int i){
//i is the index of the selected element in the checkbox
//if i==0 then the selected element is "All cars" else is a specific car
if(i>0)
cars.get(i-1).start();
else{
for(int j=0;j<cars.size();j++)
cars.get(j).start();
}
}
public void speedUpCar(int i) {
if(i>0)
cars.get(i-1).incVitess();
else{
for(int j=0;j<cars.size();j++)
cars.get(j).incVitess();
}
}
public void notifyView(){
setChanged();
notifyObservers(cars);
}
public void speedDownCar(int i) {
if(i>0)
cars.get(i-1).decVitess();
else{
for(int j=0;j<cars.size();j++)
cars.get(j).decVitess();
}
}
public void stopCar(int i) {
if(i>0)
cars.get(i-1).stop();
else{
for(int j=0;j<cars.size();j++)
cars.get(j).stop();
}
}
}
the View :
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class View implements Observer {
private JFrame fen;
private JPanel btnPanel,panel;
private JButton play,speedUp,speedDown,stop;
private JComboBox<String> listeCar;
private boolean test = false;
public View() {
fen = new JFrame();
fen.setTitle("Car Racing");
fen.setSize(900, 400);
fen.setLocationRelativeTo(null);
fen.setResizable(false);
Vector<String> v = new Vector<String>();
v.add("All cars");v.add("car 1");v.add("car 2");v.add("car 3");
listeCar = new JComboBox<String>(v);
play = new JButton("Play");
speedUp = new JButton("+");
speedDown = new JButton("-");
stop = new JButton("stop");
panel = new JPanel(new GridLayout(3,1));
btnPanel = new JPanel();
btnPanel.add(listeCar);
btnPanel.add(play);
btnPanel.add(speedUp);
btnPanel.add(speedDown);
btnPanel.add(stop);
Container c = fen.getContentPane();
c.setLayout(new BorderLayout());
c.add(btnPanel, BorderLayout.SOUTH);
c.add(panel, BorderLayout.CENTER);
fen.setVisible(true);
fen.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public void addContoller(Controller controller){
play.addActionListener(controller);
speedUp.addActionListener(controller);
speedDown.addActionListener(controller);
stop.addActionListener(controller);
}
#Override
public void update(Observable arg0, Object c) {
ArrayList<Car> cars = (ArrayList<Car>)c;
for(int i=0;i<cars.size();i++){
Car car = cars.get(i);
if(!test){ // if its the first tima, add the cars to the panel
panel.add(car);
}else{
car.repaint(); // << the Problem is HERE
}
}
test = true;
}
public JButton getPlay() {
return play;
}
public JButton getSpeedUp() {
return speedUp;
}
public JButton getSpeedDown() {
return speedDown;
}
public JButton getStop() {
return stop;
}
public JComboBox<String> getListeCar() {
return listeCar;
}
}
The controller:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Controller implements ActionListener{
private Model model;
private View view;
public Controller() {
}
public void addModule(Model m) {
model = m;
model.notifyView();
}
public void addView(View v){
view = v;
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == view.getPlay()){
model.startCar(view.getListeCar().getSelectedIndex());
}else if(e.getSource() == view.getSpeedUp()){
model.speedUpCar(view.getListeCar().getSelectedIndex());
}else if(e.getSource() == view.getSpeedDown()){
model.speedDownCar(view.getListeCar().getSelectedIndex());
}else if(e.getSource() == view.getStop()){
model.stopCar(view.getListeCar().getSelectedIndex());
}
}
}
The car class :
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Car extends JPanel{
private int id,x,y,vitess;
private Thread thread;
private Model model;
private boolean start = true;
private boolean forward = true;
private Color color;
private boolean threadStarted = false;
private BufferedImage bg; // background image
public Car(Model model,int x,int y) {
this.x =x;
this.y = y;
vitess = 7;
this.model = model;
try {
bg = ImageIO.read(new File("road.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
color = changeColor(); // Random color
thread = new Thread(new CarThread(this));
start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bg, 0, 0, null);
g.setColor(color);
g.fillRect(x, y, 100, 50); // the car is a simple rectangle
}
public void start() {
start = true;
if(!threadStarted){
threadStarted = true;
thread.start();
}else{
thread.resume();
}
}
public void move(){
System.out.println("X:"+x);
if(forward){
if(x<this.getWidth()){
x+=2;
}else{
color = changeColor();
forward = false;
}
}else{
if(x>0){
x-=2;
}else{
color = changeColor();
forward = true;
}
}
model.notifyView();
}
private Color changeColor() {
int r = (int)(Math.random()*255);
int g = (int)(Math.random()*255);
int b = (int)(Math.random()*255);
return new Color(r,g,b);
}
public void stop(){
start = false;
thread.suspend();
}
public void incVitess(){
if(vitess>1)
vitess--;
}
public void decVitess(){
if(vitess<6)
vitess++;
}
public int getId() {
return id;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getVitess() {
return vitess;
}
public boolean getStart(){
return start;
}
public void setStart(boolean m){
this.start = m;
}
public Color getColor(){
return color;
}
}
CarThraed class:
public class CarThread implements Runnable{
private Car car;
public CarThread(Car car) {
this.car = car;
}
#Override
public void run() {
while(car.getStart()){
car.move();
try {
Thread.sleep(car.getVitess());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
if you run the project you will note that the cars does not reach the end of the frame, even if used:
if(x<this.getWidth()) x++;
but when i replace
car.repaint(); // << the Problem is HERE
with
car.update(car.getGraphics()); // << the Problem is HERE
the cars can now reach the end of the Frame, but the buttons in the btnJpanel disappear
image with repaint here
image with update here
thank you in advance
I can't say that I've read all your code, but your model never notifies the view. I mean nowhere inside of the model class's code is the notifyView() method called. It should be the model's responsibility to call this whenever its state is changed.
Note that this:
car.update(car.getGraphics()); // << the Problem is HERE
Should not be used as the Graphics obtained is not stable.
Also, your model holds view components, an ArrayList of Car, a class that extends JPanel, another thing that should never occur as the model should be completely ignorant of the view, with the exception being that it knows that some things may be listening to it, and it needs to notify those things, and that's it. Instead your Model should hold an ArrayList of non-view, non-JPanel logical Car objects.
Edit
You state:
In the model i have the method: public void notifyView() which is called by the car's method public void Move() , That means whenever the thread calls the move method of the car, it calls the notifyView of the model
No, the Car should not be calling this method -- only the model itself should call this when its state is changed.
Also, I see that you've got very dangerous code using Thread#suspend() and Thread#resume() method calls. These methods have been deprecated as they've been found to be dangerous. To find out why, please check the API for Thread as well as this useful article. You will most definitely want to avoid their use.
Suggestions
Make Car a logical non-GUI class that knows its position and can have its position changed.
Override your drawing JPanels protected void paintComonent(Graphics g) method and use the Car information from the model to draw the Cars. In other words, use the state of the model to affect the state of the view.
Use a Swing Timer instead of a background thread, to change your Car positions.
Have the model and only the model call the notifyView method when its state has been changed.
Edit
Your main bug is here:
class Car extends JPanel {
private int id, x, y, vitess;
//....
public int getX() {
return x;
}
public int getY() {
return y;
}
You're inadvertently overriding the Car JPanel's getX and getY methods, messing up the location of these components. This is another reason to avoid overriding Swing components unless necessary -- to avoid these hidden side effects.
Get rid of or rename these methods.

How to display/update images after keystrokes in java in JFrame?

As a java non-expert, I would like to know how to change the code below to get it to work. Here is what I want to do
When the java code is called args contains several image filenames
I want to see the first image in this list
Then when I press a key, an index is changed, and the next image is displayed.
Using the suggestions made below, here is a compilable, running piece of code:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public class LoadImageApp extends Component {
BufferedImage img;
private static int index = 0;
public void paint(Graphics g) {
g.drawImage(img, 0, 0, null);
}
public LoadImageApp(String filename) {
try {
img = ImageIO.read(new File(filename));
} catch (IOException e) {
System.out.println(e.getMessage());
System.exit(0);
}
}
public Dimension getPreferredSize() {
if (img == null) {
return new Dimension(100,100);
} else {
return new Dimension(img.getWidth(null), img.getHeight(null));
}
}
static public void changeImage(JFrame frame, String filename) {
System.out.println("Using file "+filename);
frame.add(new LoadImageApp(filename));
frame.pack();
frame.setVisible(true);
frame.repaint();
}
public static void main(final String[] args) {
char c=0;
String filename = args[0];
int numberImages = args.length;
final JFrame f = new JFrame("Load Image Sample");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
f.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent event) {
int key = event.getKeyCode();
if (key==81) {
System.exit(0);
} else if (key==89) {
System.out.println("yes");
} else if (key==78) {
System.out.println("no");
}
index += 1;
f.removeAll();
changeImage(f, args[index]);
}
});
// show first image here
changeImage(f, args[index]);
}
}
If I use use the code like
java LoadImageApp *.jpg
it only displays the first image. I can press keys, but the image shown does not change. I want to have the image changed.
I have found revalidate() and repaint() as possible solutions. Despite frame.revalidate() does not exist at all, frame.repaint() (inside changeImage) does still not change anything. I still see the first image displayed.
Is this the right approach anyway? Is there a more elegant way?
I wrote a program to demonstrate what you asked for, here is the code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageShow {
/** Inner class: JPanel that displays images. **/
static class JImagePanel extends JPanel {
protected final LinkedList<BufferedImage> images;
protected BufferedImage currentImage;
protected int currentIndex;
public JImagePanel(final LinkedList<BufferedImage> images) {
super(true);
this.setFocusable(false);
this.images = images;
this.setIndex(0);
}
/** Has to be private to not cause issues when used in the constructor. **/
private void setIndex(final int index) {
if (index >= this.images.size()) {
this.currentIndex = 0;
} else if (index < 0) {
this.currentIndex = this.images.size() - 1;
} else {
this.currentIndex = index;
}
this.currentImage = this.images.get(this.currentIndex);
this.setPreferredSize(new Dimension(this.currentImage.getWidth(), this.currentImage.getHeight()));
this.repaint();
}
public void shiftIndex(final int amount) {
this.setIndex(this.currentIndex + amount);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(this.currentImage, 0, 0, null);
}
}
public static void main(final String[] args) {
final LinkedList<BufferedImage> images = loadImages(args);
if (images.size() > 0) {
final JFrame window = new JFrame("Image Show");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JImagePanel imagePanel = new JImagePanel(images);
window.add(imagePanel);
window.addKeyListener(new KeyAdapter() {
private void shiftIndex(final int amount) {
imagePanel.shiftIndex(amount);
window.pack();
window.setLocationRelativeTo(null);
}
#Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ESCAPE:
window.dispose();
e.consume();
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_NUMPAD4:
shiftIndex(-1);
e.consume();
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_NUMPAD6:
shiftIndex(+1);
e.consume();
break;
}
}
});
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
} else {
System.err.println("No image could be loaded.\nPlease provide a list of image files as parameters.");
}
}
private static LinkedList<BufferedImage> loadImages(final String[] filenames) {
final LinkedList<BufferedImage> result = new LinkedList<>();
for (String filename : filenames) {
try {
final File file = new File(filename);
final BufferedImage image = ImageIO.read(file);
if (image == null) {
throw new IOException("Unknown image format");
}
result.add(image);
} catch (IOException e) {
System.err.println("Unable to load image \"" + filename + "\": " + e.getMessage());
}
}
return result;
}
}
Please note that this is not the most beautiful way of writing this tool, however it works.
What you should usually do:
Each class should be in its own .java file. The idea is to have a structure, that is easy to read even if you re-visit this code 3 years later.
You should not use variables from another scope like I did here with the window and imagePanel in the main function. Instead use constructors that store local variables with either the value given or a copy of the value (depending on your needs), like I did in the JImagePanel constructor.
Whether or not you need a copy of the value depends on what you do and how much risk you are willing to take. In this example changing the image list after JImagePanel is created would potentially mess things up.
You should never use numbers like you did in your version of the key listener. You never know which key-code corresponds to which key! Whenever available use the provided constants or functions to get such a 'magic' number.
Always expect the worst when it comes to error handling. For once try to catch and handle all possible errors. For second always try to avoid potential issues. A bug you cannot make, is a bug you won't have to bother about.
In your version the image file is loaded from disk every time a button is pressed. What happens if the image is no longer present at that moment? In my version everything is checked before-hand, and once that is done, the program cannot fail anymore (at least not when trying to switch images). ;)
In general: try to find a good book or online tutorial on Java for beginners. If you just hack away, you will miss all those nice things Java has already prepared for you, that will not only speed up developing a lot, it will as well prevent all bugs that you otherwise might code in.
The keyword final indicates that the variable will not change after it has been initialized. In order to use a variable within an anonymous inner class i.e. KeyAdapter it has to be declared final.
So you need:
public static void main(final String[] args) {
and
final JFrame f = new JFrame("Load Image Sample");
But this will not work for index as you are planning on changing it. So I recommed declaring it as a static variable on class level i.e. outside of the function.
private static int index = 0;
This is more or less what you want. I recommend you to study Java from scratch and fill the gap you have.
public class LoadImageApp extends JPanel {
//Current image the Canvas shows
private Image currentImage;
//change the current image and repaint
public void setCurrentImage(Image currentImage) {
this.currentImage = currentImage;
repaint();
}
#Override
public void paintComponent(Graphics g) {
g.clearRect(0, 0, getWidth(), getHeight());
if (currentImage != null)
g.drawImage(currentImage, 0, 0, null);
}
#Override
public Dimension getPreferredSize() {
if (currentImage == null) {
return new Dimension(100, 100);
} else {
return new Dimension(currentImage.getWidth(null), currentImage.getHeight(null));
}
}
public static void main(final String[] args) throws IOException {
if (args.length > 0){
final Image [] images = new Image[args.length];
for (int i = 0; i < images.length; i++){
images[i] = ImageIO.read(new File(args[i]));
}
//It is a goog practice to attach your code to AWT thread.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
LoadImageApp app = new LoadImageApp();
JFrame f = new JFrame("Load Image Sample");
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(app, BorderLayout.CENTER);
f.addKeyListener(new MyKeyAdapter(app, images));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
}
// We create another class for the KeyAdapter
class MyKeyAdapter extends KeyAdapter{
//The canvas so we can tell it that the current image has change
private LoadImageApp canvas;
//index of the current image
private int index;
//All the images
private Image [] images;
public MyKeyAdapter(LoadImageApp canvas, Image[] images) {
super();
this.canvas = canvas;
this.images = images;
rotateRight();
}
#Override
public void keyPressed(KeyEvent event) {
int key = event.getKeyCode();
if (key == 81) {
System.exit(0);
} else if (key == KeyEvent.VK_LEFT) {
rotateLeft();
} else if (key == KeyEvent.VK_RIGHT) {
rotateRight();
}
}
private void rotateRight() {
//change the image in the canvas
canvas.setCurrentImage(images[index]);
//increment index
index++;
//last element + 1, set it to 0
if (index >= images.length) index = 0;
}
private void rotateLeft() {
//change the image in the canvas
canvas.setCurrentImage(images[index]);
//decrement index
index--;
//< 0, set it to last image
if (index < 0) index = images.length - 1;
}
}

Why is this class visible when being run directly but when called from other class, it's empty and can't be closed

When I run this class(Try class), it calls the Stacker class completely(with full functions), but when I use other class(I used JFrame with button that has actionlistener that calls Stacker class) to run this(Stacker class), the JFrame(Stacker class) will pop-up but empty and I can't close the program.
I tried to run this(Stacker) from other class like this:
public class Try {
/**
* #param args
*/
public static void main(String[] args) {
run();
}
public static void run(){
new Stacker();
}
}
The Stacker class ran fully(I can interact with it). But when I tried to call the stacker class from an actionlistener of a button in JFrame, it's blank and can't be closed.
Please help me.
here are my codes for the Stacker class:
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Stacker extends JFrame implements KeyListener {
int iteration = 1;
static double time = 200;
static int last = 0;
static int m = 10;
static int n = 20;
JButton b[][];
static int length[] = {5,5};
static int layer = 19;
static int deltax[] = {0,0};
static boolean press = false;
static boolean forward = true;
static boolean start = true;
JPanel panel;
public static void main (String[] args) {
Stacker stack = new Stacker();
stack.setVisible(true);
}
public Stacker() {
panel = new JPanel();
panel.setLayout(new GridLayout(20,10));
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
b = new JButton [m][n];
for (int y = 0;y<n;y++) {
for (int x = 0;x<m;x++) {
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.white);
b[x][y].setBorderPainted(false);
panel.add(b[x][y]);
b[x][y].setEnabled(true);
}//end inner for
}
setSize(390, 560);
setLayout(new GridBagLayout());
add(panel);
setFocusable(true);
addKeyListener(this);
pack();
setVisible(true);
go();
}
public void go() {
int tmp = 0;
Component temporaryLostComponent = null;
do {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10-length[1]){
forward = false;
} else if (deltax[1] == 0){
forward = true;
}
draw();
try {
Thread.sleep((long) time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}while(press == false);
if (layer>12) {
time= 150-(iteration*iteration*2-iteration);
} else {
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
}
if (length[1] <= 0) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line "+(18-layer)+"!");
System.exit(0);
}
last = deltax[1];
start = false;
go();
}
public int check() {
if (start == true) {
return length[1];
} else if (last<deltax[1]) {
if (deltax[1]+length[1]-1 <= last+length[0]-1) {
return length[1];
} else {
return length[1]-Math.abs((deltax[1]+length[1])-(last+length[0]));
}
} else if (last>deltax[1]) {
return length[1]-Math.abs(deltax[1]-last);
} else {
return length[1];
}
}
public void forward() {
deltax[0] = deltax[1];
deltax[1]++;
}
public void back() {
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw() {
for (int x = 0;x<length[1];x++) {
b[x+deltax[0]][layer].setBackground(Color.white);
}
for (int x = 0;x<length[1];x++) {
b[x+deltax[1]][layer].setBackground(Color.BLUE);
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
press = true;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
Your do-while loop and thread sleep look suspicious in that it is running on the Swing event thread tying it up. The problem is that these longer running tasks lock the Swing Event Dispatch Thread, or EDT, the one thread that is responsible for all Swing graphics and user interactions, preventing your application from drawing itself and all of its widgets and prevents the application from responding to any user input.
Likely the application ran in isolation because the issues that I identified above ran off of the EDT, and doing this code wise is one possible solution to your problem, but I'm betting that there are better solutions available that we can help you with if you tell us more about the details of your problem.
Also you appear to be using recursion in a dangerous way having the go method call itself. Can't really say more since I'm on a cell phone.
Firstly, I would replace:this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
with the following:
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
as DISPOSE_ON_CLOSE is a static/class variable.
In fact, I am surprised that your IDE did not pick that up (unless, you aren't using an IDE).
SwingWorker worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Stacker stack = new Stacker();
stack.setVisible(true);
return null;
}
};
worker.execute();
after using this method, when I call Stacker class from another JFrame, the game executes perfectly. Just like when calling Stacker class from Try class.
Thank you for all the responses I had. :D

Java's Thread.sleep causes my previously called method to cancel

The title could be incorrect but I couldn't think of anything else to name this question.
My issue comes with some Java code I've been writing that is driving me insane. Basically I'm trying to make a "card memory" game where I have a 4X4 grid of cards laid out; when you click one it flips, then you click the second, it flips, and if they're the same card it disappears and if they're different cards it flips them back over.
This works fine except one little detail. When you click the first card it flips, then when you click the second card it flips it and checks the value of it so fast that you can't tell if the 2nd card flips at all before they're gone or the first one is flipped back over. So I added
try{Thread.sleep(2000);}catch(InterruptedException ex) {}
after I flipped the second card. Well now it's becoming clear that the second card doesn't flip at all. I click the first card, it flips, I click the second card, it waits 2 seconds (thread sleep) and then it determines the equality and either hides or flips the first card back. All without ever showing what the 2nd card was.
I am going to add all the code I have below. I'm sorry that it's a lot but I don't know which part is relevant.
Card.java
import objectdraw.*;
import java.awt.Image;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
public class Card {
private int value;
private VisibleImage imageFront, imageBack;
public Card(int val, Location p, double w, double h, DrawingCanvas c) {
value = val;
String urlStr = "images/" + val + ".png";
BufferedImage img = null, imgb = null;
try {
img = ImageIO.read(new File(urlStr));
imgb = ImageIO.read(new File("images/card-back.png"));
} catch(IOException ex) {}
imageFront = new VisibleImage(img,p,w,h,c);
imageFront.hide();
imageBack = new VisibleImage(imgb,p,w,h,c);
}
public void flip() {
if(imageFront.isHidden()) {
imageFront.show();
imageBack.hide();
} else {
imageBack.show();
imageFront.hide();
}
}
public Boolean contains(Location p) {
if(imageFront.isHidden()) {
return imageBack.contains(p);
} else {
return imageFront.contains(p);
}
}
public void moveTo(double x, double y) {
imageFront.moveTo(x,y);
imageBack.moveTo(x,y);
}
public int getValue() {
return value;
}
public void hide() {
imageFront.hide();
imageBack.hide();
}
}
Grid.java
import objectdraw.*;
public class Grid {
private static final int ROWS = 4;
private static final int COLS = 4;
private Card[] cards = new Card[COLS * ROWS];
public Grid(double cardW, double cardH, DrawingCanvas c) {
int cnt = 0;
Location p1 = new Location(0,0);
Location p2 = new Location(0,(ROWS/2)*cardH);
for(int i = 0; i < ROWS; i++) {
for(int j = 0; j < COLS; j++) {
// Set up 2 of the same card, one at cnt and one at cnt + cards.length/2
if(cnt < cards.length/2) {
cards[cnt] = new Card(cnt+1,p1,cardW,cardH,c);
p1.translate(cardW,0);
cards[cnt + cards.length/2] = new Card(cnt+1,p2,cardW,cardH,c);
p2.translate(cardW,0);
cnt++;
}
}
p1.translate(-(cardW * COLS),cardH);
p2.translate(-(cardW * COLS),cardH);
}
}
private static int sel = -1;
public void select(Location p) {
for(int i = 0; i < cards.length; i++) {
// Find the correct card
if(cards[i].contains(p)) {
if(sel == -1) {
// This is the first card selected
System.out.printf("\nThis is the first card selected");
cards[i].flip();
sel = i;
break;
} else {
System.out.printf("\nThis is the second card");
// They selected the same card
if(i == sel) {
break;
} else {
// This is the second card selected and it's unique
// Flip it and check if they match. If they do, then hide both,
// if they don't then flip both back
cards[i].flip();
if(cards[i].getValue() == cards[sel].getValue()) {
try {
remove(cards[i], cards[sel]);
} catch(InterruptedException ex) {}
sel = -1;
break;
} else {
cards[i].flip();
cards[sel].flip();
sel = -1;
break;
}
}
}
}
} // for loop
}
public void remove(final Card card1, final Card card2) throws InterruptedException {
new Thread() {
public void run() {
sleep(2000);
card1.hide();
card2.hide();
}
}.start();
}
}
Client.java
import objectdraw.*;
public class Client extends WindowController {
public static void main(String[]args) {
new Client().startController(310,460);
}
Grid board;
public void begin() {
board = new Grid(75,102,canvas);
}
public void onMouseClick(Location p) {
board.select(p);
}
}
For reference all of this comes from the objectdraw library
I would separate the select() and remove() logic. Exactly here:
if(cards[i].getValue() == cards[sel].getValue()) {
remove(cards[i], cards[sel]); //call the remove
And remove() would start a Thread and remove the wanted values:
public void remove(final Card card1, final Card card2) {
new Thread() {
#Override
public void run() {
try {
sleep(2000);
} catch (InterruptedException e) {
}
card1.hide();
card2.hide();
}
}.start();
}
So this way you don't block the UI thread (the user can see the flip) and the new thread will hide the cards after the two seconds.

How to generate exceptions from RepaintManager

In connection with my question (may be), I found another exception type that I not able to catch and print-out from SwingWorker thread.
How can I to generate RepaintManager exceptions?
I read this CheckThreadViolationRepaintManager and this approach by Alexander Potochkin, but nothing seems to solve my issues.
If it helps, the example below throws prints multiple variations of the following Exception, mostly for each phase of the frame's UI delegate initialization. I used CheckThreadViolationRepaintManager, but the AspectJ variation looks interesting, too.
java.lang.Exception
at EDTViolation$CheckThreadViolationRepaintManager.checkThreadViolations(EDTViolation.java:43)
at EDTViolation$CheckThreadViolationRepaintManager.addDirtyRegion(EDTViolation.java:37)
at javax.swing.JComponent.repaint(JComponent.java:4734)
at java.awt.Component.repaint(Component.java:3168)
at javax.swing.JComponent.setFont(JComponent.java:2727)
at javax.swing.LookAndFeel.installColorsAndFont(LookAndFeel.java:191)
at javax.swing.plaf.basic.BasicPanelUI.installDefaults(BasicPanelUI.java:49)
at javax.swing.plaf.basic.BasicPanelUI.installUI(BasicPanelUI.java:39)
at javax.swing.JComponent.setUI(JComponent.java:662)
at javax.swing.JPanel.setUI(JPanel.java:136)
at javax.swing.JPanel.updateUI(JPanel.java:109)
at javax.swing.JPanel.(JPanel.java:69)
at javax.swing.JPanel.(JPanel.java:92)
at javax.swing.JPanel.(JPanel.java:100)
at javax.swing.JRootPane.createGlassPane(JRootPane.java:528)
at javax.swing.JRootPane.(JRootPane.java:348)
at javax.swing.JFrame.createRootPane(JFrame.java:255)
at javax.swing.JFrame.frameInit(JFrame.java:236)
at javax.swing.JFrame.(JFrame.java:159)
at EDTViolation.main(EDTViolation.java:12)
...
import java.lang.ref.WeakReference;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
/** #see https://stackoverflow.com/questions/7787998 */
public class EDTViolation {
public static void main(String args[]) {
RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
private static class CheckThreadViolationRepaintManager extends RepaintManager {
//http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html
private boolean completeCheck = true;
private WeakReference<JComponent> lastComponent;
public CheckThreadViolationRepaintManager(boolean completeCheck) {
this.completeCheck = completeCheck;
}
public CheckThreadViolationRepaintManager() {
this(true);
}
public boolean isCompleteCheck() {
return completeCheck;
}
public void setCompleteCheck(boolean completeCheck) {
this.completeCheck = completeCheck;
}
#Override
public synchronized void addInvalidComponent(JComponent component) {
checkThreadViolations(component);
super.addInvalidComponent(component);
}
#Override
public void addDirtyRegion(JComponent component, int x, int y, int w, int h) {
checkThreadViolations(component);
super.addDirtyRegion(component, x, y, w, h);
}
private void checkThreadViolations(JComponent c) {
if (!SwingUtilities.isEventDispatchThread() && (completeCheck || c.isShowing())) {
boolean repaint = false;
boolean fromSwing = false;
boolean imageUpdate = false;
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement st : stackTrace) {
if (repaint && st.getClassName().startsWith("javax.swing.")
&& // for details see
// https://swinghelper.dev.java.net/issues/show_bug.cgi?id=1
!st.getClassName().startsWith("javax.swing.SwingWorker")) {
fromSwing = true;
}
if (repaint && "imageUpdate".equals(st.getMethodName())) {
imageUpdate = true;
}
if ("repaint".equals(st.getMethodName())) {
repaint = true;
fromSwing = false;
}
if ("read".equals(st.getMethodName()) && "javax.swing.JEditorPane".equals(st.getClassName())) {
// Swing reads html from a background thread
return;
}
}
if (imageUpdate) {
//assuming it is java.awt.image.ImageObserver.imageUpdate(...)
//image was asynchronously updated, that's ok
return;
}
if (repaint && !fromSwing) {
//no problems here, since repaint() is thread safe
return;
}
//ignore the last processed component
if (lastComponent != null && c == lastComponent.get()) {
return;
}
lastComponent = new WeakReference<JComponent>(c);
violationFound(c, stackTrace);
}
}
protected void violationFound(JComponent c, StackTraceElement[] stackTrace) {
System.out.println();
System.out.println("EDT violation detected");
System.out.println(c);
for (StackTraceElement st : stackTrace) {
System.out.println("\tat " + st);
}
}
}
}
Create your own Exception
class RepaintManagerException extends Exception
{
String msg;
RepaintManagerException()
{
msg = new String("type message here");
}
}
Usage
public class My_Exception
{
public static void main (String args [ ])
{
try
{
// your code
if (expression) throw new RepaintManagerException( );
}
catch (RepaintManagerException e)
{
System.out.println (e);
}
}
}

Categories