新闻正文

Painting in AWT and Swing (From java.sun.com)

来源:JAVA天堂  JAVA学习者  2007-8-13 01:41:47 网友评论 0 条 字体:[ ] ~我要投稿!
幽幽黄桷兰 (Sun May 16 16:08:23 2004)
转信站: SJTU!netnews.sdu.edu.cn!news.uestc.edu.cn!CQUPT
Painting in AWT and Swing
Good Painting Code Is the Key to App Performance
By Amy Fowler
In a graphical system, a windowing toolkit is usually responsible for provid
ing a framework to make it relatively painless for a graphical user interfac
e (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 purp
ose is to help developers write correct and efficient GUI painting code. Whi
le 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 outp
ut. To learn how to render nice graphics, visit the Java 2D Web site.
The main topics covered in this article are:
Evolution of Paint System
Painting in the AWT
System-Triggered vs. App-Triggered Painting
The Paint Method
Painting & Lightweight Components
"Smart" Painting
AWT Painting Guidelines
Painting in Swing
Double Buffering Support
Additional Paint Properties
The Paint Methods
Paint Processing
The Repaint Manager
Swing Painting Guidelines
Summary
Evolution of the Swing Paint System
When the original AWT API was developed for JDK 1.0, only heavyweight compon
ents existed ("heavyweight" means that the component has it"s own opaque nat
ive 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 detect
ion, clip calculation, and z-ordering. With the introduction of lightweight
components in JDK 1.1 (a "lightweight" component is one that reuses the nati
ve window of its closest heavyweight ancestor), the AWT needed to implement
the paint processing for lightweight components in the shared Java code. Con
sequently, there are subtle differences in how painting works for heavyweigh
t and lightweight components.
After JDK 1.1, when the Swing toolkit was released, it introduced its own sp
in on painting components. For the most part, the Swing painting mechanism r
esembles 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 pa
int operation in a windowing environment. In AWT, there are two kinds of pai
nting operations: system-triggered painting, and application-triggered paint
ing.
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 damaged 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 nee
ds to update its contents because its internal state has changed. (For examp
le,. a button detects that a mouse button has been pressed and determines th
at it needs to paint a "depressed" button visual).
The Paint Method
Regardless of how a paint request is triggered, the AWT uses a "callback" me
chanism for painting, and this mechanism is the same for both heavyweight an
d lightweight components. This means that a program should place the compone
nt"s rendering code inside a particular overridden method, and the toolkit w
ill 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-configure
d 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) repr
esents the upper left corner of the component.
The Graphics object"s clip rectangle is set to the area of the component tha
t is in need of repainting.
Programs must use this Graphics object (or one derived from it) to render ou
tput. 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 i
n 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 exa
mple, which provides a runnable program example of how to use the paint call
back 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 su
ch code may be invoked at times when it is not appropriate to paint -- for i
nstance, before the component is visible or has access to a valid Graphics o
bject. It is not recommended that programs invoke paint() directly.
To enable app-triggered painting, the AWT provides the following java.awt.Co
mponent methods to allow programs to asynchronously request a paint operatio
n:
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 repa
int() 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 alw
ays invoke the no-arg version, which causes a repaint of the entire componen
t, 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 h
eavyweight 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 di
stinct ways, depending on whether a painting operation is system-triggered o
r 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 painte
d.
The AWT causes the event dispatching thread to invoke paint() on the compone
nt.
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 re
painted in response to some internal state change.
The program invokes repaint() on the component, which registers an asynchron
ous request to the AWT that this component needs to be repainted.
The AWT causes the event dispatching thread to invoke update() on the compon
ent.
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 reques
ts 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 of up
date() clears the component"s background (if it"s not a lightweight componen
t) and simply calls paint().
Since by default, the final result is the same (paint() is called), many peo
ple 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 a


