Painting in AWT and Swing

   

Painting in AWT and Swing

Good Painting Code Is the Key to App Performance

By Amy Fowler

Painter's paletteIn a graphical system, a windowing toolkit is usually responsible for providing a framework to make it relatively painless for a graphical user interface (GUI) to render the right bits to the screen at the right time.

Both the AWT (abstract windowing toolkit) and Swing provide such a framework. But the APIs that implement it are not well understood by some developers -- a problem that has led to programs not performing as well as they could.

This article explains the AWT and Swing paint mechanisms in detail. Its purpose is to help developers write correct and efficient GUI painting code. While the article covers the general paint mechanism ( where and when to render), it does not tell how to use Swing's graphics APIs to render a correct output. To learn how to render nice graphics, visit the Java 2D Web site.

The main topics covered in this article are:

Evolution of the Swing Paint System

When the original AWT API was developed for JDK 1.0, only heavyweight components existed ("heavyweight" means that the component has it's own opaque native window). This allowed the AWT to rely heavily on the paint subsystem in each native platform. This scheme took care of details such as damage detection, clip calculation, and z-ordering. With the introduction of lightweight components in JDK 1.1 (a "lightweight" component is one that reuses the native window of its closest heavyweight ancestor), the AWT needed to implement the paint processing for lightweight components in the shared Java code. Consequently, there are subtle differences in how painting works for heavyweight and lightweight components.

After JDK 1.1, when the Swing toolkit was released, it introduced its own spin on painting components. For the most part, the Swing painting mechanism resembles and relies on the AWT's. But it also introduces some differences in the mechanism, as well as new APIs that make it easier for applications to customize how painting works.

Painting in AWT

To understand how AWT's painting API works, helps to know what triggers a paint operation in a windowing environment. In AWT, there are two kinds of painting operations: system-triggered painting, and application-triggered painting.

System-triggered Painting

In a system-triggered painting operation, the system requests a component to render its contents, usually for one of the following reasons:

  • The component is first made visible on the screen.
  • The component is resized.
  • The component has damage that needs to be repaired. (For example, something that previously obscured the component has moved, and a previously obscured portion of the component has become exposed).

App-triggered Painting

In an application-triggered painting operation, the component decides it needs to update its contents because its internal state has changed. (For example,. a button detects that a mouse button has been pressed and determines that it needs to paint a "depressed" button visual).

The Paint Method

Regardless of how a paint request is triggered, the AWT uses a "callback" mechanism for painting, and this mechanism is the same for both heavyweight and lightweight components. This means that a program should place the component's rendering code inside a particular overridden method, and the toolkit will invoke this method when it's time to paint. The method to be overridden is in java.awt.Component:

                                 public void paint(Graphics g)             
          

When AWT invokes this method, the Graphics object parameter is pre-configured with the appropriate state for drawing on this particular component:

  • The Graphics object's color is set to the component's foreground property.
  • The Graphics object's font is set to the component's font property.
  • The Graphics object's translation is set such that the coordinate (0,0) represents the upper left corner of the component.
  • The Graphics object's clip rectangle is set to the area of the component that is in need of repainting.

Programs must use this Graphics object (or one derived from it) to render output. They are free to change the state of the Graphics object as necessary.

Here is a simple example of a paint callback which renders a filled circle in the bounds of a component:

                                             public void paint(Graphics g) {         // Dynamically calculate size information         Dimension size = getSize();         // diameter         int d = Math.min(size.width, size.height);          int x = (size.width - d)/2;         int y = (size.height - d)/2;          // draw circle (color already set to foreground)         g.fillOval(x, y, d, d);         g.setColor(Color.black);         g.drawOval(x, y, d, d);     }                   
                

Developers who are new to AWT might want to take a peek at the PaintDemo example, which provides a runnable program example of how to use the paint callback in an AWT program.

 

In general, programs should avoid placing rendering code at any point where it might be invoked outside the scope of the paint callback. Why? Because such code may be invoked at times when it is not appropriate to paint -- for instance, before the component is visible or has access to a valid Graphics object. It is not recommended that programs invoke paint() directly.

To enable app-triggered painting, the AWT provides the following java.awt.Component methods to allow programs to asynchronously request a paint operation:

                                                public void repaint()      public void repaint(long tm)      public void repaint(int x, int y, int width, int height)      public void repaint(long tm, int x, int y,                     int width, int height)             
          

The following code shows a simple example of a mouse listener that uses repaint() to trigger updates on a theoretical button component when the mouse is pressed and released:

                                                 MouseListener l = new MouseAdapter() {             public void mousePressed(MouseEvent e) {                 MyButton b = (MyButton)e.getSource();                 b.setSelected(true);                                          b.repaint();                         }              public void mouseReleased(MouseEvent e) {                 MyButton b = (MyButton)e.getSource();                 b.setSelected(false);                                          b.repaint();                         }         };                                        
                

Components that render complex output should invoke repaint() with arguments defining only the region that requires updating. A common mistake is to always invoke the no-arg version, which causes a repaint of the entire component, often resulting in unnecessary paint processing.

paint() vs. update()

Why do we make a distinction between "system-triggered" and. "app-triggered" painting? Because AWT treats each of these cases slightly differently for heavyweight components (the lightweight case will be discussed later), which is unfortunately a source of great confusion.

For heavyweight components, these two types of painting happen in the two distinct ways, depending on whether a painting operation is system-triggered or app-triggered.

System-triggered painting

This is how a system-triggered painting operation takes place:

  1. The AWT determines that either part or all of a component needs to be painted.
  2. The AWT causes the event dispatching thread to invoke paint() on the component.

App-triggered painting

An app-triggered painting operation takes place as follows:

  1. The program determines that either part or all of a component needs to be repainted in response to some internal state change.

     

  2. The program invokes repaint() on the component, which registers an asynchronous request to the AWT that this component needs to be repainted.

     

  3. The AWT causes the event dispatching thread to invoke update() on the component.

    NOTE: If multiple calls to repaint() occur on a component before the initial repaint request is processed, the multiple requests may be collapsed into a single call to update(). The algorithm for determining when multiple requests should be collapsed is implementation-dependent. If multiple requests are collapsed, the resulting update rectangle will be equal to the union of the rectangles contained in the collapsed requests.

     

  4. If the component did not override update(), the default implementation of update() clears the component's background (if it's not a lightweight component) and simply calls paint().

Since by default, the final result is the same ( paint() is called), many people don't understand the purpose of having a separate update() method at all. While it's true that the default implementation of update() turns around and calls paint(), this update "hook" enables a program to handle the app-triggered painting case differently, if desired. A program must assume that a call to paint() implies that the area defined by the graphic's clip rectangle is "damaged" and must be completely repainted, however a call to update() does not imply this, which enables a program to do incremental painting.

Incremental painting is useful if a program wishes to layer additional rendering on top of the existing bits of that component. The UpdateDemo example demonstrates a program which benefits from using update() to do incremental painting.

In truth, the majority of GUI components do not need to do incremental drawing, so most programs can ignore the update() method and simply override paint() to render the component in it's current state. This means that both system-triggered and app-triggered rendering will essentially be equivelent for most component implementations.

Painting & Lightweight Components

From an application developer's perspective, the paint API is basically the same for lightweights as it is for heavyweights (that is, you just override paint() and invoke repaint() to trigger updates). However, since AWT's lightweight component framework is written entirely in common Java code, there are some subtle differences in the way the mechanism is implemented for lightweights.

 

How Lightweights Get Painted

For a lightweight to exist, it needs a heavyweight somewhere up the containment hierarchy in order to have a place to paint. When this heavyweight ancestor is told to paint its window, it must translate that paint call to paint calls on all of its lightweight descendents. This is handled by java.awt.Container's paint() method , which calls paint() on any of its visible, lightweight children which intersect with the rectangle to be painted. So it's critical for all Container subclasses (lightweight or heavyweight) that override paint() to do the following:

                                             public class MyContainer extends Container {         public void paint(Graphics g) {             // paint my contents first...             // then, make sure lightweight children paint                                      super.paint(g);          }     }                                        
                

If the call to super.paint() is missing, then the container's lightweight descendents won't show up (a very common problem when JDK 1.1 first introduced lightweights).

It's worth noting that the default implementation of Container.update() does not use recursion to invoke update() or paint() on lightweight descendents. This means that any heavyweight Container subclass that uses update() to do incremental painting must ensure that lightweight descendents are recursively repainted if necessary. Fortunately, few heavyweight container components need incremental painting, so this issue doesn't affect most programs.

 

Lightweights & System-triggered Painting

The lightweight framework code that implements the windowing behaviors (showing, hiding, moving, resizing, etc.) for lightweight components is written entirely in Java. Often, within the Java implementation of these functions, the AWT must explicitly tell various lightweight components to paint (essentially system-triggered painting, even though it's no longer originating from the native system). However, the lightweight framework uses repaint() to tell components to paint, which we previously explained results in a call to update() instead of a direct call to paint(). Therefore, for lightweights, system-triggered painting can follow two paths:

  • The system-triggered paint request originates from the native system (i.e. the lightweight's heavyweight ancestor is first shown), which results in a direct call to paint().

     

  • The system-triggered paint request originates from the lightweight framework (i.e., the lightweight is resized), which results in a call to update(), which by default is forwarded to paint() .

In a nutshell, this means that for lightweight components there is no real distinction between update() and paint(), which further implies that the incremental painting technique should not be used for lightweight components.

Lightweights and Transparency

Since lightweight components "borrow" the screen real estate of a heavyweight ancestor, they support the feature of transparency. This works because lightweight components are painted from back to front and therefore if a lightweight component leaves some or all of its associated bits unpainted, the underlying component will "show through." This is also the reason that the default implementation of update() will not clear the background if the component is lightweight.

The LightweightDemo sample program demonstrates the transparency feature of lightweight components.

"Smart" Painting

While the AWT attempts to make the process of rendering components as efficient as possible, a component's paint() implementation itself can have a significant impact on overall performance. Two key areas that can affect this process are:

  • Using the clip region to narrow the scope of what is rendered.
  • Using internal knowledge of the layout to narrow the scope of what children are painted (lightweights only).

If your component is simple -- for example, if it's a pushbutton -- then it's not worth the effort to factor the rendering in order to only paint the portion that intersects the clip rectangle; it's preferable to just paint the entire component and let the graphics clip appropriately. However, if you've created a component that renders complex output, like a text component, then it's critical that your code use the clip information to narrow the amount of rendering.

Further, if you're writing a complex lightweight container that houses numerous components, where the component and/or its layout manager has information about the layout, then it's worth using that layout knowledge to be smarter about determining which of the children must be painted. The default implementation of Container.paint() simply looks through the children sequentially and tests for visibility and intersection -- an operation that may be unnecessarily inefficient with certain layouts. For example, if a container layed out the components in a 100x100 grid, then that grid information could be used to determine more quickly which of those 10,000 components intersect the clip rectangle and actually need to be painted.

AWT Painting Guidelines

The AWT provides a simple callback API for painting components. When you use it, the following guidelines apply:

  1. For most programs, all client paint code should be placed within the scope of the component's paint() method.

     

  2. Programs may trigger a future call to paint() by invoking repaint(), but shouldn't call paint() directly.

     

  3. On components with complex output, repaint() should be invoked with arguments which define only the rectangle that needs updating, rather than the no-arg version, which causes the entire component to be repainted.

     

  4. Since a call to repaint() results first in a call to update(), which is forwarded to paint() by default, heavyweight components may override update() to do incremental drawing if desired (lightweights do not support incremental drawing)

     

  5. Extensions of java.awt.Container which override paint() should always invoke super.paint() to ensure children are painted.

     

  6. Components which render complex output should make smart use of the clip rectangle to narrow the drawing operations to those which intersects with the clip area.

Painting in Swing

Swing starts with AWT's basic painting model and extends it further in order to maximize performance and improve extensibility. Like AWT, Swing supports the paint callback and the use of repaint() to trigger updates. Additionally, Swing provides built-in support for double-buffering as well as changes to support Swing's additional structure (like borders and the UI delegate). And finally, Swing provides the RepaintManager API for those programs who want to customize the paint mechanism further.

Double Buffering Support

One of the most notable features of Swing is that it builds support for double-buffering right into the toolkit. It does the by providing a "doubleBuffered" property on javax.swing.JComponent:

                                                   public boolean isDoubleBuffered()     public void setDoubleBuffered(boolean o)                            
          

Swing's double buffer mechanism uses a single offscreen buffer per containment hierarchy (usually per top-level window) where double-buffering has been enabled. And although this property can be set on a per-component basis, the result of setting it on a particular container will have the effect of causing all lightweight components underneath that container to be rendered into the offscreen buffer, regardless of their individual "doubleBuffered" property values.

By default, this property is set to true for all Swing components. But the setting that really matters is on JRootPane, because that setting effectively turns on double-buffering for everything underneath the top-level Swing component. For the most part, Swing programs don't need to do anything special to deal with double-buffering, except to decide whether it should be on or off (and for smooth GUI rendering, you'll want it on!). Swing ensures that the appropriate type of Graphics object (offscreen image Graphics for double-buffering, regular Graphics otherwise) is passed to the component's paint callback, so all the component needs to do is draw with it. This mechanism is explained in greater detail later in this article, in the section on Paint Processing.

Additional Paint Properties

Swing introduces a couple of additional properties on JComponent in order to improve the efficiency of the internal paint algorithms. These properties were introduced in order to deal with the following two issues, which can make painting lightweight components an expensive operation:

  • Transparency: If a lightweight component is painted, it's possible that the component will not paint all of its associated bits if partially or totally transparent; this means that whenever it is repainted, whatever lies underneath it must be repainted first. This requires the system to walk up the containment hierarchy to find the first underlying heavyweight ancestor from which to begin the back-to-front paint operation.

     

  • Overlapping components: If a lightweight component is painted, its possible that some other lightweight component partially overlaps it; this means that whenever the original lightweight component is painted, any components which overlap the original component (where the clip rectangle intersects with the overlapping area) the overlapping component must also be partially repainted. This requires the system to traverse much of the containment hierarchy, checking for overlapping components on each paint operation.

Opacity

To improve performance in the common case of opaque components, Swing adds a read-write opaque property to javax.swing.JComponent:

                                                   public boolean isOpaque()     public void setOpaque(boolean o)                            
          


The settings are:

  • true: The component agrees to paint all of the bits contained within its rectangular bounds.
  • false: The component makes no guarantees about painting all the bits within its rectangular bounds.

The opaque property allows Swing's paint system to detect whether a repaint request on a particular component will require the additional repainting of underlying ancestors or not. The default value of the opaque property for each standard Swing component is set by the current look and feel UI object. The value is true for most components.

One of the most common mistakes component implementations make is that they allow the opaque property to default to true, yet they do not completely render the area defined by their bounds, the result is occasional screen garbage in the unrendered areas. When a component is designed, careful thought should be given to its handling of the opaque property, both to ensure that transparency is used wisely, since it costs more at paint time, and that the contract with the paint system is honored.

The meaning of the opaque property is often misunderstood. Sometimes it is taken to mean, "Make the component's background transparent." However, this is not Swing's strict interpretation of opacity. Some components, such as a pushbutton, may set the opaque property to false in order to give the component a non-rectangular shape, or to leave room around the component for transient visuals, such as a focus indicator. In these cases, the component is not opaque, but a major portion of its background is still filled in.

As defined previously, the opaque property is primarily a contract with the repaint system. If a component also uses the opaque property to define how transparency is applied to a component's visuals, then this use of the property should be documented. (It may be preferable for some components to define additional properties to control the visual aspects of how transparency is applied. For example, javax.swing.AbstractButton provides the ContentAreaFilled property for this purpose.)

Another issue worth noting is how opacity relates to a Swing component's border property. The area rendered by a Border object set on a component is still considered to be part of that component's geometry. This means that if a component is opaque, it is still responsible for filling the area occupied by the border. (The border then just layers its rendering on top of the opaque component).

If you want a component to allow the underlying component to show through its border area -- that is, if the border supports transparency via isBorderOpaque() returning false -- then the component must define itself to be non-opaque and ensure it leaves the border area unpainted.

"Optimized" Drawing

The overlapping component issue is more tricky. Even if none of a component's immediate siblings overlaps the component, it's always possible that a non-ancestor relative (such as a "cousin" or "aunt") could overlap it. In such a case the repainting of a single component within a complex hierarchy could require a lot of treewalking to ensure 'correct' painting occurs. To reduce unnecessary traversal, Swing adds a read-only isOptimizedDrawingEnabled property to javax.swing.JComponent:

                                                   public boolean isOptimizedDrawingEnabled()                            
          

The settings are:

  • true: The component indicates that none of its immediate children overlap.
  • false: The component makes no guarantees about whether or not its immediate children overlap

By checking the isOptimizedDrawingEnabled property, Swing can quickly narrow its search for overlapping components at repaint time.

Since the isOptimizedDrawingEnabled property is read-only, so the only way components can change the default value is to subclass and override this method to return the desired value. All standard Swing components return true for this property, except for JLayeredPane, JDesktopPane, and JViewPort.

The Paint Methods

The rules that apply to AWT's lightweight components also apply to Swing components -- for instance, paint() gets called when it's time to render -- except that Swing further factors the paint() call into three separate methods, which are invoked in the following order:

                                                     protected void paintComponent(Graphics g)     protected void paintBorder(Graphics g)     protected void paintChildren(Graphics g)                            
          

Swing programs should override paintComponent() instead of overriding paint(). Although the API allows it, there is generally no reason to override paintBorder() or paintComponents() (and if you do, make sure you know what you're doing!). This factoring makes it easier for programs to override only the portion of the painting which they need to extend. For example, this solves the AWT problem mentioned previously where a failure to invoke super.paint() prevented any lightweight children from appearing.

The SwingPaintDemo sample program demonstrates the simple use of Swing's paintComponent() callback.

Painting and the UI Delegate

Most of the standard Swing components have their look and feel implemented by separate look-and-feel objects (called "UI delegates") for Swing's Pluggable look and feel feature. This means that most or all of the painting for the standard components is delegated to the UI delegate and this occurs in the following way:

  1. paint() invokes paintComponent() .
  2. If the ui property is non-null, paintComponent() invokes ui.update().
  3. If the component's opaque property is true, ui.udpate() fills the component's background with the background color and invokes ui.paint().
  4. ui.paint() renders the content of the component.

This means that subclasses of Swing components which have a UI delegate (vs. direct subclasses of JComponent), should invoke super.paintComponent() within their paintComponent override:

                                                                    public class MyPanel extends JPanel {         protected void paintComponent(Graphics g) {             // Let UI delegate paint first              // (including background filling, if I'm opaque)                                        super.paintComponent(g);              // paint my contents next....         }     }                                                               
                

If for some reason the compone nt extension does not want to allow the UI delegate to paint (if, for example, it is completely replacing the component's visuals), it may skip calling super.paintComponent(), but it must be responsible for filling in its own background if the opaque property is true, as discussed in the section on the opaque property.

Paint Processing

Swing processes "repaint" requests in a slightly different way from the AWT, although the final result for the application programmer is essentially the same -- paint() is invoked. Swing doesthis to support its RepaintManager API (discussed later), as well as to improve paint performance. In Swing, painting can follow two paths, as described below:

(A) The paint request originates on the first heavyweight ancestor (usually JFrame, JDialog, JWindow, or JApplet):

  1. the event dispatching thread invokes paint() on that ancestor

     

  2. The default implementation of Container.paint() recursively calls paint() on any lightweight descendents

     

  3. When the first Swing component is reached, the default implementation of JComponent.paint() does the following:
    1. if the component's doubleBuffered property is true and double-buffering is enabled on the component's RepaintManager, will convert the Graphics object to an appropriate offscreen graphics.
    2. invokes paintComponent() (passing in offscreen graphics if doubled-buffered)
    3. invokes paintBorder() (passing in offscreen graphics if doubled-buffered)
    4. invokes paintChildren() (passing in offscreen graphics if doubled-buffered), which uses the clip and the opaque and optimizedDrawingEnabled properties to determine exactly which descendents to recursively invoke paint() on.
    5. if the component's doubleBuffered property is true and double-buffering is enabled on the component's RepaintManager, copies the offscreen image to the component using the original on-screen Graphics object.

    Note: the JComponent.paint() steps #1 and #5 are skipped in the recursive calls to paint() (from paintChildren(), described in step#4) because all the lightweight components within a Swing window hierarchy will share the same offscreen image for double-buffering.

(B) The paint request originates from a call to repaint() on an extension of javax.swing.JComponent:

  1. JComponent.repaint() registers an asynchronous repaint request to the component's RepaintManager, which uses invokeLater() to queue a Runnable to later process the request on the event dispatching thread.

     

  2. The runnable executes on the event dispatching thread and causes the component's RepaintManager to invoke paintImmediately() on the component, which does the following:

    1. uses the clip rectangle and the opaque and optimizedDrawingEnabled properties to determine the 'root' component from which the paint operation must begin (to deal with transparency and potentially overlapping components).
    2. if the root component's doubleBuffered property is true, and double-buffering is enabled on the root's RepaintManager, will convert the Graphics object to an appropriate offscreen graphics.
    3. invokes paint() on the root component (which executes (A)'s JComponent.paint() steps #2-4 above), causing everything under the root which intersects with the clip rectangle to be painted.
    4. if the root component's doubleBuffered property is true and double-buffering is enabled on the root's RepaintManager, copies the offscreen image to the component using the original on-screen Graphics object.

    NOTE: if multiple calls to repaint() occur on a component or any of its Swing ancestors before the repaint request is processed, those multiple requests may be collapsed into a single call back to paintImmediately() on the topmost Swing component on which repaint() was invoked. For example, if a JTabbedPane contains a JTable and both issue calls to repaint() before any pending repaint requests on that hierarchy are processed, the result will be a single call to paintImmediately() on the JTabbedPane, which will cause paint() to be executed on both components.

This means that for Swing components, update() is never invoked.

Although repaint() results in a call to paintImmediately(), it is not considered the paint "callback", and client paint code should not be placed inside of a paintImmediately(). In fact, there is no common reason to override paintImmediately() at all.

Synchronous Painting

As described in the previous section, paintImmediately() acts as the entry point for telling a single Swing component to paint itself, making sure that all the required painting occurs appropriately. This method may also be used for making synchronous paint requests, as its name implies, which is sometimes required by components which need to ensure their visual appearance 'keeps up' in real time with their internal state (e.g. this is true for the JScrollPane during a scroll operation).

Programs should not invoke this method directly unless there is a valid need for real-time painting. This is because the asynchronous repaint() will cause multiple overlapping requests to be collapsed efficiently, whereas direct calls to paintImmediately() will not. Additionally, the rule for invoking this method is that it must be invoked from the event dispatching thread; it's not an api designed for multi-threading your paint code!. For more details on Swing's single-threaded model, see the archived article "Threads and Swing."

The RepaintManager

The purpose of Swing's RepaintManager class is to maximize the efficiency of repaint processing on a Swing containment hierarchy, and also to implement Swing's 'revalidation' mechanism (the latter will be a subject for a separate article). It implements the repaint mechanism by intercepting all repaint requests on Swing components (so they are no longer processed by the AWT) and maintaining its own state on what needs to be updated (known as "dirty regions"). Finally, it uses invokeLater() to process the pending requests on the event dispatching thread, as described in the section on "Repaint Processing" (option B).

For most programs, the RepaintManager can be viewed as part of Swing's internal system and can virtually be ignored. However, its API provides programs the option of gaining finer control over certain aspects of painting.

 

The "Current" RepaintManager

The RepaintManager is designed to be dynamically plugged, although by default there is a single instance. The following static methods allow programs to get and set the "current" RepaintManager:

                                                    public static RepaintManager currentManager(Component c)     public static RepaintManager currentManager(JComponent c)     public static void           setCurrentManager(RepaintManager aRepaintManager)                            
          

Replacing The "Current" RepaintManager

A program would extend and replace the RepaintManager globally by doing the following:

                                                   RepaintManager.setCurrentManager(new MyRepaintManager());                            
          

You can also see RepaintManagerDemo for a simple running example of installing a RepaintManager which prints out information about what is being repainted.

A more interesting reason for extending and replacing the RepaintManager would be to change how it processes repaint requests. Currently the internal state used by the default implementation to track dirty regions is package private and therefore not accessible by subclasses. However, programs may implement their own mechanisms for tracking dirty regions and for collapsing requests by overriding the following methods:

                                                   public synchronized void        addDirtyRegion(JComponent c, int x, int y, int w, int h)      public Rectangle getDirtyRegion(JComponent aComponent)     public void markCompletelyDirty(JComponent aComponent)      public void markCompletelyClean(JComponent aComponent) {                            
          

The addDirtyRegion() method is the one which is invoked when repaint() is called on a Swing component, and thus can be hooked to catch all repaint requests. If a program overrides this method (and does not call super.addDirtyRegion()) then it becomes its responsibility to use invokeLater() to place a Runnable on the EventQueue which will invoke paintImmediately() on an appropriate component (translation: not for the faint of heart).

Global Control Over Double-Buffering

The RepaintManager provides an API for globally enabling and disabling double-buffering:

                                                    public void setDoubleBufferingEnabled(boolean aFlag)     public boolean isDoubleBufferingEnabled()                            
          

This property is checked inside of JComponent during the processing of a paint operation in order to determine whether to use the offscreen buffer for rendering. This property defaults to true, but programs wishing to globally disable double-buffering for all Swing components can do the following:

                                RepaintManager.currentManager(mycomponent).                   setDoubleBufferingEnabled(false);             
          

Note: since Swing's default implementation instantiates a single RepaintManager instance, the mycomponent argument is irrelevant.

Swing Painting Guidelines

Swing programs should understand these guidelines when writing paint code:

  1. For Swing components, paint() is always invoked as a result of both system-triggered and app-triggered paint requests; update() is never invoked on Swing components.

     

  2. Programs may trigger a future call to paint() by invoking repaint(), but shouldn't call paint() directly.

     

  3. On components with complex output, repaint() should be invoked with arguments which define only the rectangle that needs updating, rather than the no-arg version, which causes the entire component to be repainted.

     

  4. Swing's implementation of paint() factors the call into 3 separate callbacks:
    1. paintComponent()
    2. paintBorder()
    3. paintChildren()
    Extensions of Swing components which wish to implement their own paint code should place this code within the scope of the paintComponent() method ( not within paint()).

     

  5. Swing introduces two properties to maximize painting efficiency:
    • opaque: will the component paint all its bits or not?
    • optimizedDrawingEnabled: may any of this component's children overlap?

     

  6. If a Swing component's opaque property is set to true, then it is agreeing to paint all of the bits contained within its bounds (this includes clearing it's own background within paintComponent()), otherwise screen garbage may result.
  7. Setting either the opaque or optimizedDrawingEnabled properties to false on a component will cause more processing on each paint operation, therefore we recommend judicious use of both transparency and overlapping components.

  8. Extensions of Swing components which have UI delegates (including JPanel), should typically invoke super.paintComponent() within their own paintComponent() implementation. Since the UI delegate will take responsibility for clearing the background on opaque components, this will take care of #5.

  9. Swing supports built-in double-buffering via the JComponent doubleBuffered property, and it defaults to true for all Swing components, however setting it to true on a Swing container has the general effect of turning it on for all lightweight descendents of that container, regardless of their individual property settings.

  10. It is strongly recommended that double-buffering be enabled for all Swing components.

  11. Components which render complex output should make smart use of the clip rectangle to narrow the drawing operations to those which intersect with the clip area.

Summary

To get the best performance from these APIs, application programs must also take responsibility for writing programs which use the guidelines outlined in this document.

Left Curve
Java SDKs and Tools
Right Curve
Left Curve
Java Resources
Right Curve
JavaOne Banner
Java 8 banner (182)