Java Programming   Java Programming
 |Sofia Home | Content Gallery |
Home
Syllabus
Schedule
Lessons
Assignments
Resources

Lesson 4.5 The paint() Methods

Writing the paint() Method

In this lesson, you learned how to write your own methods. You also learned about serveral different kinds of methods and method relationships. You learned about:
  • methods that produce values.
  • methods that take arguments
  • overloaded methods, having the same name, but different arguments. 
  • static methods that can be called without benefit of an object.
You've also used several inherited methods. You learned to call the setFont() and setForeground() methods that the Button and Label classes inherited from their superclass, the Component class.

But there's one class of method you haven't really studied, although you've been using this type of method all along. That is the category of overridden methods.

Overridden and Applet Methods

Surprisingly, you've been writing overridden methods even longer than any of the others. The Applet init() method, which you've used from Lesson 1, is an overridden method. You'll remember from Lesson 4.1, "Writing Methods", that an overridden method is a method that has exactly the same signature as a method in the superclass [or other ancestor class]. 

Frequently, students confuse overloaded and overridden methods, because both of them allow you to use the same name for different methods. The important difference you must remember to avoid confusion is:

  • Overloaded methods: same name but different signatures.
  • Overridden methods: same name and exactly the same signature.

Life Cycle Methods

When you run an applet in your Web browser, or using the appletviewer, the browser creates an instance of your applet--an Applet object. It then sends a predictable set of messages to the Applet object. This set of predictable methods is called the applet life cycle, pictured here: [click image to enlarge]

Image showing the applet life-cycle. Click image to enlarge.

init()

When an Applet object is first created, your browser starts by calling its init() method. This method is only called once. The version of init() that your applet inherits from the java.applet.Applet class does absolutely nothing, so if your applet has any initialization at all, you'll normally override this method.

Here is the signature you must use:

public void init() { }

If you use a different signature, spelling the method Init(), for instance [capital I], it is not an error; this simply creates a different method. [It's not an overloaded method either because the methods now have different names]. Even though the compiler doesn't complain, however, your program will fail to run, because your browser calls a method named init(), while your program has a method named Init().

start()
After calling init(), your browser calls the inherited method named start(). You can put more initialization in this method if you like. The difference between start() and init() is that init() is only called when an applet is loaded, while start() is called every time the Web page containing the applet is displayed, whether newly loaded or from your browser's cache.

That might not seem like a big distinction, but it is. When you leave a Web page to visit another one, the page is normally stored locally in your disk and memory cache. When you revisit the page--using the back button, for instance--the local copy of your Web page is redisplayed. 

When this occurs, your browser calls your applet's start() method. Here is the signature you must use to override start():

public void start() { }
stop()
In the same way that start() is called whenever you display an applet, the stop() method is called when your browser leaves the page containing your applet. 

You'll normally override the stop() method to put your program in a dormant state. If you are playing music, for instance, you'll use the stop() method to turn it off and the start() method to start it back up. You'll see how to do this later in the course when you begin working with threads.

The signature for the stop() method is:

public void stop() { }
destroy()
The last of the basic applet life cycle methods is destroy(). Your browser will call your applet's destroy() method when it needs to discard the applet from memory. If you revisit the Web page containing the applet after destroy() has been called, then a new applet object will be created, and init() will be called once again.

If your applet allocates a system resource, typically a thread, you should override the destroy() method to kill the thread. The stop() method will always be called before the destroy() method.

The signature for the destroy() method is:

public void destroy() { }
Back to Top

A Life Cycle Example

Here's an example program that allows you to some visual evidence of the applet life cycle at work. Click on the applet life cycle illustration appearing above, or click your back and forward buttons to repeatedly access this page, while watching the labels below. Does what appears in the labels seem consistent with what you know of the applet life cycle?

If you have access to both Internet Explorer and Netscape Navigator, you can see that each browser treats the life-cycle a little differently. Navigator keeps applets in memory and only calls destroy() when there are too many applets in memory [a process called pruning.] Explorer, on the other hand, seems to call destroy() whenever you leave the page containing an applet.

The paint() Method

In addition to the four messages listed above, there is one other message sent by your browser to your applet that you will frequently override: the paint() message.

The paint() message is sent to your browser when your applet first appears, and thereafter whenever any portion of your applet's window is obscured and needs to be repainted. If you use your browser's scrollbars, for instance, to scroll your applet out of the browser window, and then scroll it back into view, your applet will need to be repainted.

Up to now, we haven't needed to worry about this because we haven't been directly drawing on our applet's surface. When you use labels and buttons, each Label object and each Button object has its own paint() method, so we've been able to ignore the mechanics of painting. 

But no more; it's time to learn how to paint. We'll start, in this section, by drawing text.

Drawing Text

To draw a phrase on the surface of your applet, you just call the drawString() method which takes three arguments:
  • the String to display
  • the horizontal position to start drawing
  • the veritical position to start drawing

Here's how you'd display the text "Hi Mom" 10 pixels down from the top of your applet and 15 pixels in from the left:

g.drawString("Hi Mom", 15, 10);
Back to Top

The Coordinate System

When painting, the horizontal [x],  and vertical [y] positions are measured from the upper-left corner of your applet, [not from the screen or from your browser window], and the unit used for measurement is the pixel as you can see with this figure. [Click the image to expand]
Figure showing the coordinate system used by the AWT. Click image to enlarge.

This is similar to the coordinate system used to position labels or buttons or any other type of component, with one exception. When you position a Label object, the x,y arguments represent the upper-left corner of the component. 
Thus, if you position a Label object at location 0,0, like this

myLabel.setLocation(0, 0);

the actual Label object is displayed "hanging down" from that position.

On the other hand, if you use the same coordinates when calling the drawString() method, your words will not appear on screen at all. With drawString(), as you can see in the illustration, the x,y arguments represent the baseline of the text--an imaginary line where the text "sits". 

Drawn at position 0,0, all of your text will be positioned outside of your applet, and Java [wisely] prohibits you from painting all over screen real estate that you don't own.

Back to Top

Introducing Graphics

In the drawString() example, listed above, the message is sent to an object named "g". Who, or what, exactly, is "g"? At first glance, it would seem that the Applet or String classes would be likely candidates to handle such drawing chores. In fact, however, neither of them is up to it. 

The String class knows all about characters, but it doesn't know anything about the resolution of your video display, or how many pixels are requried to draw a "t". For that, you need the services of an expert; in Java, that expert is a member of the Graphics class.

As with the NumberFormat class, you don't create a Graphics object using the new operator, and a convenient constructor. Instead, you have to "borrow" a Graphics object from someone who already has one. Fortunately, the Applet class is willing to "loan" you its resident Graphics object.

To borrow a Graphics object from your applet simply call the applet's getGraphics() method, like this:

Graphics g = getGraphics();

When you're finished using the Graphics object, return it by calling the dispose() method, like this:

g.dispose();

Make sure you do this before the method creating the Graphics object ends.
"Borrowing" a Graphics Object
When you borrow a Graphics object from your applet, it doesn't hand you over the real thing; instead, it gives you a copy. Unfortunately, there are only a limited number of copies to go around. Whenever you borrow a Graphics object via the getGraphics() method, you have to remember to dispose of your copy when you're done with it [by calling its dispose() method]. This doesn't apply to the Graphics objects that you receive automatically in the paint() method. The applet class will dispose of those copies itself.

 

Back to Top

BadPaint...

The BadPaint applet from your text uses getGraphics() to paint a snappy slogan on the applet surface whenever a button is pressed. Here's the relevant code from the applet's actionPerformed() method. [Some names have been changed for size reasons. You can download the actual code by clicking on the BadPaint hyperlink at the beginning of this paragraph.]

public void 
actionPerformed(ActionEvent theEvent)
{
  Graphics g = getGraphics( );

  g.setFont( bigFont );
  g.setColor( Color.blue );

  g.drawString(theText,  25,  75 );
  g.drawString(theText, 100, 125 );
  g.drawString(theText, 175, 175 );

  g.dispose();
}

As you can see, when the ActionEvent occurs, the method:

  1. obtains a Graphics object from the applet
  2. changes the font used for drawing
  3. changes the color used for drawing
  4. displays its message three times
  5. returns the Graphics object so that it can be reused

You can see the BadPaint applet running here:

Back to Top

...GoodPaint

So, what's the problem with BadPaint? 

If you press the button, you'll see that the message is nicely displayed in a bright blue. Also, notice that Java prevents the slogan from spilling over to other parts of the Web page.

But, if you scroll a portion of the image out of the browser window, or, if you drag another window over a portion of the slogan, you'll see that the text is not at all permanent, it is immediately erased. 

That's where the paint() method comes in. Every time a portion of your applet is hidden and then redisplayed, your Web browser calls your applet's paint() method, just as it does the init(), start(), stop(), and destroy() methods. If you want to draw directly on your applet's surface, you should put the necessary code inside the paint() method, where it will be automatically updated as necessary.

The signature for the paint() method looks like this:

public void paint(Graphics g) { }

Note that your applet will automatically take care of passing you a Graphics object, so you don't have to call getGraphics(). Since you didn't "borrow" this Graphics object, you don't need to dispose of it either.

Here's the code for the GoodPaint applet. [Actually, if you download the code, you'll see the name is SecondPaint.java. As with BadPaint, we've changed some names to protec.., er.., to fit into the Web page window.] As you can see, the program is smaller and cleaner, and you don't have to deal with events.

public void paint( Graphics g )
{
  g.setFont(theFont);
  g.setColor( Color.red );

  g.drawString( theText, 175,  50 );
  g.drawString( theText, 100, 100);
  g.drawString( theText,  25, 150);
}

You can see the GoodPaint applet running here. Notice that if you scroll the applet out of the window, or cover it up with another window, it comes right back. It's magic!

Back to Top

The FontMetrics Class

There is one more little detail we need to tackle before we can leave the subject of drawing text. How do you tell exactly where your should draw?

Here's the problem. When you use drawString() you have to tell the method where to start drawing, using pixels as your measuring instrument. It would be a lot easier if we could use rows and columns or something of that sort.

Well, you can, more or less. At least the row part is easy. The column part is more difficult because the characters displayed in a Java font are usually proportional--that is, the letter 'i' is narrower than the letter 'm'. If you want to move the fifth column, should you move over five 'i's worth of pixels, or five 'm's worth of pixels?

Calculating Rows

The height of a row [or line] of text depends upon the size of the font you use. If you use 12-point type, then each line might be 18 pixels high. If you use 36-point type, then each line might be 48 pixels high. Bigger fonts mean bigger line-height when measured in pixels.

That part is easy. So, all we need is some kind of table that tells us how many pixels-per-point, and we're off and running, right? 

Well..., not so quick. 

Suppose you run your computer in 640 x 480 mode, and you want to know the height of a 72-point font. If you get a magnifying glass and count the pixels, you'll see that a 72-point font uses approximately [depending on your O/S and display drivers] 35 pixels per line. Now, if you take the same computer, and the same program, and change the display resolution to 1024 x 768, you'll see that the 72-point font uses about 55 pixels per line.

What's going on here?

When you specify a 72-point font, you are saying you want a font that is [nominally] 1-inch high. [Each point is 1/72 of an inch]. If your display device uses 480 pixels to the inch, then an inch is about 35 pixels; if it uses 768 pixels to the inch, then an inch is about 55 pixels.

So, as you can see, it is impossible to come up with a simple formula that translates between point sizes and pixels. Fortunately, you don't have to; Java has a class that does just that: the FontMetrics class.

When you create a FontMetrics object it will give you information about:

  • the active Font object
  • in a particular Graphics environment
That sounds quite complex, but the reality is much simpler. Let's take a look.
Back to Top

Getting a FontMetrics object

To get a FontMetrics you use the Graphics getFontMetrics() method. Because the FontMetrics object that you get back refers to a particular Font object, you should set the Font before calling getFontMetrics(). Like this:

public void paint(Graphics g)
{
  g.setFont(myBigFont);
  FontMetrics fm = g.getFontMetrics();

  // Use the FontMetrics object, fm, here
}

Now that you have a FontMetrics object, you can use it to compute the size of a row of text like this:

row = fm.getHeight();

You can also use it to compute the width of a String, in pixels, like this:

width = fm.stringWidth(myText);

In Homework 4 you'll get some practice using the various methods in the FontMetrics class as you use it to measure and center a String.

Something to Talk About

Here's an easy exercise. Download, compile, and run the SecondPaint applet [GoodPaint] using the hyperlink in the page above. Now, change the applet so that it prints your name [rather than Jumbo's advertising slogan] three times. 

How can you position each line against the left margin of your applet, and use the FontMetrics class to calculate the vertical spacing, rather than hard-coding the numbers?

Please continue to the next section of this lesson.

 

Back to Top

 

Content Developed by Stephen Gilbert, Licensed under a Creative Commons License
Published by the Sofia Open Content Initiative
© 2004 Foothill-De Anza Community College District &The William and Flora Hewlett Foundation