nd calls paint(), this update "hook" enables a program to handle the app-tri
ggered painting case differently, if desired. A program must assume that a c
all 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() d
oes not imply this, which enables a program to do incremental painting.
Incremental painting is useful if a program wishes to layer additional rende
ring on top of the existing bits of that component. The UpdateDemo example d
emonstrates a program which benefits from using update() to do incremental p
ainting.
In truth, the majority of GUI components do not need to do incremental drawi
ng, so most programs can ignore the update() method and simply override pain
t() to render the component in it"s current state. This means that both syst
em-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 light
weight component framework is written entirely in common Java code, there ar
e some subtle differences in the way the mechanism is implemented for lightw
eights.
How Lightweights Get Painted
For a lightweight to exist, it needs a heavyweight somewhere up the containm
ent hierarchy in order to have a place to paint. When this heavyweight ances
tor 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.Con
tainer"s paint() method , which calls paint() on any of its visible, lightwe
ight children which intersect with the rectangle to be painted. So it"s crit
ical 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 de
scendents 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 recursive
ly 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 (show
ing, hiding, moving, resizing, etc.) for lightweight components is written e
ntirely in Java. Often, within the Java implementation of these functions, t
he AWT must explicitly tell various lightweight components to paint (essenti
ally system-triggered painting, even though it"s no longer originating from
the native system). However, the lightweight framework uses repaint() to tel
l components to paint, which we previously explained results in a call to up
date() instead of a direct call to paint(). Therefore, for lightweights, sys
tem-triggered painting can follow two paths:
The system-triggered paint request originates from the native system (i.e. t
he lightweight"s heavyweight ancestor is first shown), which results in a di
rect 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(), wh
ich by default is forwarded to paint() .
In a nutshell, this means that for lightweight components there is no real d
istinction between update() and paint(), which further implies that the incr
emental painting technique should not be used for lightweight components.
Lightweights and Transparency
Since lightweight components "borrow" the screen real estate of a heavyweigh
t ancestor, they support the feature of transparency. This works because lig
htweight components are painted from back to front and therefore if a lightw
eight component leaves some or all of its associated bits unpainted, the und
erlying component will "show through." This is also the reason that the defa
ult implementation of update() will not clear the background if the componen
t 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 effici
ent as possible, a component"s paint() implementation itself can have a sign
ificant impact on overall performance. Two key areas that can affect this pr
ocess 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 po
rtion 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, the
n 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 numer
ous components, where the component and/or its layout manager has informatio
n about the layout, then it"s worth using that layout knowledge to be smarte
r about determining which of the children must be painted. The default imple
mentation of Container.paint() simply looks through the children sequentiall
y and tests for visibility and intersection -- an operation that may be unne

cessarily inefficient with certain layouts. For example, if a container laye
d 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 th
e 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 o
f the component"s paint() method.
Programs may trigger a future call to paint() by invoking repaint(), but sho
uldn"t call paint() directly.
On components with complex output, repaint() should be invoked with argument
s which define only the rectangle that needs updating, rather than the no-ar
g version, which causes the entire component to be repainted.
Since a call to repaint() results first in a call to update(), which is forw
arded to paint() by default, heavyweight components may override update() to
do incremental drawing if desired (lightweights do not support incremental
drawing)
Extensions of java.awt.Container which override paint() should always invoke
super.paint() to ensure children are painted.
Components which render complex output should make smart use of the clip rec
tangle to narrow the drawing operations to those which intersects with the c
lip 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. Additionall
y, Swing provides built-in support for double-buffering as well as changes t
o support Swing"s additional structure (like borders and the UI delegate). A
nd finally, Swing provides the RepaintManager API for those programs who wan
t to customize the paint mechanism further.
Double Buffering Support
One of the most notable features of Swing is that it builds support for doub


le-buffering right into the toolkit. It does the by providing a "doubleBuffe
red" property on javax.swing.JComponent:
public boolean isDoubleBuffered()
public void setDoubleBuffered(boolean o)
Swing"s double buffer mechanism uses a single offscreen buffer per containme
nt 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 caus
ing all lightweight components underneath that container to be rendered into
the offscreen buffer, regardless of their individual "doubleBuffered" prope
rty values.
By default, this property is set to true for all Swing components. But the s
etting that really matters is on JRootPane, because that setting effectively
turns on double-buffering for everything underneath the top-level Swing com
ponent. 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 o
ff (and for smooth GUI rendering, you"ll want it on!). Swing ensures that th
e appropriate type of Graphics object (offscreen image Graphics for double-b
uffering, regular Graphics otherwise) is passed to the component"s paint cal
lback, so all the component needs to do is draw with it. This mechanism is e
xplained in greater detail later in this article, in the section on Paint Pr
ocessing.
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 w
ere introduced in order to deal with the following two issues, which can mak
e 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 underne


ath it must be repainted first. This requires the system to walk up the cont
ainment hierarchy to find the first underlying heavyweight ancestor from whi
ch 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 whic
h overlap the original component (where the clip rectangle intersects with t
he overlapping area) the overlapping component must also be partially repain
ted. 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 rec
tangular 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 ea
ch standard Swing component is set by the current look and feel UI object. T
he 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 ren
der the area defined by their bounds, the result is occasional screen garbag
e in the unrendered areas. When a component is designed, careful thought sho
uld be given to its handling of the opaque property, both to ensure that tra
nsparency is used wisely, since it costs more at paint time, and that the co
ntract with the paint system is honored.
The meaning of the opaque property is often misunderstood. Sometimes it is t
aken to mean, "Make the component"s background transparent." However, this i
s not Swing"s strict interpretation of opacity. Some components, such as a p
ushbutton, may set the opaque property to false in order to give the compone
nt a non-rectangular shape, or to leave room around the component for transi
ent 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 t
ransparency is applied to a component"s visuals, then this use of the proper
ty 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 ContentAreaFil
led property for this purpose.)
Another issue worth noting is how opacity relates to a Swing component"s bor


der property. The area rendered by a Border object set on a component is sti
ll 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 b
y the border. (The border then just layers its rendering on top of the opaqu
e component).
If you want a component to allow the underlying component to show through it
s border area -- that is, if the border supports transparency via isBorderOp
aque() returning false -- then the component must define itself to be non-op
aque 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 pro
perty 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 c
omponents can change the default value is to subclass and override this meth
od to return the desired value. All standard Swing components return true fo
r this property, except for JLayeredPane, JDesktopPane, and JViewPort.
The Paint Methods
The rules that apply to AWT"s lightweight components also apply to Swing com
ponents -- for instance, paint() gets called when it"s time to render -- exc
ept 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 pain
tBorder() or paintComponents() (and if you do, make sure you know what you"r
e 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 pai
ntComponent() callback.
Painting and the UI Delegate
Most of the standard Swing components have their look and feel implemented b
y separate look-and-feel objects (called "UI delegates") for Swing"s Pluggab
le look and feel feature. This means that most or all of the painting for th

e standard components is delegated to the UI delegate and this occurs in the
following way:
paint() invokes paintComponent() .
If the ui property is non-null, paintComponent() invokes ui.update().
If the component"s opaque property is true, ui.udpate() fills the component"
s background with the background color and invokes ui.paint().
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() with
in 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 component extension does not want to allow the UI del
egate to paint (if, for example, it is completely replacing the component"s


visuals), it may skip calling super.paintComponent(), but it must be respons
ible for filling in its own background if the opaque property is true, as di
scussed 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 AP
I (discussed later), as well as to improve paint performance. In Swing, pain
ting 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 calls paint() on
any lightweight descendents
When the first Swing component is reached, the default implementation of JCo
mponent.paint() does the following:
if the component"s doubleBuffered property is true and double-buffering is e


nabled on the component"s RepaintManager, will convert the Graphics object t
o 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 the opaque and optimizedDrawingEnabled properties t
o determine exactly which descendents to recursively invoke paint() on.
if the component"s doubleBuffered property is true and double-buffering is e
nabled 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 ca
lls to paint() (from paintChildren(), described in step#4) because all the l
ightweight components within a Swing window hierarchy will share the same of
fscreen image for double-buffering.
(B) The paint request originates from a call to repaint() on an extension of
javax.swing.JComponent:
JComponent.repaint() registers an asynchronous repaint request to the compon
ent"s RepaintManager, which uses invokeLater() to queue a Runnable to later
process the request on the event dispatching thread.
The runnable executes on the event dispatching thread and causes the compone
nt"s RepaintManager to invoke paintImmediately() on the component, which doe
s the following:
uses the clip rectangle and the opaque and optimizedDrawingEnabled propertie
s to determine the "root" component from which the paint operation must begi
n (to deal with transparency and potentially overlapping components).
if the root component"s doubleBuffered property is true, and double-bufferin
g is enabled on the root"s RepaintManager, will convert the Graphics object
to an appropriate offscreen graphics.
invokes paint() on the root component (which executes (A)"s JComponent.paint
() steps #2-4 above), causing everything under the root which intersects wit
h the clip rectangle to be painted.


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.
走过的都是沙漠,但不等于前面没有绿洲,关键是看你有没有勇气去坚持.



收藏到ViVi   收藏此页到365Key
上一篇: 求救,刷新的问题?
下一篇: 怎么将ToolTip分多行显示?
用户名:新注册) 密码: 匿名评论 [所有评论]
评论内容:不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
本栏搜索
  • Google
   网站首页 -  网站地图 -  技术学习 -  网站投稿 -  帮助中心
Copyright 2003-2008 www.javah.net All Rights Reserved
2008 如果你喜欢本站 请收藏本站 并推荐给你的朋友一起分享