Documentation of repaint() in Component class.
Exercise:
Open Java console on your browser and type the letter t
Then, your Java console will print all active threads.
One of a number of system threads. This thread is responsible for accepting input events and calling the paint() method. The programmer should leave calling paint() to this thread. Java applets rarely call paint() directly.
There are two ways paint() calls are generated:The GUI thread makes these calls. Every applet or application with a GUI (i.e., a Frame) has a GUI thread.
There are four situations when the GUI thread calls paint() spontaneously.
The repaint() method is the one invoked by a program to do drawing. Their are 4 versions of this method but the one with no arguments is usually used. Drawing via repaint() most often takes place in response to user input.
repaint() ==> update() ==(usually calls)==> paint()
repaint() does not invoke paint() directly. It schedules a call to an intermediate method, update(). Finally, update() calls paint() (unless you override update).
The reason for this complexity is Java's support for concurrent programming. It does this using threads.
However, let's start with a discussion what happens if paint() or update()
are called directly.
What is of concern here is what happens if the user, for example, covers all or part of your drawing by another window, and then uncovers your drawing again. Does it survive? Similarly, what happens if the user resizes the browser window?
Not
the right way -- RepaintApplet.Java
Source
When paint() is called after init(), or after the applet has been
covered or iconified, it uses the default paint() which just paints
the background colour, thereby erasing the image. (Note, however,
the useful method getGraphics() used in this example.)
Putting
drawing in the
paint() method,
calling paint() directly --
RepaintApplet1.Java
Source
A paint() method has been added. This works but the method paint() is overly complex.
An
improvement using
update() method
to redraw the background --RepaintApplet2.Java
Source
This is a simpler way to do it, because
update() automatically covers the drawing area with
the background colour, essentially an erasing operation, and then
automatically calls paint().
Overriding
update() to prevent background overlay. -- RepaintApplet2a.Java
Source.
The paint() method is always called by the AWT main GUI thread. It is possible that other applets are also using that thread. The environment also uses the thread to reconstruct components damaged by, for example, resizing or overlaying or iconifying/deiconifying. If you have a complex paint() method and call it directly you may tie everything else up, thereby degrading performance and defeating the multitasking nature of Java. A bug in your code may not only stop you applet but everything else running on the page as well.
The repaint() method is designed to avoid these problems. However it can be tricky to use as it was mentioned before
The behaviour of the following example is slightly different from the previous examples, if you only consider the applet in isolation. In particular, it implements the different interface: MouseMotionListener interface. But the main difference is that this version is more 'polite' than the others: it uses repaint.
A
simple repaint() example -- RepaintApplet3.Java
source
(In this example, drag the red dot with the mouse.)
Another,
slightly different version RepaintApplet3a.java
Source
(In this case update() is not overridden.)
If repaint() is just called by user interactions with components you should have no problems using repaint() this way. However, if repaint() is called in a tight loop, the AWT thread queue may be overwhelmed and strange things may happen.
repaint() merely requests the AWT thread to call update(). It then returns immediately. This type of behaviour is called asynchronous. How the AWT thread reacts to the request is up to it. This behavour can lead to problems if you are not careful:
Multiple repaint() requests can unintentionally be combined into one
Click
the applet and see the expected output
source (a 1-thread approach).
The behavour which causes the most trouble is the fact that the AWT may combine multiple rapid fire repaint() requests into one request. In practice this behaviour often means that only the last repaint() request in the sequence actually causes update() and paint() to be called. The result is that part of your beautiful drawing goes missing.
This problem usually occurs when repaint() is being called from a program loop.
Example
of rapid fire repaint() requests-- CirclesApplet1.java
source
(only 1 last circle is painted)
On the other hand, some textbooks recommend that you use the Thread.sleep() method. The idea is to put,
try {
Thread.sleep(100);
}
catch(InterruptedException
ex) {}
either just before or just after the repaint()
call. The Thread.sleep(100) puts the currently running thread
to sleep for 100 milliseconds. This allows other threads, if there
are any, a chance to run.
This doesn't work! Putting the AWT thread to sleep solves nothing. Nothing happens, it wakes up, another repaint() request comes in but the AWT thread goes to sleep again. So the pile up of requests just gets frozen for the sleep time. In the end they are combined the same way as without the sleep and your drawing is still ruined.
Thread.sleep()
no help example - CirclesApplet1a.Java
source
Using Thread.sleep() in a programmer created thread to slow down rapid fire repaint() calls solves the problem of combined repaint() requests. The AWT GUI thread keeps running and has time to deal with requests to update() and paint() because the thread which is the source of the requests is put to sleep periodically and thus slowed down. The AWT thread meanwhile goes on its merry way.
Thread.sleep()
solves rapid fire repaint() problem -- CirclesApplet2.Java
source
The previous example show that it is necessary to understand more about threads.