Reputation: 745
I have two classes. I try to move circle. When I type a break
statement in Game.java it draws a circle for me without movement. That's fine. However when I want to move circle(by deleting break
it displays nothing. I dont know how to debug that. I found out program is not going inside paint method when I use while without break;
statement.
Can anybody also tell me the way how to debug problem like that. Thanks in advance
App.java
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class App extends JFrame {
App() {
JFrame jf = new JFrame();
JPanel jp = new JPanel();
jf.add(jp);
jp.setLayout(new BorderLayout());
jf.setSize(500, 500);
jf.setTitle("Chain Reactor Game");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
Game game = new Game();
jp.add(game, BorderLayout.CENTER);
MenuBar menuBar = new MenuBar();
jf.setJMenuBar(menuBar.createMenuBar());
}
public static void main(String[] args) {
/*
* oracle recommendation to run swing applications in special thread
* they suggest it during thread implementation
*/
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new App();
}
});
}
}
Game.java
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JPanel;
public class Game extends JPanel {
private static final long serialVersionUID = 1L;
int x = 0;
int y = 0;
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
System.out.println("im goint to filloval now");
g2d.fillOval(x, y, 30, 30);
}
public Game() {
while(true) {
moveBall();
repaint();
try {
System.out.println("inside thread");
Thread.sleep(2000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
break; // if i delete this line it not drawing. Why is that ?
}
}
private void moveBall() {
x = x + 1;
y = y + 1;
}
}
Upvotes: 0
Views: 3163
Reputation: 347194
Let's break the code down...
The program starts here...
public static void main(String[] args) {
/*
* oracle recommendation to run swing applications in special thread
* they suggest it during thread implementation
*/
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new App();
}
});
}
This is good, it starts the GUI within the context of the Event Dispatching Thread, as described in Initial Threads...
Next, the App
is constructed...
App() {
JFrame jf = new JFrame();
JPanel jp = new JPanel();
jf.add(jp);
jp.setLayout(new BorderLayout());
jf.setSize(500, 500);
jf.setTitle("Chain Reactor Game");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
Game game = new Game();
jp.add(game, BorderLayout.CENTER);
MenuBar menuBar = new MenuBar();
jf.setJMenuBar(menuBar.createMenuBar());
}
Okay, this "seems" okay, but if we take a closer look at Game
public Game() {
while(true) {
moveBall();
repaint();
try {
System.out.println("inside thread");
Thread.sleep(2000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
break; // if i delete this line it not drawing. Why is that ?
}
}
Ah, here's a problem...the while
is been executed within the context of the Event Dispatching Thread, the EDT is used to process events and help schedule repaints, any thing that blocks the EDT will prevent it from, amongst other things, painting the UI
Start by taking a look at Concurrency in Swing
Now, you have a catch 22 situation here, Swing is a single threaded framework AND is NOT thread safe, this means that you can do long running/block process, but you also can't update the UI from outside the Event Dispatching Thread.
A simple solution in your case is to replace the while-loop
with a javax.swing.Timer
public Game() {
javax.swing.Timer timer = new javax.swing.Timer(2000, new javax.swing.ActionListener() {
public void actionPerformed(javax.swing.ActionEvent evt) {
moveBall();
repaint();
}
});
timer.start();
}
Take a look at How to use Swing Timers for more details...
As a side note, it's typically recommended to use paintComponent
to perform custom painting over paint
. Painting is a complex series of method calls, anything you can do to prevent screwing it up will keep your hair in place...
Have a look at Painting in AWT and Swing and Performing Custom Painting for more details
Upvotes: 4
Reputation: 285403
You need to get that while loop out of your Swing event thread as it's tying up the thread preventing Swing from performing its drawing functions or user interactions. You're basically stepping on the Swing event thread, violating Swing concurrency rules. Use a Swing Timer instead for simple animations which was built for this sort of thing.
Upvotes: 4
Reputation: 10183
When you leave the break;
in you have an infinite loop. It will never end, meaning that the line Game game = new Game();
will never return, meaning that the next line, which adds the Game
to the JPanel
will never run, which is why nothing is drawn on the screen. To get this to work, you need to create the GUI, and then perform the drawing.
One way to get this to work is to create a new Thread
to perform the continual drawing:
public Game() {
(new Thread() {
public void run() {
draw();
}
).start();
}
private void draw() {
while(true) {
moveBall();
repaint();
try {
System.out.println("inside thread");
Thread.sleep(2000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Alternatively, since you are setting up the GUI on the Swing thread, you could run the drawing on the main thread.
Upvotes: 2