12 Painting
12 Painting
Facultad de Ingeniera
Departamento de Sistemas
ertificacin en
AVA
12. PAINTING
The paint() method and the Graphics
Context
The Visual Components
The Container Components
The Menu Components
Objectives
Many types of AWT components (buttons and scroll
bars, for example) have their appearance dictated
by the underlying window system. Other component
types, notably applets, frames, panels, and canvases,
have no intrinsic appearance. If you use any of these
classes and want your component to look at all useful,
you will have to provide the code that implements the
component's appearance
Java's painting mechanism provides the way for you
to render your components. The mechanism is robust,
and if you use it correctly you can create good,
scaleable, reusable code. The best approach is to
understand how Java's painting really works.
Objectives
The fundamental concepts of painting are
The paint() method and the graphics context
The GUI thread and the repaint() method
Spontaneous painting
Painting to images
This chapter will take you through the steps
necessary to understand and apply these concepts.
And while the topics covered here are not explicitly
mentioned many exam objectives, you may well
find this information useful or essential
The paint() method and the Graphics
Context
example of the paint() method:
1. import java.applet.Applet;
2. import java.awt.*;
3.
4. public class SimplePaint extends Applet {
5. public void paint( Graphics g ) {
6. g.setColor( Color.black );
7. g.fillRect( 0, 0, 300, 300 );
8. g.setColor( Color.white );
9. g.fillOval( 30, 30, 50, 50 );
10. }
11.}
A very simple painting applet
Applet Viewer: SimplePaint.class
Applet started.
One interesting point about this applet is
that no calls are made to the paint()
method. The method is simply provided
The environment seems to do a good job of
calling paint() at the right moment
Exactly when the environment chooses to
call paint() is the subject of "Spontaneous
Painting," later in this chapter
Painting on a component is accomplished by
making calls to a graphics context, which
is an instance of the Graphics class
A graphics context knows how to render
onto a single target
The three media a graphics context can
render onto are:
Components
Images
Printers
Any kind of component can be associated
with a graphics context
The association is permanent; a context
cannot be reassigned to a new
component
Although you can use graphics contexts to
paint onto any kind of component, it is
unusual to do so with components that
already have an appearance
Buttons, choices, check boxes, labels, scroll
bars, text fields, and text areas do not often
require programmer-level rendering
Most often, these components just use the
version of paint() that they inherit from the
Component superclass. This version does
nothing; the components are rendered by the
underlying window system
However, there are four classes of "blank"
components that have no default
appearance and will show up as empty
rectangles, unless they are subclassed and
given paint() methods
These four component classes are:
Applet
Canvas
Frame
Panel
The four major operations provided by the
Graphics class are:
Selecting a color
Selecting a font
Drawing and filling
Clipping
Selecting a Color
Colors are selected by calling the setColor()
method
The argument is an instance of the Color class
There are 13 predefined colors, accessible as
static final variables of the Color class
The variables are themselves instances of the
Color class, which makes some people
uneasy, but java has no trouble with such
things
Predefined colors
Color.yellow
Color.blue
Color.green
Color.orange
Color.magenta
Color.cyan
Color.pink
Color.lightGray
Color.darkGray
Color.gray
Color.white
Color.black
Color.red
If you want a color that is not on this list,
you can construct your own
There are several versions of the Color
constructor; the simplest is
Color( int redLevel, int greenLevel, int blueLevel )
1. public void paint( Graphics g ) {
2. Color c = new Color( 170, 255, 170 );
3. g.setColor( c );
. . .
Selecting a Font
Setting the font of a graphics context is like
setting the color:
subsequent string-drawing operations will
use the new font, while previously drawn
strings are not affected
The first parameter is the name of the font
Font availability is platform dependent
Font( String fontname, int style, int size )
Before you can set a font, you have to create
one
You can get a list of available font names,
returned as an array of strings, by calling
the getFontList() method on your toolkit
String fontnames[] =
Toolkit.getDefaultToolkit().getFontList()
"Serif"
"SansSerif"
"Monospaced"
There are three font names that you can
always count on, no matter what platform
you are running on:
On releases of the JDK before 1.1, these
were called, respectively,
"TimesRoman", "Helvetica", and
"Courier
1. Font f = new Font( "SansSerif", Font.BOLD, 24 );
2. gc.setFont( f )
The style parameter of the Font constructor
should be one of the following three ints:
Font.PLAIN
Font.BOLD
Font.ITALIC
You can specify a bold italic font by
passing Font.BOLD+Font.ITALIC as the
style parameter to the Font constructor
All the rendering methods of the Graphics
class specify pixel coordinate positions for
the shapes they render
Every component has its own coordinate
space, with the origin in the component's
upper-left corner, x increasing to the
right, and y increasing downward
Drawing and Filling
X
Y
(0,0)
Every component has its own coordinate
space, with the origin in the component's
upper-left corner, x increasing to the
right, and y increasing downward
drawLine()
drawRect() and fillRect()
drawOvalQ and fillOval()
drawArc() and fillArc()
drawPolygon() and fillPolygon()
drawPolyline()
drawString()
Graphics contexts do not have an extensive
repertoire of painting methods
Sophisticated rendering is handled by
extended APIs such as 2D, 3D, and
Animation
drawLine()
The drawLine() method draws a line from
point (x0, y0) to point (x1, y1)
public void drawLine( int x0, int y0, int x1, int y1 );
Applet Viewer: ...
Applet started.
g.drawLine( 20, 120, 100, 50 );
drawRect() and fillRect()
The drawRect() and fillRect() methods
respectively draw and fill rectangles
public void drawRect( int x, int y, int width, int height );
public void fillRect( int x, int y, int width, int height );
The x and y parameters are the coordinates
of the upper-left corner of the rectangle
Width and height must be positive numbers,
or nothing will be drawn
g.drawRect( 20, 20, 100, 80 );
Applet Viewer: ...
Applet started.
g.fillRect( 20, 20, 100, 80 );
Applet Viewer: ...
Applet started.
drawOval() and fillOval()
The drawOval() and fillOval() methods
respectively draw and fill ovals
An oval is specified by a rectangular
bounding box
To draw a circle, use a square bounding
box
The oval lies inside the bounding box and is
tangent to each of the box's sides at the
midpoint
90
0 180
270
The two oval-drawing methods require you
to specify a bounding box in exactly the
same way that you specified a rectangle
in the drawRect() and fillRect() methods:
public void drawOval( int x, int y, int width, int height );
public void fillOval( int x, int y, int width, int height );
g.drawOval( 10, 10, 150, 100 );
Applet Viewer: ...
Applet started.
g.fillOval( 10, 10, 150, 100 );
Applet Viewer: ...
Applet started.
drawArc() and fillArc()
An arc is a segment of an oval
To specify an arc, you first specify the
oval's bounding box, just as you do with
drawOval() and fillOval()
You also need to specify the starting and
ending points of the arc, which you do by
specifying a starting angle and the angle
swept out by the arc
Angles are measured in degrees. For the
starting angle, 0 degrees is to the right, 90
degrees is upward, and so on, increasing
counterclockwise
A filled arc is the region bounded by the arc
itself and the two radii from the center of
the oval to the endpoints of the arc
public void drawArc( int x, int y, int width, int height,
int startDegrees, int arcDegrees );
public void fillArc( int x, int y, int width, int height,
int startDegrees, int arcDegrees );
g.drawArc( 10, 10, 150, 100, 45, 180);
Applet Viewer: ...
Applet started.
Applet Viewer: ...
Applet started.
g.fillArc( 10, 10, 150, 100, 45, 180);
drawPolygon() and fillPolygon()
A polygon is a closed figure with an
arbitrary number of vertices
The vertices are passed to the
drawPolygon() and fillPolygon() methods
as two int arrays
The first array contains the x coordinates of
the vertices
the second array contains the y coordinates
A third parameter specifies the number of
vertices
public void drawPolygon( int[] xs, int[] ys, int numPoints );
public void fill Polygon( int[] xs, int[] ys, int numPoints );
1. int[] polyXs = { 20, 150, 150 };
2. int[] polyYs = { 20, 20, 120 };
3. g.drawPolygon( polyXs, polyYs, 3 );
Applet Viewer: ...
Applet started.
1. int[] polyXs = { 20, 150, 150 };
2. int[] polyYs = { 20, 20, 120 };
3. g.fillPolygon( polyXs, polyYs, 3 );
Applet Viewer: ...
Applet started.
drawPolyline()
A polyline is similar to a polygon, but it is
open rather than closed:
there is no line segment connecting the last
vertex to the first
There is no fillPolyline() method, since
fillPolygon() achieves the same result
The parameters to drawPolyline() are the
same as those to drawPolygon():
a pair of int arrays representing vertices
and
an int that tells how many vertices there
are
public void drawPolyline( int[] xs, int[] ys, int numPoints );
1. int[] polyXs = { 20, 150, 150 };
2. int[] polyYs = { 20, 20, 120 };
3. g.drawPolyline( polyXs, polyYs, 3 );
Applet Viewer: ...
Applet started.
drawString()
The x and y parameters specify the left edge
of the baseline of the string
Characters with descenders (g, j, p, q, and y
in most fonts) extend below the baseline
public void drawString( String s, int x, int y );
The drawString() method paints a string of
text
By default, a graphics context uses the font
of the associated component
However, you can set a different font by
calling the graphics context's setFont()
method
1. Font font = new Font( "Serif", Font.PLAIN, 24 );
2. g.setFont( font );
3. g.drawString( "juggle quickly", 20, 50 );
4. g.setColor( Color.gray );
5. g.drawLine( 20, 50, 150, 50 );
Applet Viewer: ...
juggle quickly
drawImage()
An Image is an off-screen representation of
a rectangular collection of pixel values
Java's image support is complicated
The section "Images" discusses what you
need to know about creating and
manipulating images
For now, assume that you have somehow
obtained an image (that is, an instance of
class java.awt.Image) that you want to
render to the screen using a certain
graphics context
im is the image to be rendered, and
x and y are the coordinates within the
destination component of the upper-left
corner of the image
There are other versions of the method, but
this is the most common form
void drawlmage( Image im, int x, int y, ImageObserver observer );
call the graphics context's drawlmage()
method
The image observer must be an object that
implements the ImageObserver interface
and can be null
Clipping
Most calls that programmers make on
graphics contexts involve color selection
or drawing and filling. A less common
operation is clipping.
Clipping is simply restricting the region that
a graphics context can modify
Every graphics context (that is, every
instance of the Graphics class) has a clip
region, which defines all or part of the
associated component
When you call one of the drawXXX() or
fillXXX() methods of the Graphics class,
only those pixels that lie within the
graphics context's clip region are modified
The default clip region for a graphics context
is the entire associated component
Methods that retrieve and modify a clip
region
1. public void paint( Graphics g ) {
2. for ( int i = 10; i < 500; i += 20 )
3. for ( int j = 10; j < 500; j += 20 )
4. g.fillOval( i, j, 15, 15 );
5. }
Applet Viewer: Vi...
Applet started.
1. public void paint( Graphics g ) {
2. g.setClip( 100, 100, 100, 100 );
3. for ( int i = 10; i < 500; i += 20 )
4. for ( int j = 10; j < 500; j += 20 )
5. g.fillOval( i, j, 15, 15 );
6. }
Applet Viewer: Cl...
Applet started.
If an applet or a frame contains components
that have their own paint() methods, then
all the paint() methods will be called by
the environment when necessary.
For example, if a frame contains a panel and
a canvas, then at certain times the
environment will call the frame's paint(),
the panel's paint(), and the canvas'
paint().
Painting a Contained Component
1. import java.awt.*;
2.
3. public class ThingsInFrame extends Frame {
4. public ThingsInFrame() {
5. super( "Panel and Canvas in a Frame" );
6. setSize( 350, 500 );
7. setLayout( new GridLayout(3, 1 ) );
8. add( new RectsPanel() );
9. add( new OvalsCanvas() );
10. }
11.
12. public static void main( String[] args ) {
13. ThingsInFrame tif = new ThingsInFrame();
14. tif.setVisible( true );
15. }
16.
17. public void paint( Graphics g ) {
18. Rectangle bounds = getBounds();
19. int y = 12;
20. while ( y < bounds.height ) {
21. g.drawString( "frame frame frame frame frame frame" ,
60, y );
22. y += 12;
23. }
24. }
25. }
26.
1.
2.
3.
4. class RectsPanel extends Panel {
5. public RectsPanel() {
6. setBackground( Color.lightGray );
7. }
8.
9. public void paint( Graphics g ) {
10. Rectangle bounds = getBounds();
11. int x = 0;
12. int y = 0;
13. int w = bounds.width - 1;
14. int h = bounds.height - 1;
15. for ( int i = 0; i < 10; i++ ) {
16. g.drawRect( x, y, w, h );
17. x +- 10;
18. y += 10;
19. w -= 20;
20. h -= 20;
21. }
22. }
23. }
24.
1.
2.
3. class OvalsCanvas extends Canvas {
4. public OvalsCanvas() {
5. setForeground( Color.white ) ;
6. setBackground( Color.darkGray );
7. }
8.
9. public void paint( Graphics g ) {
10. Rectangle bounds = getBounds();
11. int x = 0;
12. int y = 0;
13. int w = bounds.width - 1;
14. int h = bounds. height - 1;
15. for ( int i = 0; i < 10; i++ ) {
16. g.drawOval( x, y, w, h );
17. x += 10;
18. y += 10;
19. w -= 20;
20. h -= 20;
21. }
22. }
23. }
The frame uses a Grid layout manager with
three rows and one column
The panel is added to the frame first, so it
appears in the top third of the frame
The canvas is added second, so it appears in
the middle third
Since there is no component in the last grid
cell, what you see in the bottom third of the
frame is the frame itself (that is, you see
whatever the frame itself draws in its own
paint() method)
paint() method and the graphics
context
(summary)
A graphics context is dedicated to a single
component
To paint on a component, you call the
graphics context's drawXXX () and fillXXX()
methods
To change the color of graphics
operations, you call the graphics contexts
setColor() method
The GUI Thread and the repaint()
Method
The runtime environment creates and
controls its own threads that operate
behind the scenes, and one of these threads
is responsible for GUI management
This GUI thread is the environment's tool
for accepting user input events and for
calling the paint() method of components
that need painting
Calls to paint() are not all generated by
the environment
Java programs can of course make their
own calls, either directly (which is not
recommended) or indirectly (via the
repaint() method)
paint() calls can be generated by:
Spontaneous painting, initiated by the
environment
Programmer-initiated painting
Spontaneous Painting is not an official Java
term, but it gets the point across
Some painting happens all by itself, with no
impetus from the program
For example, when a browser starts up an
applet, shortly after the init() method
completes, a call is made to the paint()
method
Spontaneous Painting
Also, when part or all of a browser or a
frame is covered by another window and
then becomes exposed, a call is made to
the paint() method
It is the GUI thread that makes these calls
to paint().
Every applet, and every application that has
a GUI, has a GUI thread
The GUI thread spontaneously calls paint()
under four circumstances, two of which
are only applicable for applets:
After exposure
After de-iconification
Shortly after init() returns (applets only)
When a browser returns to a previously
displayed page containing an applet,
provided the applet is at least partially
visible
When the GUI thread calls paint(), it must
supply a graphics context, since the
paint() method's input parameter is an
instance of the Graphics class
The GUI thread makes sure that the
graphics contexts that get passed to
paint() have their clip regions
appropriately set
Most often, the default clip region is
appropriate. Recall that the default clip
region is the entire component
However, when a component is exposed,
the clip region is set to be just that
portion of the component that requires
repair
If only a small piece of the component
was exposed, then the clip region
insures that no time is wasted on
drawing pixels that are already the
correct color
The repaint() Method
There are times when the program, not the
environment, should initiate painting. This
usually happens in response to input
events
Suppose you have an applet that wants to
draw a red dot at the point ot the most
recent mouse click. The remainder of the
applet should be yellow
1. public void mouseClicked( MouseEvent e ) {
2. Graphics g = getGraphics();
3. g.setColor( Color.yellow );
4. g.fillRect( 0, 0, getSize().width, getSize().height
);
5. g.setColor( Color.red ); // red dot
6. g.fillOval( e.getX()-10, e.getY()-10, 20, 20 );
7. }
Assume that the applet is handling its own mouse
events. Your event handler might look like this:
There are two reasons why this approach is far from
optimal
First, if the applet ever gets covered and exposed,
the GUI thread will call paint(). Unfortunately,
paint() does not know about the red circle that was
drawn in mousedClicked(), so the red circle will not
be repaired
It is a good rule of thumb to do all drawing
operations in paint(), or in a method called from
paint(), so that the GUI thread will be able to repair
exposure damage. The GUI thread expects paint() to
be able to correctly reconstruct the screen at anv
arbitrary moment
The way to give the GUI thread what it
expects is to remove all drawing code from
event handlers
Event handlers such as mousedClicked()
above should store state information in
instance variables, and then cause a call to
paint()
1. public void mouseClicked( MouseEvent e ) {
2. mouseX=e.getX();
3. mouseY=e.getY();
4. Graphics g = getGraphics();
5. paint (g);
6. }
The paint() method should use the values of the
instance variables as instructions on what to draw
In our example, mousedClicked() should be
modified as shown below, assuming that the class
has instance variables m_mouseX and m_mouseY:
1. public void paint( Graphics g ) {
2. g.setColor( Color.yellow ); // yellow
background
3. g.fillRect( 0, 0, getSize().width, getSize().height
);
4. g.setColor(Color.red); // red dot
5. g.fillOval( m_mouseX-1O, m_mouseY-10, 20, 20
);
6. }
7.
The paint() method should be as follows:
Much better! Now if a dot gets covered and exposed,
the damage will be repaired automatically.
There remains, however, a second problem, and it is
a bit subtler than the spontaneous painting issue.
setBackground( Color.yellow );
The program can be simplified a bit.
There is a method of the Component class called
update(), which clears the component to its
background color and then calls paint().
The input parameter to update() is a graphics
context
The applet's init() method could set the background
color to yellow:
1. public void mousedicked( MouseEvent e ) {
2. m_mouseX = e.getX();
3. m_mouseY = e.getY();
4. Graphics g = getGraphics();
5. update( g );
6. }
7.
8. public void paint( Graphics g ) {
9. g.setColor( Color.red );
10. g.fillOval( m_mouseX-10, m_mouseY-10, 20, 20
);
11. }
12.
Now the event handler should call update() rather
than paint(), and update() only needs to draw the
red dot:
In the real world, programs often need to track
many different kinds of events: action events,
adjustment events, key events, focus events, mouse
events, mouse-motion events, and so on. It may be
that every event requires painting the screen anew
Consider what happens if a large number of events
of different type are generated in rapid succession:
this is not unusual; moving or dragging the mouse
can create a lot of mouse-moved and mouse-
dragged events in very short order
Time after time, an event will be generated, event
handlers will modify instance variables, and paint()
will modify the screen, only to have the cycle repeat
and repeat
Most of the screen-drawing operations will instantly
get clobbered by other screen-drawing operations
triggered by more recent events. Many compute
cycles will be wasted
The more compute-intensive the paint() method is,
the worse the situation becomes. If the user can
generate events faster than paint() can handle
them, then the program will tail farther and farther
behind
It would be ideal if the event handlers could just
modify the instance variables and have paint() run
from time to time, often enough that the screen
stays up to date, but not so often that compute
cycles are wasted
This is where the repaint() method comes in
The repaint() method schedules a call to the
update() method
All this means that a request flag is set in the GUI
thread. Every 100 milliseconds (on most platforms),
the GUI thread checks the flag
If the flag is set, thell thread calls update() and
clears the flag
No matter how many requests are made during any
100-millisecond period, only a single call is made to
update()
1. public void mouseclicked( MouseEvent e ) {
2. m_mouseX = e.getX();
3. m_mouseY = e.getY();
4. repaint();
5. }
6.
7. public void paint( Graphics g ) {
8. g.setColor( Color.red );
9. g.fillOval( m_mouseX-10, m_mouseY-10,
20, 20 );
10.}
The code above shows the preferred approach to
handling events that cause the screen to be
changed:
event handlers store information in instance
variables and then call repaint(), and paint() draws
the screen according to the information in the
instance variables
The two benefits of this approach are:
The screen is correctly repaired when the
environment spontaneously calls paint()
The Virtual Machine never gets overwhelmed
by events
If you want to accumulate dots, rather than have
each dot cleared away when a new one is to be
drawn, you can always override update() so that it
does not clear
All update() needs to do in this case is call paint(),
as shown below:
1. public void update( Graphics g ) {
2. paint( g );
3. }
This is a standard technique
Images
Images are off-screen representations of
rectangular pixel patterns
There are three things you can do with
images:
Create them
Modify them
Draw them to the screen or to other
images
There are two ways to create an image
1. You can create an empty one, or
2. you can create one that is initialized from
a .gif or a .jpg file
To create an empty' image, call the createlmage()
method of the Component class and pass in the
desired width and height
For example, the following line creates an image
called im that is 400 pixels wide by 250 pixels
high; it might appear in the init() method of an
applet:
Image im = createlmage( 400, 250 );
An image can be created based on the information in
a .gif or a .jpg file
The Applet and Toolkit classes both have a method
called getlmage(), which has two common forms:
getImage( URL fileURL )
getImage( URL dirURL, String path )
The code fragment below shows an applet's init()
method; it loads an image from a file that resides
in the same server directory as the page that
contains the applet:
1. public void init() {
2. Image im = getImage( getDocumentBase(),
"thePicture.gif" ):
3. }
If you load an image from a file, you may want to
modify it; you will definitely want to modify any
image that you create via createlmage()
Fortunately, images have graphics contexts. All you
need to do is obtain a graphics context for the
image you wish to modify and make the calls
that were discussed earlier in this chapter in
"Drawing and Filling."
To obtain a graphics context, just call the
getGraphics() method of the image
The code below implements an applet whose init()
method creates an image and then obtains a
graphics context in order to draw a blue circle
on a yellow background
The applet's paint() method renders the image
onto the screen using the drawImage() method
1. import java.applet.Applet;
2. import java.awt.*;
3.
4. public class Paintlmage extends Applet {
5. Image im;
6.
7. public void init() {
8. im = createlmage( 300, 200 );
9. Graphics imgc = im.getGraphics();
10. imgc.setColor( Color.yellow );
11. imgc.fillRect( 0, 0, 300, 200 );
12. imgc.setColor( Color.blue );
13. imgc.fillOval( 50, 50, 100, 100 );
14. }
15.
16. public void paint( Graphics g ) {
17. g.drawlmage( im, 25, 80, this );
18. }
19. }
1. import java.awt.*;
2. public class SimplePaint extends Frame{
3. Image im;
4. public SimplePaint()
5. {
6. im =Toolkit.getDefaultToolkit().createImage
7. ("Paraiso.jpg");
8. }
9. public void paint( Graphics g )
10. {
11. if (im!=null)
12. {
13. g.drawImage(im,0,0,getWidth(),
14. getHeight(),0,0,im.getWidth(this),
15. im.getHeight(this),this);
16. }
17. }
18.
19. public static void main(String args[])
20. {
21. SimplePaint a = new SimplePaint();
22. a.setSize(100,100);
23. a.setVisible(true);
24. }
25. }