|
This is an excerpt from Graphic Java 1.2: Mastering the JFC Volume 1 - AWT by David M. Geary, published August 1998. The first of three volumes, written for experienced programmers looking for thorough and detailed explanations of the 1.2 AWT class libraries, Volume 1 covers all aspects of the AWT. The book also covers advanced topics such as clipboard and data transfer, double buffering, custom dialogs, and sprite animation. Together with the forthcoming Volume 2 - Swing (Fall 1998) and Volume 3 - 2D API (Winter 1998), Graphic Java 1.2 provides Java developers with the tools needed to build professional, cross-platform applications that take full advantage of the Java Foundation Classes. Graphics
The AWT provides user interface components such as buttons, lists, menus,
dialogs and so on, but it does not include anything analogous to purely
graphical objects. For instance, the AWT does not provide
The AWT does provide
Instead of providing purely graphical objects, the AWT employs
a simpler--albeit less flexible and extensible--model. Every AWT
component comes complete with its own
java.awt.Graphics
Nearly all applets (and applications) that use the AWT manipulate a
In addition to performing graphical operations within a component, each
In order to provide an overall picture of the role that the
Nearly all of the methods that are passed a reference to a
See JDK Methods that Return a Graphics Reference lists methods that
return a
Images and print jobs also provide a
Graphics Parameters
The
The main focus of attention for the
The
Each
The font used to draw text may be specified and read by the following methods:
Font metrics are represented by the
The no-argument version of the method returns the font metrics associated
with the
Each
The clipping rectangle can be specified either by four integers representing
the bounding box of the rectangle, or by a
The last method listed above computes a new clipping rectangle which is
the intersection of the previous clipping rectangle with the rectangle
specified by the method's arguments. In early versions of the AWT,
Finally, graphics mode, which is discussed in detail in See Graphics Modes, determines how text and shapes are drawn over existing graphics. Two methods are provided to set the mode:
The Graphics Coordinate SystemThe graphics coordinate system is anchored in the upper left-hand corner of a component, with coordinates increasing down and to the right, as depicted in See Graphics Coordinate System.
Coordinates lie between pixels of the output device. Operations that draw
outlines of shapes, such as Drawing ShapesSee RectTest Applet lists an applet that invokes
|
import java.applet.Applet;
import java.awt.*;
public class RectTest extends Applet {
public void paint(Graphics g) {
g.drawRect(2,2,4,4);
}
}
|
Although the applet has a simple implementation, it illustrates an important point.
At first glance, it may appear that the arguments to
drawRect(2,2,4,4) define the bounds of the rectangle to be
drawn in pixel coordinates. In reality, as shown in See Drawing Shape
Outlines, the arguments specify a coordinate path that the
graphics' pen will traverse when drawing the rectangle. The coordinate
path starts at (2,2) and is 4 coordinates wide and 4 coordinates high:
(2,2) --> (6,2) --> (6,6) --> (2,6) --> (2,2)
The graphics pen traverses the path, hanging down and to the right of the
path. As the pen traverses the path, it colors pixels that it comes in
contact with. The color used by the pen can be specified by invoking
Graphics.setColor(Color).
Drawing a rectangle by invoking Graphics.drawRect() results
in an extra row of pixels on the right and bottom sides of the rectangle.
This is due to the fact that the arguments passed to
Graphics.drawRect() define the coordinate path
that the pen will follow, and not the size of the rectangle
itself. Since the pen hangs beneath and to the right of the coordinates
along the path, the statement g.drawRect(2,2,4,4) actually draws
a rectangle whose width and height are 5 pixels--not 4 pixels as you might
expect.
AWT Tip
Coordinates Lie Between Pixels
Graphics coordinates lie between pixels, not on them. Graphics methods that specify coordinates specify a path that the graphics' pen--always a pixel-sized square--will traverse. The pen paints pixels below and to the right of the coordinates on the path. As a result, Graphics methods that paint outlines of shapes draw an extra row of pixels on the right and bottom sides of the shape's outline. For example, the statement
g.drawRect(x,y,10,10)paints a rectangle 11 pixels wide and 11 pixels high. The arguments to Graphics.drawRect() specifies the coordinate path--not the dimensions of the rectangle.
When drawing a border around a component for the first time, newcomers to
the AWT often override the component's paint method like
so:4
public void paint(Graphics g) {
Dimension size = getSize();
g.drawRect(0, 0, size.width, size.height);
}
However, as we know, the rectangle will be one pixel wider and one pixel taller than the size of the component. As a result, the right and bottom edges of the border will be drawn outside of the component, and therefore will not be visible.
The solution, of course, is to subtract one pixel from both the width and the height:
Dimension size = getSize(); g.drawRect(0, 0, size.width-1, size.height-1);
See FillTest Applet lists another simple applet, which is identical to the one listed in See RectTest Applet, except that the rectangle is filled.
FillTest Applet
import java.applet.Applet;
import java.awt.*;
public class FillTest extends Applet {
public void paint(Graphics g) {
g.fillRect(2,2,4,4);
}
}
|
The arguments passed to fillRect() specify the same coordinate
path as the previous call to drawRect(). However,
Graphics methods that fill shapes fill the interior of the
path, and therefore the filled rectangle is 4 pixels wide and 4 pixels
high, as depicted in See Filling Shapes.
The fact that shape fills are smaller than shape outlines can be
confusing. Additionally, the arguments passed to drawRect()
and fillRect() define a coordinate path, but are often
mistaken for representing the bounds of the rectangle in pixels. All
Graphics methods that draw outlines and fills exhibit a
discrepancy in size. The effect for arcs can be seen in See Drawing and
Filling Arcs, which draws an outline and fill of an arc.
There are two ways to obtain a reference to a component's Graphics:
override one of the methods listed in See JDK Methods that are passed a
Graphics Reference that are passed a Graphics reference, or
invoke one of the methods listed in See JDK Methods that Return a Graphics
Reference, all of which return a Graphics reference.
It is worth mentioning that the Graphics reference returned
from getGraphics() in Component, Image
and PrintJob does not, as the name might seem to imply, return
a reference to a single Graphics. Instead, the methods return
a brand new Graphics that is a copy of the original. As we'll
see in the next section, this has some important consequences.
It is important to emphasize that Graphics references refer
to copies of the actual Graphics associated with a
component. Consider the applet listed in See CopyTest Applet--an applet
that merely draws a line from its upper left-hand corner to the lower
right-hand corner.
CopyTest Applet
import java.applet.Applet;
import java.awt.*;
public class CopyTest extends Applet {
public void paint(Graphics g) {
setForeground(Color.yellow);
g.drawLine(0,
0,
getSize().width-1,
getSize().height-1);
}
}
|
The applet sets its foreground color to yellow, and then draws a line from the upper left-hand corner to the lower-right hand corner of the applet. However, this applet may not perform as you expect--the line will not be yellow the first time it is drawn.
The applet is shown in See Graphics References Passed to paint().
Refer to Copies.
The picture on the left shows the applet in its initial state; the line is
drawn in the default foreground color for the applet, which, for Windows95,
is black. The picture on the right shows the applet after it has been resized,
and therefore repainted. After the initial painting of the applet, subsequent
calls to paint() result in a yellow line. Here's why:
The call to Component.setForeground() changes the current color
of the component's Graphics --in this case to yellow.
setForeground() affects the applet's Graphics, but
not the copy of the Graphics that was passed to
paint(). Therefore, when paint() is invoked for
the first time the line will not be yellow; the Graphics passed
to paint() is out of synch with the actual Graphics
when the call to drawLine() is made.
Subsequent calls to paint() are passed new copies of the applet's
Graphics, and by then the call to setForeground()
has resulted in the current color of the applet's Graphics being
set to yellow.
If the applet is modified as listed in See CopyTest2 Applet, the line will initially be drawn in yellow.
CopyTest2
Applet
import java.applet.Applet;
import java.awt.*;
public class CopyTest2 extends Applet {
public void paint(Graphics g) {
setForeground(Color.yellow);
// the next line would do just as
// well as the following
// g.setColor(Color.yellow);
Graphics copy = getGraphics();
try {
System.out.println("g=" + g.getColor() +
" copy=" + copy.getColor());
copy.drawLine(0,0,
getSize().width-1, getSize().height-1);
}
finally {
copy.dispose();
}
}
}
|
The Graphics passed to paint() is ignored. Instead,
getGraphics() is called to obtain a new Graphics
reference which is used to draw the line. Since the Graphics
is obtained after the call to setForeground(), the current color
of the Graphics, and therefore the color of the line, will be
yellow.
The first time paint() is called, it prints the following
output:5
g=java.awt.Color[r=0,g=0,b=0]
copy=java.awt.Color[r=255,g=255,b=0]
Notice that the Graphics returned by
Component.getGraphics() is disposed of before the method returns
by invoking Graphics.dispose(), while the Graphics
passed to paint() is not. See See Disposing of a Graphics for
more on when it is sometimes necessary to dispose of a Graphics.
In addition to referring to a copy of the real thing, references to
Graphics that are passed to methods such as paint()
and update() are only valid during the execution of the method
they are passed to. Once the method returns, the reference is no longer
valid.
Consider the foolish applet listed in See HoldRef Applet, which tries to reuse
the Graphics reference initially passed to paint().
The line will be drawn the first time paint() is invoked, but
subsequent calls to paint() will result in the line being drawn
into an invalid Graphics, and the line no longer shows up (the
applet can be forced to repaint by resizing the window).
HoldRef Applet
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class HoldRef extends Applet {
private Graphics oldg;
private boolean first = true;
public void paint(Graphics g) {
if(first) {
oldg = g;
first = false;
}
oldg.drawLine(
0,
0,
getSize().width-1,
getSize().height-1);
}
}
|
Graphics references passed to methods have short life-spans
because they are a finite resource that must be disposed of. Each
Graphics represents a graphics context supplied by the native
windowing system. Such graphics contexts are usually available in a finite
quantity, and callers that pass references to a Graphics are
careful to dispose of it when the call returns. For example, when a call to
Component.paint() returns, the caller disposes of the
Graphics that was passed to paint().
Although Java has garbage collection, there are a couple of places in the
AWT where it is up to the developer to dispose of system resources, and
disposing of Graphics is one of
them. 6 There are two
issues related to disposing of a Graphics: When it needs to be
done, and how it is done.
Graphics
Here's a simple rule for when you should dispose of a Graphics:
If you obtain a reference to a Graphics by invoking one of the
getGraphics methods, listed in See JDK Methods that Return a
Graphics Reference, or by creating a Graphics via
Graphics.create(), then you are responsible for disposing of it.
If you override a method which is passed a Graphics reference,
such as Component.paint() or Component.update(),
then you are off the hook-- it is the caller's responsibility to dispose of
the reference.
Disposing of a Graphics is accomplished by invoking
Graphics.dispose(), as the code fragment below demonstrates.
public void someMethodInAComponent() {
// code fragment
Graphics g = getGraphics();
if(g != null) {
try {
// do something with g - if an exception is
// thrown, the finally block will be executed
}
finally {
g.dispose() // crucial
}
}
}
|
The call to dispose() is crucial because neglecting to do so
can cause the windowing system to run out of graphics contexts, which on most
operating systems is not a pretty sight.
Also note that it is not merely paranoia that causes the check for
g against null. getGraphics() can
indeed return null, if it is invoked before the component's
peer has been created.
The call to Graphics.dispose() is placed in a finally
block; manipulation of the Graphics is performed within
a corresponding try block. This ensures that the call to
dispose() will be made in the event that an exception is
thrown from within the try block.
AWT Tip
Graphics References Passed to Methods Refer to Copies
Graphics represent native graphics contexts, which are typically a finite resource. Therefore, Graphics references returned by a method must be disposed of by calling Graphics.dispose().
Graphics references passed to methods such as Component.paint(), do not need to be manually disposed of by the method--the caller of the method is responsible for disposing of the Graphics when the calls return.
The Graphics class provides methods for rendering the following:
Rectangles, arcs, ovals and polygons can also be filled. In this section, we will explore all but the last item listed above.
Lines are drawn by invoking
Graphics.drawLine(int x, int y, int x2, int y2). The AWT is
not capable of drawing lines of variable thickness; the graphics's pen,
and therefore the lines it draws, are always one pixel thick. In addition,
lines are always drawn solid--there is no provision for patterned lines
such as dotted or dashed. However, the Java 2D API provides extensive
support for various linestyles and pen sizes.
The applet shown in See Drawing Lines draws lines at random locations. The lines are also fitted with random lengths, directions, and colors.
The applet shown in See Drawing Lines is listed in See PickupSticks Applet.
Activation of the "scatter" button causes the applet to repaint.
Math.random() is used to randomize the line's parameters.
random() returns a
psuedo-random7 number
between 0.0 and 1.0. That number is multiplied by some factor of ten to
give reasonable bounds to the line's location and size.
PickupSticks Appletimport java.applet.Applet; |
The arguments to Graphics.drawLine(int x, int y, int x2, int y2)
represent the endpoints of the line. Notice that the line, like all other
shapes drawn by a Graphics, is drawn in the Graphics'
current color. The applet listed above uses a random number between 1 and 10
to select an index into the colors array in order to randomize
the color of the line.
A polyline is a series of connected line segments, which are drawn by
drawPolyline(int[] xPoints, int[] yPoints, int numPoints). The
method is passed two arrays, one specifying the x coordinate of each point,
and the other representing the y coordinate. Additionally, the method takes
an integer value signifying the number of points to be
drawn.8
The figure drawn by drawPolyline() is not closed if the first
and last points differ.
The applet shown in See Drawing Polylines randomly generates polylines. The applet contains a repaint button which causes the applet to repaint when activated. The number of points and their locations, in addition to the color of the polyline varies in a psuedo-random fashion.
The applet shown in See Drawing Polylines is listed in See Polylines Applet.
Polylines Appletimport java.applet.Applet; |
In contrast to lines, the Graphics class provides a wealth of
support for rectangles; three types of rectangles are supported:
The Graphics methods for painting and filling rectangles are listed below:
void clearRect(int x, int y, int w, int h) void drawRect(int x, int y, int w, int h) void drawRoundRect(int x, int y, int w, int h, int
arcWidth, in arcHeight) void draw3DRect(int x, int y, int w, int h, boolean raise) void fillRoundRect(int x, int y, int w, int h, int arcWidth,
int arcHeight) void fillRect(int x, int y, int w, int h) void fill3DRect(int x, int y, int w, int h, boolean
raise)
It is important to remember that the x,y,w and h
arguments passed to each of the functions listed above defines a coordinate
path--which is not necessarily the size of the rectangle. As a result,
Graphics methods that draw outlines of shapes draw an extra
row of pixels on the right and bottom sides, making them w
+1 pixels wide and h +1 pixels high. Fill methods, on the
other hand, fill the interior of the path making them w pixels
wide and h pixels high. See Drawing Shapes.
The methods for drawing and filling 3D rectangles take an argument in
addition to x,y,w and h. The boolean
argument specifies whether the 3D effect is raised or inset; true
equates to raised, and false equates to inset.
The methods for drawing and filling round rectangles take two additional
arguments: arcWidth and arcHeight, both integer
values. arcWidth specifies the arc's horizontal diameter in
pixels, and arcHeight specifies the vertical diameter of the
arc. See Rounded Rectangles illustrates the horizontal and vertical diameters
of the arcs.
The applet shown in See Drawing Rectangles paints rectangles with random
parameters for color, size and location. All three types of rectangles
supported by the Graphics class are drawn.
The applet comes equipped with three buttons and a checkbox. Each button represents the type of rectangle to be drawn; activating a button results in a repaint using the type of rectangle the button represents.
A checkbox is also included for specifying whether or not the rectangles are filled.
The applet is rather lengthy, but a good percentage of it is concerned
with constructing the buttons and checkbox and their corresponding event
handling. Our concern, namely drawing rectangles, is encapsulated in the
applet's paint method.
public class RandomRectangles extends Applet {
...
private boolean fill = false, raise = false,
round = false, threeD = false;
...
public void paint(Graphics g) {
for(int i=0; i < numRects; i++) {
Point lhc = randomPoint(); // left hand corner
Dimension size = randomDimension();
g.setColor(colors[(int)(Math.random()*10)]);
if(round) {
if(fill)
g.fillRoundRect(
lhc.x,lhc.y,size.width,size.height,
(int)(Math.random()*250),
(int)(Math.random()*250));
else
g.drawRoundRect(
lhc.x,lhc.y,size.width,size.height,
(int)(Math.random()*250),
(int)(Math.random()*250));
}
else if(threeD) {
g.setColor(Color.lightGray);
if(fill)
g.fill3DRect(
lhc.x,lhc.y,size.width,size.height,raise);
else
g.draw3DRect(
lhc.x,lhc.y,size.width,size.height,raise);
}
else {
if(fill)
g.fillRect(
lhc.x,lhc.y,size.width,size.height);
else
g.drawRect(
lhc.x,lhc.y,size.width,size.height);
}
raise = raise ? false : true;
}
}
...
}
|
Boolean class members are used to keep track of which button
was last activated, and whether or not the fill option is currently checked.
All of the rectangles drawn by the applet are drawn with a one pixel square pen, because that is the only size the AWT provides.
When 3D rectangles are drawn, the Graphics' color is set to
light gray before drawing the rectangle, because draw3DRect()
and fill3DRect() draw rectangles that only look three dimensional
if they are drawn in light gray.
The applet is listed in its entirety in See RandomRectangles Applet.
RandomRectangles Appletimport java.applet.Applet; |
java.awt.Graphics provides the following methods for drawing and
filling arcs:
void drawArc(int x, int y, int w, int h, int startAngle,
int endAngle) void fillArc(int x, int y, int w, int h, int startAngle,
int endAngle) Both methods are passed the same list of arguments. The first four arguments specify a coordinate path for a bounding box into which the arc will be drawn (or filled). The last two arguments specify the start and end angles of the arc in degrees. For example, the applet shown in See Drawing Arcs draws an arc with a start angle of 0 degrees and an end angle of 270 degrees whose width and height are specified as 151 and 101 pixels, respectively. The applet is listed in See DrawArc Applet.
DrawArc
Appletimport java.applet.Applet; |
The first four arguments passed to Graphics.drawArc() specify
a coordinate path for the bounding rectangle--not the size of the rectangle
itself. Since the graphic's pen hangs down and to the right of the coordinate
path, the actual rectangle will always be one pixel wider and one pixel taller
than the width and height arguments for the coordinate path--see See Drawing
Shapes.
Graphics.fillArc() fills the interior of the coordinate path
specified by the arguments it is passed. The applet listed in See DrawArc
Applet is modified to fill the arc in addition to drawing it, as shown in
See Drawing and Filling Arcs.
Notice that the black outline of the arc is visible on the right and bottom
sides of the arc, even though the calls to drawArc() and
fillArc() specify the same coordinate path. This is due to
the fact that Graphics methods that draw shape outlines draw
an extra row of pixels on the left and bottom sides of the shape.
Arcs are the only unclosed shape that can be filled. Filled arcs are closed by drawing lines that emanate from the center of the arc to its endpoints. The applet shown See Drawing and Filling Arcs is listed in See DrawAndFillArc Applet.
DrawAndFillArc Appletimport java.applet.Applet; |
The Graphics class provides methods for drawing and filling
elliptical shapes:
void drawOval(int x, int y, int w, int h) void fillOval(int x, int y, int w, int h) The arguments passed to both methods specify a coordinate path for a bounding rectangle for the ellipse. If the width and height of the coordinate path are equal, then a circle is drawn. The oval is centered at the center of the bounding box, and just fits inside it.
As with other Graphics methods that draw shapes,
drawOval() draws an ellipse that fits into a rectangle which is
w+1 pixels wide and h+1 pixels high.
Polygons may be drawn and filled by the following Graphics
methods:
void drawPolygon(int[] xPoints, int[] yPoints,
int numPoints) void drawPolygon(Polygon polygon)void drawPolygon(int[] xPoints, int[] yPoints,
int numPoints) void drawPolygon(Polygon polygon)
A polygon may be drawn or filled by specifying either a Polygon
object, or arrays of x and y values specifying the points of the polygon.
Polygons are automatically closed if the first and last points are not the
same.
It is interesting to note that although the AWT provides non-graphical
Polygon and Rectangle classes, there is no
drawRect(Rectangle) method provided by the Graphics
class, although there is a drawPolygon(Polygon).
The Graphics class provides three methods for rendering text:
void drawString(String s, int x, int y) void drawChars(char[], int offset, int length, int x, int y) void drawBytes(byte[], int offset, int length, int x, int y) The text may be specified as a string, an array of characters, or an array of bytes, depending upon which method is invoked.
All three methods for rendering text are passed an x,y location at which to draw the text. The location corresponds to the baseline of the text, and not the upper left-hand corner of the text, as is the case for rectangles, as depicted in See Drawing Strings With the drawString() Method.
The offset and length arguments passed to drawChars() and
drawBytes() specifies an offset into the array at which to start
drawing, and the number of characters to draw, respectively.
The Graphics class does not provide the capability to rotate
text.
AWT Tip
Locations for Strings and Shapes Differ
The location specified for drawing a string specifies the baseline of the text, whereas the location specified for drawing a shape refers to the upper left-hand corner of the shape's boundary. If a string and rectangle are drawn with the same location specified for each, the string will appear above the rectangle.
Unless specified otherwise, the origin of a Graphics' coordinate
system coincides with the upper left-hand corner of its associated component,
image or print job, as illustrated in See Graphics Coordinate System.
Graphics.translate() can be used to translate the origin to a new
location. The translate method is passed two integer
values representing a point in the original coordinate system that will become
the origin of the translated coordinate system.
Translating a coordinate system's origin is done for numerous reasons; one reason is to scroll the contents of a container without scrollbars, as our next applet illustrates.
The applet shown in See Translating a Coordinate System's Origin displays an image that can be scrolled by dragging the mouse within the applet.
The scrolling is accomplished by translating the origin of the
Graphics' coordinate system whenever a mouse drag event is
detected, and repainting the image.
The top left picture in See Translating a Coordinate System's Origin shows the applet in its initial state. The top right picture shows the applet after the origin has been translated and the image has been redrawn. The bottom picture illustrates that the origin may be translated to negative x and y values.
The applet's init method loads the image, and its
paint method draws the image at (0,0).
public void init() {
image = getImage(getCodeBase(), "saint.gif");
try {
MediaTracker mt = new MediaTracker(this);
mt.addImage(image, 0);
mt.waitForID(0);
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
|
init() employs an instance of MediaTracker to ensure
that the image is fully loaded before it is displayed, but loading images is
not the focus of our discussion here.
The applet defines inner class versions of mouse and mouse motion listeners. When the mouse is pressed in the applet, the location of the mouse press is saved.
public class TranslateTest extends Applet {
Image image;
Point pressed = new Point(), lastTranslate =
new Point();...
public void init() {
...
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
Point loc = e.getPoint();
// adjust mouse press location for
// translation ...
pressed.x = loc.x - lastTranslate.x;
pressed.y = loc.y - lastTranslate.y;
}
});
...
}...
}
|
Again, the focus of our discussion here concerns translating the origin of
a coordinate system and not event handling or inner classes. The gist of the
event handling code is that when a mouse press occurs in the applet,
mousePressed() listed above, is invoked.
The location of the mouse press is adjusted for the last translation point.
The lastTranslate point is initially (0,0),
9 so the first time
the mouse is pressed, the pressed location is the same as the mouse press
coordinates. lastTranslate is updated every time the coordinate
system is translated as the result of a mouse dragged event.
Its important to realize that mouse press point is adjusted for the last
translation because the translations only apply to the copy
of the component's Graphics obtained from the call to
getGraphics(). The actual Graphics associated
with the applet is never translated; only the copies of the actual
Graphics get translated.
The call to Graphics.translate() takes place in
mouseDragged() :
public class TranslateTest extends Applet {...
public void init() {
...
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
Point loc = e.getPoint();
Point translate = new Point(loc.x - pressed.x,
loc.y - pressed.y);
Graphics g = getGraphics();
try {
g.clearRect(0,0,
getSize().width,getSize().height);
g.translate(translate.x, translate.y);
showStatus("Translating Graphics: " +
translate);
g.drawImage(image, 0, 0, TranslateTest.this);
}
finally {
g.dispose();
}
lastTranslate = translate;
}
});
}...
}
|
As the mouse is dragged, a translate point is calculated
by subtracting the coordinates of the original mouse press event from
the coordinates of the mouse dragged event, as shown in See Translation
Coordinates.
The copy of the Graphics obtained from getGraphics()
has its origin translated to the calculated value.
There are a couple of points regarding the previous example worth reiterating
concerning Graphics.
The first is that a call is made to Graphics.drawImage() to
redraw the image after the translation. Since the applet's paint
method also draws the image at (0,0), it might be tempting to simply call
repaint() after translating the origin, and let
paint() take care of rendering the image. However, remember
that the Graphics object obtained in mouseDragged(),
like the Graphics passed to paint(), are copies
of the actual graphics context associated with the component. Invoking
g.translate(translate.x, translate.y) translates the origin of
the copy of the Graphics returned from getGraphics()
but not the copy that would be passed to
paint().10
Second, notice that a call is made to dispose of the Graphics
obtained from the call to Component.getGraphics(). It is not
necessary to dispose of the Graphics passed to
paint().
Finally, if you run the applet from the CD in the back of the book, you will notice that the image flickers as it is being dragged. This is due to the fact that the erasing and rendering of the image is done in the onscreen graphics. The applet could easily be double buffered to eliminate the flicker.
The translateTest applet is listed in its entirety in See
TranslateTest Applet.
Transla int h)
The first, as we have seen, creates an exact duplicate of the
Graphics on whose behalf the method is invoked.
The second method also creates a duplicate; however, the arguments
specify a translation (x,y) and a new clipping rectangle
(x,y,w,h). The origin of the Graphics
returned from create(int,int,int,int) is translated
to the (x,y) coordinates, whereas the clipping rectangle
winds up being the intersection of the original clipping rectangle
with the specified rectangle.
The applet shown in See Creating a Graphics displays an image twice--once
using the Graphics passed to paint(), and a second
time using a Graphics copy that has had it's origin translated
and clipping rectangle modified by the call to create().
The applet is listed in its entirety in See CreateTest Applet.
CreateTest Appletimport java.applet.Applet; |
An image is loaded and the applet's paint method creates a
copy of the Graphics it is passed. The copy has its origin
translated to (imw,0), where imw represents the
width of the image.
The copy also has its clipping rectangle set to the intersection of the
original Graphics' clipping rectangle, and the rectangle
specified by (image.getWidth(this),0,100,100). Since the
original Graphics' clipping rectangle covers the area occupied
by the applet, the intersection of the two rectangles winds up being
(image.getWidth(this),0,100,100).
The tip-off that the copy's Graphics has been translated comes
from the fact that both calls to drawImage() draw the image at
(0,0).
Unlike some object-oriented graphical toolkits, the AWT does not provide
classes for specific shapes, such as Line and Circle
classes. Instead, each AWT component comes with a Graphics object
that is used to perform graphical operations in the component. In addition to
components, other output devices, such as printers and offscreen images, also
have associated Graphics for performing graphical operations.
java.awt.Graphics provides a wealth of methods for drawing
and filling shapes, drawing text and setting graphical parameters such as
the color and font used for the next rendering operation. However, in some
respects Graphics are quite limited--for instance, the pen used
for drawing is restricted in size to a one pixel square. The Java 2D API
remedies many of the shortcomings of the Graphics class; however,
the 2D API is not strictly a part of the AWT, and is beyond the scope of this
book (see Graphic Java Mastering the JFC Volume III: The 2D
API for an in-depth look at the 2D API).
The coordinate system for graphical operations is anchored in the upper
left-hand corner of the device, with x and y coordinates increasing down
and to the right, respectively. Coordinates lie in between pixels instead
of on them, and Graphics methods that draw outlines of shapes
are passed arguments that define the shape in terms of coordinates instead
of pixels. The graphics pen hangs down and to the right of the coordinate
path, and therefore outlines of shapes result in an extra row of pixels on
the right and bottom sides of the shapes. Shape fills, on the other hand,
fill the interior of the shape, and therefore the fill is the same size as
the coordinate path. This discrepancy in size between shape outlines and
fills is often a stumbling block to newcomers to the AWT.
Each Graphics corresponds to a graphics context from the
underlying native windowing system. As a result, Graphics
represent a finite resource that must be manually disposed of. If a
Graphics is obtained by invoking a method that returns
a reference to a Graphics, Graphics.dispose()
must be called for the Graphics in order to free system
resources. On the other hand, methods that are passed a Graphics
reference are generally absolved from having to dispose of the
Graphics. In general it is up to the caller of such methods to
dispose of the Graphics.
The Graphics class also comes with a number of handy features such as: translating the origin of the coordinate system, clipping graphical operations to a specified shape, and the ability to create a copy of an existing Graphics.
java.awt.peer methods omittedjava.awt.peer methods omittedGraphics'
font.getSize() is a Component
method.Point no-argument constructor
sets the point's location to (0,0).
The author, David M. Geary, was the lead engineer for the user interface toolkit for Sun's Java Software Division's Java Management API. David has been developing GUIs and using object-oriented technology since 1984, including C++, Smalltalk, Eiffel, Objective-C and now Java. He is currently an independent Java programming consultant.
Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.
|
| ||||||||||||