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 (Graphic User Interface) 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 (Advanced Windowing Toolkit) 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 (Regenerate) 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
Oracle’s Javadoc on
repaint package : available:
Oracle’s Javadoc on
paint package : available: