repaint() requests an erase and redraw (update)
after a small time delay. This allows time for more changes to the screen before
actually redrawing. Without the delay you would end up repainting the screen
many times a second, once for every tiny change. Imagine an odometer on screen
spinning frantically. Repaint delay allows the odometer to spin without
labouriously painting every single value. When you invoke repaint(),
it sends a message to the native GUI suggesting that it would be a good idea if
sometime in the distant future when it is convenient and things are slack and
when the GUI feels in the mood that the painting work should be done. The native
GUI enqueues this request, not the Java SystemEventQueue.
As windows occlude each other and reveal each other, the native GUI itself
decides that certain components, or parts of components also need to be
repainted. The native GUI merges all these requests and removes the duplicates.
It may reorder them so that background panels are repainted before the
overlaying components.
You can give a hint that some repaints are more important than others by
specifying a desired time in milliseconds by which you would like the repaint
done. This is not a request for a delay. The technique is thus not suitable for
animations. The GUI may decide to do the repaint right away, no matter what time
you tell it.
The GUI then repaints some components on it own and indirectly generates ComponentEvent.COMPONENT_RESIZED,
PaintEvent. UPDATE and PaintEvent.PAINT
events to request the Java side of things handle the painting. These are
enqueued into the SystemEventQueue just like ordinary
events. These events in turn trigger the update() and paint()
methods of the affected component to be called.
What triggers a repaint? There is a chain of
occurrences:
- invalidate() (marking a container, and its
enclosing containers, as needing to be re-laid out.) Sometimes application code
directly calls invalidate(). More often invalidate()
gets called as a side effect of adding or deleting a component, making a
component visible or invisible (The AWT is smart enough not to invalidate if you setVisible( true )
when the component is already visible), changing the size or location of a
component with setSize(), setLocation()
or setBounds(). invalidate()
is also called as the first step in processing a COMPONENT_RESIZED
event. Invoking invalidate by itself will not schedule a repaint
or validate().
- validate() (similar to pack).
This redoes the layout if necessary deciding on new sizes and locations of all
the components in the container. Most often it gets called directly by
application programmers, after a frame or other container been composed, but
just before the Frame.setVisible( true ). validate()
is also called as the second step in processing a COMPONENT_RESIZED
event. Invoking validate() by itself will not schedule
a repaint.
- setVisible( true ) (formerly known as show). setVisible( true )
for containers will typically invoke validate().
For now, it is probably safer not to count on setVisible
doing this, and invoke it yourself. Unless the component is already visible, setVisible( true )
for components will invalidate() the parent
container (and the enclosing containers too). Unless the container is already
invisible, setVisible( false ) for containers
will typically invalidate() the parent container (and
the enclosing containers too). Unless the component is already invisible, setVisible( false )
for components will invalidate() the parent
container (and the enclosing containers too). setVisible
also schedules a repaint if necessary.
- repaint() does not actually paint. It calls the
peer repaint which enqueues a request in some platform-dependent
way inside the native GUI for a repaint. repaint() is
also called as the third step in processing a COMPONENT_RESIZED
event. Calling repaint() directly won’t invoke setVisible()
or validate().
- SystemEventQueue When the native GUI is good and
ready, it indirectly enqueues a PaintEvent.PAINT or PaintEvent.UPDATE
event into Java’s SystemEventQueue using Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(Event
e). The event dispatcher in EventDispatcherThread.run
eventually pops events off the SystemEventQueue and
dispatches them. The PAINT and UPDATE
events are specially handled. For an update event, the component’s update()
method is invoked, and passed a Graphics state which
includes the clip region to be erased/redrawn.
- The update() method typically erases the component
(the erasure is automatically clipped), or does nothing if the paint
method will cover the entire area. Then it typically calls paint.
- The paint() method then actually does the drawing,
using the Graphics state object passed to it which
includes the clip region. The paint routine is usually
oblivious of the clip region. It just paints everything, and allows the AWT to
prune off the unwanted data. Container.update() and Container.paint()
arrange to paint all the contained lightweight components after painting the
background of the container. The native GUI handles scheduling or actually
repainting of any contained heavyweight components. updateAll
and paintAll are responsible for painting all the
contained components.
You might expect Label.setText(), as a side effect, to
call repaint(), but it does not. The native GUI is
smart enough to internally handle the repainting as a side effect of peer.setText().
Anything that just changes the contents of the display, but not its size or
location need not invalidate(), just a repaint()
since the old layout will do fine.
Instead of using the repaint mechanism, you can use Component.getGraphics
to handle your animation changes.
Faster Repaints
One simple technique to speed up repaint is to use
repaint( int x, int y, int width, int height );
When you know that only a portion of the component has changed. You can further
speed things up, in your paint or paintComponent
method by paying attention to the clip bounds. Anything you paint outside the
clipping region will just be ignored. For efficiency, you should make some
effort to avoid rendering large amounts of screen real estate outside it.
Rectangle r = g.getClipBounds();
Another technique is to render the image offscreen, and then when it is needed,
blast it onscreen with a drawImage bit/blt. The
rendered image might be huge. You just blast a piece of it onto the screen each
time. You can then scroll either horizontally or vertically very quickly by just
copying a piece of the huge image to the screen. You might construct your huge
image in tiles, and just create them and discard them as needed. They will fit
together seamlessly when you blast them on screen. You can get clever and have a
background thread preemptively create nearby offscreen tiles that might be soon
needed. Consider using soft references to keep
as many tiles around as will fit. The problem is, when you have a large Image,
the amount of memory needed for the bit map goes up as the square of the size.
You don’t necessarily have room for the entire Image
you pan over.
For super speed, you store some of your offscreen images in the video REGEN
buffer using VolatileImage. I used this technique
for the Las Vegas Hilton Hotel’s giant screens that slowly scroll images.
The size of the REGEN buffer is limited, so you just keep in there what you plan
to view in the near future. For continuous displays, you can use a squirrel cage
backing image that wraps around. You continuously update the backing image,
never creating a new object. To scroll, you copy two chunks from the backing
image to the screen. VolatileImage is tricky because
at any point your backing store can disappear on you.
Repaints and ScrollPanes
If you do a repaint of an Applet
containing a ScrollPane containing a Canvas,
from the Applet’s point of view, only the
visible part of the Canvas needs to be re-rendered.
If your intent was to invalidate the entire Canvas,
including the currently invisible parts, and re-render with different data, you
will have to also do a Canvas. repaint
to logically invalidate the entire invisible Canvas.
Otherwise, when you scroll, old pre-rendered stuff from before the repaint
will reappear. because the AWT caches your paint
renderings when it can.
Tips
- If repaint fails to trigger a call to paintComponent,
check the size of the JComponent at the time you
call repaint. If it is 0 × 0, the repaint
event will be ignored.
Learning More
Sun’s Javadoc on the
repaint method : available: