以下是素人不負責任翻譯
在AWT和Swing中畫畫 |
在AWT和Swing中畫畫
好的畫圖程式碼可以提高程式的效率
By Amy Fowler(翻譯 cgk)
在圖形系統中,視窗工具通常提供了一個環境讓圖形使用者能較輕鬆下給於正確即時的資訊給使用者.
AWT(抽象視窗工具)和Swing兩者提供這樣的環境.但是其APIs(應用程式介面)在實作上是不易被一些開發者所理解的.其中之一的問題即是不易被開發者所使用.這篇文章將解釋AWT和Swing畫圖機制的細節.希望能達到幫助開發者寫出正確且有效率的GUI(使用者圖形化介面)程式碼為目的.本文章內容包涵著畫圖的機制(何時和何處提供),但並沒有說明如何使用Swing的畫圖APIs畫出正確的成果.想學習如何畫出好的圖形,請拜訪Java 2D Web site..
本文主要的主題如下:
圖畫系統的進化
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.
當時JDK1.0所發展的AWT APIs中,只有重量級 的元件存在(重量級指的是其本身無原生視窗)
這使得AWT在畫圖上極度依賴自身的原生平台.因此JDK1.1有了輕量級(輕量級元件是可模擬各原生平台以達到重複使用,)AWT在畫圖過程中需要實作在共享的Java程式碼內輕量元件.所以,在使用輕量和重量級作畫時存在著微妙的差別.在JDK1.1之後,當Swing工具釋出,其擁有自身的繪圖元件.最重要的是,Swing繪圖機制類似和相依於AWT.但也有一些部份不同於AWT,而使得新的API讓程式能更自由簡單的進行繪圖的作業.
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 injava.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'sforeground
property. - The
Graphics
object's font is set to the component'sfont
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.
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:
|
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:
|
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:- The AWT determines that either part or all of a component needs to be painted.
- 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:- The program determines that either part or all of a component needs to be repainted in response to some internal state change.
- The program invokes
repaint()
on the component, which registers an asynchronous request to the AWT that this component needs to be repainted.
- The AWT causes the event dispatching thread to invoke
update()
on the component. NOTE: If multiple calls torepaint()
occur on a component before the initial repaint request is processed, the multiple requests may be collapsed into a single call toupdate()
. 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.
- If the component did not override
update()
, the default implementation ofupdate()
clears the component's background (if it's not a lightweight component) and simply callspaint()
.
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 overridepaint()
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 byjava.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:
|
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 usesrepaint()
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 topaint()
.
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 ofupdate()
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'spaint()
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).
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:- For most programs, all client paint code should be placed within the scope of the component's
paint()
method.
- Programs may trigger a future call to
paint()
by invokingrepaint()
, but shouldn't callpaint()
directly.
- 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.
- Since a call to
repaint()
results first in a call toupdate()
, which is forwarded topaint()
by default, heavyweight components may overrideupdate()
to do incremental drawing if desired (lightweights do not support incremental drawing)
- Extensions of
java.awt.Container
which overridepaint()
should always invokesuper.paint()
to ensure children are painted.
- 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 ofrepaint()
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 onjavax.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 onJComponent
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-writeopaque
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.
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-onlyisOptimizedDrawingEnabled
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
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:paint()
invokespaintComponent()
.- If the
ui
property is non-null,paintComponent()
invokesui.update().
- If the component's
opaque
property is true,ui.udpate()
fills the component's background with the background color and invokesui.paint()
. ui.paint()
renders the content of the component.
JComponent
), should invoke super.paintComponent()
within their paintComponent
override:
|
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
):- the event dispatching thread invokes
paint()
on that ancestor
- The default implementation of
Container.paint()
recursively callspaint()
on any lightweight descendents
- When the first Swing component is reached, the default implementation of
JComponent.paint()
does the following:- if the component's
doubleBuffered
property istrue
and double-buffering is enabled on the component'sRepaintManager
, will convert theGraphics
object to an appropriate offscreen graphics. - invokes
paintComponent()
(passing in offscreen graphics if doubled-buffered) - invokes
paintBorder()
(passing in offscreen graphics if doubled-buffered) - invokes
paintChildren()
(passing in offscreen graphics if doubled-buffered), which uses the clip and theopaque
andoptimizedDrawingEnabled
properties to determine exactly which descendents to recursively invokepaint()
on. - if the component's
doubleBuffered
property istrue
and double-buffering is enabled on the component'sRepaintManager
, copies the offscreen image to the component using the original on-screenGraphics
object.
JComponent.paint()
steps #1 and #5 are skipped in the recursive calls topaint()
(frompaintChildren()
, described in step#4) because all the lightweight components within a Swing window hierarchy will share the same offscreen image for double-buffering.
- if the component's
repaint()
on an extension of javax.swing.JComponent
:JComponent.repaint()
registers an asynchronous repaint request to the component'sRepaintManager
, which usesinvokeLater()
to queue aRunnable
to later process the request on the event dispatching thread.
- The runnable executes on the event dispatching thread and causes the component's
RepaintManager
to invokepaintImmediately()
on the component, which does the following:
- uses the clip rectangle and the
opaque
andoptimizedDrawingEnabled
properties to determine the 'root' component from which the paint operation must begin (to deal with transparency and potentially overlapping components). - if the root component's
doubleBuffered
property istrue
, and double-buffering is enabled on the root'sRepaintManager
, will convert theGraphics
object to an appropriate offscreen graphics. - invokes
paint()
on the root component (which executes (A)'sJComponent.paint()
steps #2-4 above), causing everything under the root which intersects with the clip rectangle to be painted. - if the root component's
doubleBuffered
property istrue
and double-buffering is enabled on the root'sRepaintManager
, copies the offscreen image to the component using the original on-screenGraphics
object.
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 topaintImmediately()
on the topmost Swing component on whichrepaint()
was invoked. For example, if aJTabbedPane
contains aJTable
and both issue calls torepaint()
before any pending repaint requests on that hierarchy are processed, the result will be a single call topaintImmediately()
on theJTabbedPane
, which will causepaint()
to be executed on both components.
- uses the clip rectangle and the
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'sRepaintManager
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
TheRepaintManager
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 theRepaintManager
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
TheRepaintManager
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:- 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.
- Programs may trigger a future call to
paint()
by invokingrepaint()
, but shouldn't callpaint()
directly.
- 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.
- Swing's implementation of
paint()
factors the call into 3 separate callbacks:paintComponent()
paintBorder()
paintChildren()
paintComponent()
method ( not withinpaint()
).
- 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?
- If a Swing component's
opaque
property is set totrue
, then it is agreeing to paint all of the bits contained within its bounds (this includes clearing it's own background withinpaintComponent()
), otherwise screen garbage may result. -
Setting either the
opaque
oroptimizedDrawingEnabled
properties tofalse
on a component will cause more processing on each paint operation, therefore we recommend judicious use of both transparency and overlapping components.
-
Extensions of Swing components which have UI delegates (including
JPanel
), should typically invokesuper.paintComponent()
within their ownpaintComponent()
implementation. Since the UI delegate will take responsibility for clearing the background on opaque components, this will take care of #5.
-
Swing supports built-in double-buffering via the
JComponent
doubleBuffered
property, and it defaults totrue
for all Swing components, however setting it totrue
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.
-
It is strongly recommended that double-buffering be enabled for all Swing components.
-
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.