I am writing a breakout Game and everything is working fine except one little thing.
I have a ball object which extends from thread and RepainterThread which can either implement Runnable or extends Thread which calls the paint method which re-renders the game field with bricks, a paddle, and a Ball
I have a Singelton GameController which connects all things.
I have a GameState with an isActive boolean to decide if the game should be paused or not.
I start the game and I can play and the program behaves as it should.
The Frame gets drawn the ball moves the bricks break when the ball hits everything fine.
Then I pause the game via a Button where I set is Active to false.
The Ball stops as it should. Then I hit the continue Button and isActive is again true. The ball object starts running again and the running method of Repainter Thread is allso triggered but the Swing Frame Freezes completely.
I tried various things these are my nearest approaches i have.
I spent days on it please help
public class Ball extends MovingObject {
private double hSpeed; // Horizontal velocity
private double vSpeed; // Vertical velocity
public Ball() {
this.color = Color.MAGENTA;
this.height = GameSettings.ballSize;
this.width = GameSettings.ballSize;
this.position = new Point2D.Double(GameSettings.defaultBallX, GameSettings.defaultBallY);
this.hSpeed = GameSettings.ballHSpeed;
this.vSpeed = GameSettings.ballYSpeed;
}
public Ball(Ball ball) {
color = ball.color;
height = ball.height;
width = ball.width;
position = ball.position;
hSpeed = ball.hSpeed;
vSpeed = ball.vSpeed;
}
public double getHSpeed() {
return this.hSpeed;
}
public double getVSpeed() {
return this.vSpeed;
}
public void run() {
try {
while (GameController.getInstance().getGameState().isActive()) {
System.out.println("Ich run im Ball");
Thread.sleep(10);
this.meetingWall();
this.meetingBrick();
this.position.setLocation(this.getPosition().getX() + this.hSpeed,
this.getPosition().getY() + this.vSpeed);
if (this.meetingPaddle()) {
this.newDirection();
}
if (this.out()) {
GameController.getInstance().stopGame();
this.stopThread();
}
this.position = this.getPosition();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void stopThread() {
GameController.getInstance().getGameState().setActive(false);
}
public void startThreadAgain() {
this.run();
}
#Override
public synchronized void start() {
this.position.setLocation(GameSettings.defaultBallX, GameSettings.defaultBallY);
super.start();
}
class GamePanel extends JPanel {
private static final long serialVersionUID = 1L;
private final Color backgroundColor = Color.BLACK;
GameState gameState = GameController.getInstance().getGameState();
public GamePanel() {
super();
this.addKeyListener(new KeyListener() {
// Dieser KeyListener soll auf Inputs der Pfeiltasten nach links
// <- und rechts -> hoeren und eine entsprechende Bewegung des
// Schlaegers erwirken, aber nur, wenn das Spiel nicht
// pausiert/gestoppt ist.
public void keyPressed(KeyEvent keyEvent) {
if (gameState.isActive()) {// gameState.isActive()
if (keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
gameState.getPaddle().setPositionRigth();
}
if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
gameState.getPaddle().setPositionLeft();
}
}
}
public void keyReleased(KeyEvent keyEvent) {
// TODO
}
public void keyTyped(KeyEvent arg0) {
}
});
}
public void paint(Graphics g) {
Graphics2D graphics2D = (Graphics2D) g;
graphics2D.setColor(this.backgroundColor);
graphics2D.fillRect(0, 0, this.getWidth(), this.getHeight());
for (int i = 0; i < gameState.getBricks().length; i++) {
for (int j = 0; j < gameState.getBricks()[i].length; j++) {
if ((gameState.getBricks()[i][j] != null)) {
graphics2D.setColor(gameState.getBricks()[i][j].getColor());
graphics2D.fillRect(
(i * GameSettings.brickWidth) + (i + 1) * (GameSettings.spaceAroundBrick + 1),
(j * GameSettings.brickHeight) + (j + 1) * GameSettings.spaceAroundBrick,
GameSettings.brickWidth, GameSettings.brickHeight);
}
scoreLabel.setText(this.gameState.getScore() + "");
gameState.getPaddle().draw(graphics2D);
gameState.getBall().draw(graphics2D);
}
}
}
}
// First Approach
private class RepainterThread implements Runnable {
public RepainterThread() {
System.out.println("RepainThreadCOntructor");
}
private void updateGUI() {
System.out.println("before invoke later");
System.out.println("repaint");
getGamePanel().requestFocus();
getGamePanel().repaint();
}
#Override
public void run() {
System.out.println("Repainter run");
System.out.println(GameController.getInstance().getGameState().isActive());
while (GameController.getInstance().getGameState().isActive()) {
System.out.println("inside while");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
updateGUI();
}
}
}
// Second Approach
private class RepainterThread implements Runnable {
public RepainterThread() {
System.out.println("RepainThreadCOntructor");
}
private void updateGUI(){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
System.out.println("repaint");
getGamePanel().requestFocus();
getGamePanel().repaint();
}
});
}
#Override
public void run() {
System.out.println("Repainter run");
System.out.println(GameController.getInstance().getGameState().isActive());
while (GameController.getInstance().getGameState().isActive()) {
System.out.println("inside while");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
updateGUI();
}
}
}
Frame should no longer freeze
please help
This is kind of a wild guess, but since according to your description the program works correctly until you pause and unpause it, the problem likely lies with the startThreadAgain method:
public void startThreadAgain() {
this.run();
}
Your code does not show how you call this method, but in that method you just call this.run(), i.e. you call the run method as a regular, blocking method, while probably still in the UI thread. Instead, you should call start() to start a proper new thread executing the run method.
public void startThreadAgain() {
super.start(); // not this start, otherwise it resets the ball's location
}
Related
Hi I'm studying the basics of java threads,
but I'd like to get some help because I don't understand the examples in the book.
https://imgur.com/a/pcdOq2n
In this example, when the user presses any key,
the yellow bar is filled with magenta and the bar's magenta is
reduced by a thread.
But the part I don't understand is if you press the key for a long time while bar is full,
the magenta of the bar does not reduce immediately when the key is released
when I checked. the fill() function was running for a certain time even when the key was released.
I want to know, why if the bar is full and the key is pressed for a long time,
the fill() function works for a certain time even when the key is released
and the bar does not reduce immediately.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class MyLabel extends JLabel {
private int barSize = 0;
private int maxBarSize;
public MyLabel(int maxBarSize) {
this.maxBarSize = maxBarSize;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.MAGENTA);
int width = (int) ( ( (double)( getWidth() ) )/maxBarSize *barSize );
if (width == 0) return;
g.fillRect(0, 0, width, this.getHeight());
}
synchronized void fill() {
if (barSize == maxBarSize) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
return;
}
}
barSize++;
repaint();
notify();
}
synchronized void consume() {
if (barSize == 0) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
return;
}
}
System.out.println("consume");
barSize--;
repaint();
notify();
}
}
class ComsumerThread extends Thread {
private MyLabel bar;
public ComsumerThread(MyLabel bar) {
this.bar = bar;
}
#Override
public void run() {
// TODO Auto-generated method stub
while(true) {
try {
sleep(100);
bar.consume();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
return;
}
}
}
}
public class TabAndThreadEx extends JFrame {
private MyLabel bar = new MyLabel(100);
public TabAndThreadEx(String title) {
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(null);
bar.setBackground(Color.orange);
bar.setOpaque(true);
bar.setLocation(20, 50);
bar.setSize(300, 20);
c.add(bar);
c.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
bar.fill();
}
});
setSize(350, 200);
setVisible(true);
c.setFocusable(true);
c.requestFocus();
ComsumerThread th = new ComsumerThread(bar);
th.start();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new TabAndThreadEx("Quickly press any key to fill the bar");
}
}
I have a 2D tile game and my hero can use magic(in this case fire), and the goal is to make the fireball move tile by tile until it finds either a wall or an enemy and to make the game stop while the fire is moving. I already have the fire moving and stopping if there is a wall or an enemy(and damaging the enemy). The problem is i can't seem to make the game show the fireball change from tile to tile, which means when i launch the fireball the game automatically shows me the fireball in its last position before collision, and then it disappears from the tiles. Anyone got any ideas as to what I am doing wrong or what i should do to make the game update the fire tile by tile?
(Btw i thought it might have something to do with my observer but I've tried thread.sleep and wait() and it isn't quite working, maybe I am doing it the wrong way).Thank you for your help and if you guys need any code just ask.
public class ImageMatrixGUI extends Observable {
private static final ImageMatrixGUI INSTANCE = new ImageMatrixGUI();
private final String IMAGE_DIR = "images";
private final int SQUARE_SIZE;
private final int N_SQUARES_WIDTH;
private final int N_SQUARES_HEIGHT;
private JFrame frame;
private JPanel panel;
private JPanel info;
private Map<String, ImageIcon> imageDB = new HashMap<String, ImageIcon>();
private List<ImageTile> images = new ArrayList<ImageTile>();
private List<ImageTile> statusImages = new ArrayList<ImageTile>();
private int lastKeyPressed;
private boolean keyPressed;
private ImageMatrixGUI() {
SQUARE_SIZE = 48;
N_SQUARES_WIDTH = 10;
N_SQUARES_HEIGHT = 10;
init();
}
public static ImageMatrixGUI getInstance() {
return INSTANCE;
}
public void setName(final String name) {
frame.setTitle(name);
}
private void init() {
frame = new JFrame();
panel = new RogueWindow();
info = new InfoWindow();
panel.setPreferredSize(new Dimension(N_SQUARES_WIDTH * SQUARE_SIZE, N_SQUARES_HEIGHT * SQUARE_SIZE));
info.setPreferredSize(new Dimension(N_SQUARES_WIDTH * SQUARE_SIZE, SQUARE_SIZE));
info.setBackground(Color.BLACK);
frame.add(panel);
frame.add(info, BorderLayout.NORTH);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initImages();
new KeyWatcher().start();
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
lastKeyPressed = e.getKeyCode();
keyPressed = true;
releaseObserver();
}
});
}
synchronized void releaseObserver() {
notify();
}
synchronized void waitForKey() throws InterruptedException {
while (!keyPressed) {
wait();
}
setChanged();
notifyObservers(lastKeyPressed);
keyPressed = false;
}
private void initImages() {
File dir = new File(IMAGE_DIR);
for (File f : dir.listFiles()) {
assert (f.getName().lastIndexOf('.') != -1);
imageDB.put(f.getName().substring(0, f.getName().lastIndexOf('.')),
new ImageIcon(IMAGE_DIR + "/" + f.getName()));
}
}
public void go() {
frame.setVisible(true);
}
public void newImages(final List<ImageTile> newImages) {
synchronized (images) { // Added 16-Mar-2016
if (newImages == null)
return;
if (newImages.size() == 0)
return;
for (ImageTile i : newImages) {
if (!imageDB.containsKey(i.getName())) {
throw new IllegalArgumentException("No such image in DB " + i.getName());
}
}
images.addAll(newImages);
}
}
public void removeImage(final ImageTile image) {
synchronized (images) {
images.remove(image);
}
}
public void addImage(final ImageTile image) {
synchronized (images) {
images.add(image);
}
}
public void clearImages() {
synchronized (images) {
images.clear();
}
public void newStatusImages(final List<ImageTile> newImages) {
synchronized (statusImages) {
if (newImages == null)
return;
if (newImages.size() == 0)
return;
for (ImageTile i : newImages) {
if (!imageDB.containsKey(i.getName())) {
throw new IllegalArgumentException("No such image in DB " + i.getName());
}
}
statusImages.addAll(newImages);
}
}
public void removeStatusImage(final ImageTile image) {
synchronized (statusImages) {
statusImages.remove(image);
}
public void addStatusImage(final ImageTile image) {
synchronized (statusImages) {
statusImages.add(image);
}
}
public void clearStatus() {
synchronized (statusImages) {
statusImages.clear();
}
}
#SuppressWarnings("serial")
private class RogueWindow extends JPanel {
#Override
public void paintComponent(Graphics g) {
// System.out.println("Thread " + Thread.currentThread() + "
// repainting");
synchronized (images) {
for (ImageTile i : images) {
g.drawImage(imageDB.get(i.getName()).getImage(), i.getPosition().getX() * SQUARE_SIZE,
i.getPosition().getY() * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE, frame);
}
}
}
}
#SuppressWarnings("serial")
private class InfoWindow extends JPanel {
#Override
public void paintComponent(Graphics g) {
synchronized (statusImages) {
for (ImageTile i : statusImages)
g.drawImage(imageDB.get(i.getName()).getImage(), i.getPosition().getX() * SQUARE_SIZE, 0,
SQUARE_SIZE, SQUARE_SIZE, frame);
}
}
}
private class KeyWatcher extends Thread {
public void run() {
try {
while (true)
waitForKey();
} catch (InterruptedException e) {
}
}
}
public void update() {
frame.repaint();
}
public void dispose() {
images.clear();
statusImages.clear();
imageDB.clear();
frame.dispose();
}
public Dimension getGridDimension() {
return new Dimension(N_SQUARES_WIDTH, N_SQUARES_HEIGHT);
}
}
Ok, I've got some code I setup to create a simple little overlay window to use as an alert message for a program I'm working on. Everything works fine the first run through, but trying to run through it again, it freezes the whole thing, forcing me to terminate it via the debugger or task manager. I know I'm doing something wrong, I'm just not sure what, due to my limited experience with Java.
Below is the code I use to setup my window and place it in the lower-right corner above the taskbar:
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public static JWindow alertWindow() {
JWindow newWin = new JWindow();
JPanel panel = new JPanel();
BufferedImage img = null;
try {
img = ImageIO.read(Main.class.getResource("/images/test.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
JLabel imgLbl = new JLabel(new ImageIcon(img));
panel.add(imgLbl);
newWin.setContentPane(panel);
newWin.pack();
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(newWin.getGraphicsConfiguration());
int taskBar = scnMax.bottom;
int x = screenSize.width - newWin.getWidth();
int y = screenSize.height - taskBar - newWin.getHeight();
newWin.setLocation(x,y);
newWin.setVisible(true);
final PulseWindow pulseWin = new PulseWindow(newWin);
pulseWin.getWindow().addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent click) {
if(SwingUtilities.isRightMouseButton(click)) {
pulseWin.stopPulsing();
pulseWin.destroyPulse();
} else {
System.out.println(pulseWin.isPulsing());
if(pulseWin.isPulsing()) {pulseWin.stopPulsing();}
else {pulseWin.startPulse();}
}
}
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
});
pulseWin.startPulsing();
return newWin;
}
And below is the code I've setup to make it pulse to draw the user's attention:
import javax.swing.JWindow;
public class PulseWindow {
private boolean pulse = true;
private boolean doPulse = true;
private Float floor = 0.50f;
private JWindow win;
public PulseWindow(JWindow win) {
this.win = win;
}
public void startPulsing() {
pulse = true;
boolean decreasing = true;
double inc2 = 0.03;
double current = win.getOpacity();
while(pulse) {
if(doPulse) {
if(decreasing) {
current = current - inc2;
if((float) current <= floor) {
current = floor;
win.setOpacity((float) current);
decreasing = false;
} else {
win.setOpacity((float) current);
}
} else {
current = current + inc2;
if((float) current >= 1.0f) {
current = 1.0;
win.setOpacity((float) current);
decreasing = true;
} else {
win.setOpacity((float) current);
}
}
} else {
current = 1.0;
win.setOpacity(1.0f);
decreasing = true;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
win.setOpacity(1.0f);
}
public void destroyPulse() {
pulse = false;
win.dispose();
}
public boolean isPulsing() { return doPulse; }
public void setFloor(float floor) { this.floor = floor; }
public void stopPulsing() { doPulse = false; }
public void startPulse() { doPulse = true; }
public JWindow getWindow() { return win; }
}
Anyway, like I mentioned, it works fine for the first use, but as soon as you close the window via the right-click then attempt to re-run it later (whether by calling the startPulsing() method or by completely reinitializing the whole class with a new JWindow by calling alertWindow() again), the whole program freezes. Any ideas why this is?
Like I said, I'm still a bit of a newbie to Java, so if you see anything else I'm doing wrong/inefficiently, as well, feel free to point it out so I can do it correctly.
Edit:
I'm starting to think the issue is with JWindows, now. I setup some other code for a different method of displaying the alert and, while it doesn't freeze this time, it doesn't work as intended, either.
public class AlertWindow extends JWindow {
private static Border compound = BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createLoweredBevelBorder());
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public AlertWindow() {
JPanel panel = new JPanel();
panel.setBorder(compound);
panel.setBackground(Color.RED);
JLabel imgLbl = new JLabel("Enter Alert Msg Here!");
imgLbl.setFont(new Font(null,Font.BOLD,16));
panel.add(imgLbl);
setContentPane(panel);
pack();
this.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent click) {
if(SwingUtilities.isLeftMouseButton(click)) {
scrollOff();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
scrollOn();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
});
scrollOn();
}
public void scrollOn() {
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration());
int taskBar = scnMax.bottom;
int x = screenSize.width - getWidth();
int yEnd = screenSize.height - taskBar - getHeight();
int yStart = screenSize.height;
setLocation(x,yStart);
setVisible(true);
int current = yStart;
while(current > yEnd) {
current-=2;
System.out.println(current);
setLocation(x,current);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void scrollOff() {
int x = screenSize.width - getWidth();
int yEnd = screenSize.height;
int yStart = this.getBounds().y;
setLocation(x,yStart);
int current = yStart;
while(current < yEnd) {
current+=2;
setLocation(x,current);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
setVisible(false);
}
}
Just like the pulsing window issue, it works as intended the first time, then breaks on subsequent uses. In this case, the only thing that breaks is the scrollOn() command. It scrolls on while invisible, then becomes visible once it reaches its destination. The console output of the position clearly shows that it's moving, but you can't see it until it stops moving.
Edit 2:
And back to feeling dumb... I found the issue (actually found it some time ago but forgot to update this...). The issue ended up being that I was only using the runnable and not placing it inside of a new Thread() object. For some reason I was thinking runnable objects created their own new threads, but once I figured out my mistake, it was an easy fix. Obviously I still have a long ways to go in learning Java...
Edit:
Ok, now I'm annoyed... apparently it still breaks if you attempt to run it from an action listener of some kind. My most recent version of the PulseAlert class (below) that calls into the PulseWindow class shown in the original answer further below:
public class PulseAlert {
private static Border compound = BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createLoweredBevelBorder());
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public void runAlert() throws InterruptedException {
final PulseWindow pulseWin = new PulseWindow(alertWindow());
pulseWin.getWindow().addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent click) {
if(SwingUtilities.isRightMouseButton(click)) {
pulseWin.stopPulsing();
pulseWin.destroyPulse();
} else if(SwingUtilities.isLeftMouseButton(click) && pulseWin.isPulsing()) {
pulseWin.stopPulsing();
} else if(SwingUtilities.isLeftMouseButton(click) && !pulseWin.isPulsing()) {
pulseWin.startPulsing();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
});
try {
pulseWin.startPulse();
} catch (Exception e) {
e.printStackTrace();
}
while(pulseWin.pulserActive()) {
Thread.sleep(100);
}
System.out.println("done with second SW");
}
public static JWindow alertWindow() {
System.out.println("Start");
JWindow newWin = new JWindow();
JPanel panel = new JPanel();
panel.setBorder(compound);
panel.setBackground(Color.RED);
JLabel imgLbl = new JLabel("Enter Alert Msg Here!");
imgLbl.setFont(new Font(null,Font.BOLD,16));
panel.add(imgLbl);
newWin.setContentPane(panel);
newWin.pack();
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(newWin.getGraphicsConfiguration());
int taskBar = scnMax.bottom;
int x = screenSize.width - newWin.getWidth();
int y = screenSize.height - taskBar - newWin.getHeight();
newWin.setLocation(x,y);
newWin.setVisible(true);
return newWin;
}
}
And below is how I can call up the alert window - repeatedly, if I like, as long as it's outside of an action listener.
PulseAlert alertPulse = new PulseAlert();
alertPulse.runAlert();
The above code works flawlessly until placed into an action listener of some kind such as:
trayIcon.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
alertPulse.runAlert();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
});
Once the runAlert() method is called from an action listener, the whole thing freezes like it did previously. Runs perfectly fine until then. Any ideas what is causing this? Is this a bug in Java or am I doing something wrong?
Original Answer:
Ok, I feel pretty dumb, now. All I had to do to fix the issue was place the startPulsing() contents into a new runnable and it all works, and as many times as I need it to.
public void startPulsing() throws Exception {
new Runnable() {
#Override
public void run() {
pulse = true;
win.setVisible(true);
boolean decreasing = true;
double inc = 0.05;
double current = win.getOpacity();
while(pulse) {
if(doPulse) {
if(decreasing) {
current = current - inc;
if((float) current <= floor) {
current = floor;
win.setOpacity((float) current);
decreasing = false;
} else {
win.setOpacity((float) current);
}
} else {
current = current + inc;
if((float) current >= 1.0f) {
current = 1.0;
win.setOpacity((float) current);
decreasing = true;
} else {
win.setOpacity((float) current);
}
}
} else {
current = 1.0;
win.setOpacity(1.0f);
decreasing = true;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
win.setOpacity(1.0f);
}
}.run();
}
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
I have a class that implements MouseListener (JPanel). When I click on the panel something happens. What I want is some kind of while-loop that loops as long as left mousebutton is pressed down.
#Override
public void mousePressed(MouseEvent e) {
while(e.isPressedDownD) { // <--
//DO SOMETHING
}
}
This obviously doesn't work, but I hope you understand what I'm trying to achieve.
The whole class for those that are interested:
package control;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import model.GridModel;
import view.GUIView;
public class MapListener implements MouseListener{
private GridModel model;
private GUIView view;
private int posX;
private int posY;
public MapListener(GridModel model, GUIView view) {
this.model = model;
this.view = view;
}
#Override
public void mouseClicked(MouseEvent e) {
posX = e.getX();
posY = e.getY();
model.setMouseAtX(posX);
model.setMouseAtY(posY);
view.paintTile();
System.out.println("X: " + posX + " Y: " + posY);
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent e) {
while(e.getModifiers() == MouseEvent.MOUSE_PRESSED) { //Obviously doesn't work
//DO SOMETHING
}
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
As pointed out by other answers, the place to do your work is not in the mouse event listener methods.
Also there is no explicit "mouse pressed" notion in MouseEvent, so you must track that yourself. I have provided an example of how to do this. Also note the MouseEvent.BUTTON1 references, as this is just to track the state of the left mouse button.
This is where you must start to learn about concurrency. For that reason, I've added in a synchronized method as you need to be aware that funny things happen when multiple threads access properties at the same time, and synchronized is a mechanism for keeping this sane. Consider it further reading beyond the scope of this example.
Untested, but this should work:
volatile private boolean mouseDown = false;
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
mouseDown = true;
initThread();
}
}
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
mouseDown = false;
}
}
volatile private boolean isRunning = false;
private synchronized boolean checkAndMark() {
if (isRunning) return false;
isRunning = true;
return true;
}
private void initThread() {
if (checkAndMark()) {
new Thread() {
public void run() {
do {
//do something
} while (mouseDown);
isRunning = false;
}
}.start();
}
}
Why do so many of these answers wrongly assert that there is no explicit "mouse pressed" notion in MouseEvent?
Although the other commenters are correct that the OP should not be doing all that stuff in an event handler, there are other situations in which querying the button state in a mouse listener is useful. In those cases, you actually CAN determine the button down state. For example:
#Override
public void mouseExited(MouseEvent event) // Or any other mouse event handler...
{
int buttonsDownMask = MouseEvent.BUTTON1_DOWN_MASK
| MouseEvent.BUTTON2_DOWN_MASK
| MouseEvent.BUTTON3_DOWN_MASK; // Or whichever buttons you care about...
if ( (event.getModifiersEx() & buttonsDownMask) != 0 )
System.out.println("Hey! Some button is pressed!");
}
Notice in particular the use of the MouseEvent.getModifiersEx() method, along with MouseEvent.BUTTON1_DOWN_MASK and friends.
You could create a new Thread containing your while loop.
You start that Thread when the mouse button is pressed. You stop it when the mouse button is released.
You shouldn't be doing that in an event handler as no more events will be processed until the event handler exits.
What you want to achieve can be done with a separate worker thread. Create the thread from the mousePressed listener, do whatever you want to do in the thread (this should contain the while loop) and make the thread exit when the mouse is released (your mouseReleased listener should notify the thread).
for example
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ClickListener extends MouseAdapter implements ActionListener {
private final static int clickInterval = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
private MouseEvent lastEvent;
private Timer timer;
public ClickListener() {
this(clickInterval);
}
public ClickListener(int delay) {
timer = new Timer(delay, this);
}
#Override
public void mouseClicked(MouseEvent e) {
/*if (e.getClickCount() > 2) {
return;
}
lastEvent = e;
if (timer.isRunning()) {
timer.stop();
doubleClick(lastEvent);
} else {
timer.restart();
}*/
if (timer.isRunning() && !e.isConsumed() && e.getClickCount() > 1) {
System.out.println("double");
timer.stop();
} else {
timer.restart();
}
}
#Override
public void actionPerformed(ActionEvent e) {
timer.stop();
singleClick(lastEvent);
}
public void singleClick(MouseEvent e) {
}
public void doubleClick(MouseEvent e) {
}
public static void main(String[] args) {
JFrame frame = new JFrame("Double Click Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addMouseListener(new ClickListener() {
#Override
public void singleClick(MouseEvent e) {
System.out.println("single");
}
#Override
public void doubleClick(MouseEvent e) {
System.out.println("double");
}
});
frame.setPreferredSize(new Dimension(200, 200));
frame.pack();
frame.setVisible(true);
}
}