Reputation: 604
So I have created a small window with a ball bouncing around on it in Java and was trying to debug and watch the program work. Heres the important code for the question:
public class Ball {
private static final int SPEED = 2;
private int x = 0, y = 0;
private int xInc = SPEED, yInc = SPEED;
-> public void paint(Graphics2D g) {
g.fillOval(x, y, 30, 30);
}
}
public class Game extends JPanel {
Ball gameBall = new Ball();
public void paint(Graphics g) {
super.paint(g); // cleans the panel
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
gameBall.paint(g2d);
}
public void updateBall() {
gameBall.move(this.getWidth(), this.getHeight());
}
public static void main(String[] args) {
JFrame mainFrame = new JFrame("Simple Pong");
mainFrame.setSize(400, 400);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Game gamePanel = new Game();
mainFrame.add(gamePanel);
mainFrame.setVisible(true);
while (true) {
try {
gamePanel.updateBall();
gamePanel.repaint();
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println(e);
System.exit(1);
}
}
}
}
I set a method pointer for the entrance of the paint method of Ball, thinking it would cause the program to halt each time the repaint() method is called, since I thought it calls the paint method. Then, I could watch the ball move one increment at a time instead of placing a line pointer where the repaint method is.
When I do this, the program does not halt with each update of the placement of my ball, and instead halts in what seems like random increments. The ball will be close to the edge, then in the middle so many calls to paint had to of occured.
Question: Does the repaint method always call the paint method for my lightweight container? And if so, why does the debugger not halt with each increment of the ball and instead only halt to display the new position sometimes? Obviously if the ball is moving smoothly on the screen while running, something is painting the Ball object onto my Game Panel exactly as I would like it to.
Note: The same thing happens if we place a method breakpoint for the entrance of the paint method for Game, which I have extending JPanel and is what I paint the ball onto.
Upvotes: 0
Views: 3205
Reputation: 347194
Does the repaint method always call the paint method for my lightweight container?
No. Calls to repaint
are requests for an update. The repaint manager may optimise the requests and consolidate them down into a single event if it can.
Swing uses a passive painting algorithm which is designed to improve performance for systems which don't need to repainted all the time, painting only when it needs to be done.
On a side note, you should avoid overriding paint
of components and instead prefer using paintComponent
. Also beware, the Graphics
context passed to your paint methods is shared with everything else been painted, making changes to it might have undesirable side effects.
Instead, you should create a snapshot of the Graphics
context's state and modify it...
Graphics2D g2d = (Graphics2D g2d)g.create();
//... Update, modify, transform, do what you want...
g2d.dispose();
Take a look at Painting in AWT and Swing and Performing Custom Painting for more details.
Swing operates in a single thread. That is, all painting and even notification is done within the context of the Event Dispatching Thread, see Concurrency in Swing for more details.
When you hit your break point, the Event Dispatching Thread is STOPPED, but the while-loop
you have running is not, so the x/y position of the Ball
is still been updated, meaning that when the program resumes, the ball will appear to "jump" to a new location.
You can test this by placing a breakpoint on gamePanel.updateBall();
, the UI will continue to be update (after each request to repaint
), but will incrementally update as, as the state of the ball is not been altered.
The debugging process is not stopping the other threads that are running, but only effecting the thread from within which the breakpoint was triggered.
When Java calls you main
method, it is doing so from what is known as the "main thread". When you call setVisible
on your mainFrame
, a new thread is created (the Event Dispatching Thread), this allows the UI to run independently on the while(true)
, preventing it from "hanging" the UI. Check out Initial Threads for more details
Upvotes: 1
Reputation: 58848
repaint
does not directly call paint
. It just sets a variable so that Swing will, at some point, remember to call paint
.
Now, do you know about threads? Because the explanation of what's happening here involves threads.
Everything to do with Swing usually runs on one thread, called the "event thread". In particular, paint
gets called on the event thread.
main
is called on a thread called the "main thread". The main thread is not the event thread. Your main
method moves the ball (indirectly, by calling updateBall
). The main thread's code is roughly "move the ball, set the 'repaint me later' variable, repeat".
When your breakpoint is hit, only the thread that hit the breakpoint is paused. That means the event thread is paused. The main thread, which updates the ball's position, continues running. The ball's position keeps getting updated while you think the program is paused (because actually only one thread is paused).
When you unpause the event thread, the event thread will go back to doing what it was doing (i.e. painting). Since, by now, the "repaint me later" variable will have been set, it will paint the game panel again - but the ball will have moved quite far this time.
Upvotes: 1