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: