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: