28.4 Painting and Repainting


Repainting is a fundamental task for a graphical application, but one that is rarely explained in the detail you'd expect for something so central. This section is intended to give you a better feel for how painting and repainting via the repaint manager and JComponent work. You typically do not need to get involved with the RepaintManager class, and only the extremely brave override it. However, there are some instances in which a firm understanding of the repaint manager can help avoid confusion, and, starting with SDK 1.4, Swing provides a way for savvy code to take advantage of accelerated graphics hardware when it is available.

28.4.1 Swing Responsibilities

Recall that Swing uses lightweight components, which are drawn inside heavyweight top-level containers. Since the operating system has no knowledge of lightweight components (that's what makes them lightweight), it can't help coordinate their repainting. To continue the analogy first presented in the earlier lightweight and heavyweight discussion, Swing is responsible for painting and repainting everything inside its own easels. Swing delegates this duty to a RepaintManager class, which organizes and schedules repainting when told to do so.

28.4.2 The RepaintManager Class

The RepaintManager class is responsible for keeping track of the components (and the components' parts) that have become dirty, which means that they need to be repainted. Note that the "dirty region" does not necessarily include the entire region of affected components, but often only portions of them. The RepaintManager is also charged with the second responsibility of revalidating components that have been marked invalid. Both responsibilities ultimately result in the same thing: redrawing the component.

There is only one RepaintManager per thread group. Like the FocusManager class, you don't instantiate one directly. Instead, the static methods currentManager( ) and setCurrentManager( ) retrieve and set the current repaint manager, respectively. (Note that there is no RepaintManager.getCurrentManager( ) method.) Once a repaint manager is activated for a thread group, it remains active until it is replaced or that thread group is shut down.

You typically access the current manager as follows:

RepaintManager rm = RepaintManager.currentManager( );

At the heart of the RepaintManager are two data structures: a Hashtable of component references and their rectangular regions that need to be repainted, and a Vector of invalidated components. You can add component regions to the hashtable of dirty regions with the addDirtyRegion( ) method. Likewise, you can add a component to the invalidation vector with a call to addInvalidComponent( ) . If you wish to remove a component from the invalidation vector, you can remove it with a call to removeInvalidComponent( ).

Here are some important rules for working with the repaint manager:

  • If a component has a dirty region on the repaint queue, and another region from the same component is added, the repaint manager takes the rectangular union of the two sections. As a result, there is never more than one dirty region per component on the queue at any time.

  • If a component has been invalidated with the addInvalidComponent( ) method, the RepaintManager invalidates the first ancestor of this component to return true for the isValidateRoot( ) method (typically a container). This has the desirable side effect of invalidating all the components below it.

  • Repainting and revalidating are handled via work requests, which are Runnable objects sent to the system event queue. Since the work request repaints and revalidates any components that need it at the time it is processed, it doesn't make sense (and would be wasteful) to have more than one in the event queue for each top-level container. The SystemEventQueueUtilities class takes care of this optimization for the repaint manager.

You can get the current dirty region for each component by using the getDirtyRegion( ) method. If you want to mark the entire component as dirty, forcing a complete redraw on its next paint, use the markCompletelyDirty( ) method. You can check to see if an entire component's region is marked as dirty with the isCompletelyDirty( ) method if this will let your paint operation run more efficiently because it doesn't need to worry about optimizing or clipping its drawing operations. To remove a component's dirty region from the list, use the markCompletelyClean( ) method.

The RepaintManager class is equipped with a double-buffering mechanism, which it provides as a service to all JComponent objects. By default, it is enabled for all components that wish to take advantage of it, unless the operating system is already performing double-buffering natively (as Mac OS X does). Many built-in components use double-buffering effectively for smooth drawing. You can manually override the operation of this feature using the setDoubleBufferingEnabled( ) method, but there should be no reason to do this because its default value is optimized for the graphics environment. The maximum size of the double buffer is, by default, the size of the entire screen. If for some reason you need to, you can alter it by calling setDoubleBufferMaximumSize( ). To find out the current setting, use getDoubleBufferMaximumSize( ).

SDK 1.4 introduced a new VolatileImage class that allows components to take advantage of accelerated graphics hardware for extremely efficient double-buffering. Because using such hardware presents some special conditions that don't arise with normal Image buffers, components must explicitly request them via the getVolatileOffscreenBuffer( ) method. It's worth learning how to use this method (and the VolatileImage class) because of the potential performance boost it can give your components.

28.4.2.1 Key methods

The Swing repaint manager works closely with a few key methods in JComponent: paint( ) , repaint( ), revalidate( ), and paintImmediately( ). A call to repaint( ) at the component level results in a region being added to the dirty list queue and, consequently, scheduled for repainting. This sets up a work request that is placed in the system event queue. Once the work request is processed, the current RepaintManager calls back the paintImmediately( ) method of the component. This method finally renders the dirty portion of the component (without delay, as it's running in the event dispatch thread).

The revalidate( ) method in JComponent adds the component to the RepaintManager invalidation queue. At that point, the revalidation of the component and its ancestors, if necessary, is sent as a work request to the system event queue. Once the request is dequeued, the method calls the validate( ) method of the appropriate container, recomputing its layout to account for the changes that prompted the revalidation request. Figure 28-10 shows the RepaintManager at work repainting dirty regions and revalidating invalid components.

Figure 28-10. Repainting and revalidating with JComponent and the RepaintManager
figs/swng2.2810.gif
28.4.2.2 Methods
public static RepaintManager currentManager(JComponent comp)

Return the current RepaintManager. If there isn't one available, Swing initializes one and sets it for this thread group.

public static void setCurrentManager(RepaintManager aRepaintManager)

Set the current RepaintManager for the invoker's thread group. It's unlikely you'll ever use this method.

public void addInvalidComponent(JComponent invalidComponent)

Add a component to the invalidation vector. The method searches for the first ancestor component that returns true for isValidateRoot( ) and queues a validation and repaint for that component, and consequently all components below it. Generally, JComponent's revalidate( ) method calls this for you.

public void removeInvalidComponent(JComponent component)

Remove a component from the invalidation vector.

public void addDirtyRegion(JComponent c, int x, int y, int w, int h)

Add a rectangular region for a specific component to the hashtable of dirty regions that need to be repainted. If a rectangular region for that component already exists in the hashtable, a rectangle that encompasses both regions is calculated and placed on the hashtable to represent that component. There is at most one rectangular region for each component on the dirty-region hashtable at any time.

public Rectangle getDirtyRegion(JComponent aComponent)

Return the current dirty region for the component specified, or an empty Rectangle object if there is none.

public void markCompletelyDirty(JComponent aComponent)

Mark the component's entire width and height as dirty and add it to the dirty-region hashtable.

public void markCompletelyClean(JComponent aComponent)

Remove any reference to the component from the dirty-region hashtable.

public boolean isCompletelyDirty(JComponent aComponent)

Indicate whether the entire component has been placed on the dirty list. If the method returns true, the entire component is repainted on the next call to paintDirtyRegions( ).

public void validateInvalidComponents( )

Make all containers on the invalidation list redo their layouts to accommodate any changes. Must be called on the event dispatch thread; this is automatically scheduled by addInvalidComponent( ).

public void paintDirtyRegions( )

Make each of the dirty regions immediately repaint themselves. Must be called on the event dispatch thread; this is automatically scheduled by addDirtyRegion( ).

public String toString( )

Return a textual description of the state of the RepaintManager for debugging.

public Image getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight)

Return an offscreen Image that can be used as a double buffer. The size of the double buffer is typically set to the proposed width and height passed in, unless it violates a preset maximum buffer size.

public Image getVolatileOffscreenBuffer(Component c, int proposedWidth, int proposedHeight)

Return an offscreen VolatileImage that can be used as a double buffer, taking advantage of accelerated graphics hardware. Components coded to handle the special requirements of working with VolatileImage (primarily, the occasional need to recreate the image if the operating system has stolen its resources while you were trying to use it) can get a performance boost by using this method. The size of the double buffer is typically set to the proposed width and height passed in, unless it violates a preset maximum buffer size.

public void setDoubleBufferMaximumSize(java.awt.Dimension d)

Set the maximum size of the RepaintManager object's offscreen drawing buffer.

public Dimension getDoubleBufferMaximumSize( )

Retrieve the maximum size of the RepaintManager object's offscreen drawing buffer.

public void setDoubleBufferingEnabled(boolean aFlag)

Activate or deactivate the double-buffering mechanism. You should generally leave this alone, as it is initialized to the right value for the system graphics environment.

public boolean isDoubleBufferingEnabled( )

Return a boolean indicating whether the double buffering mechanism is enabled for this RepaintManager object.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net