Previous Page TOC Next Page


— 12 —
The TickerTape Applet

The preceding chapters have covered a great deal of material; now you can put that knowledge to work in a fun applet. Much of the enthusiasm surrounding Java has focused on its ability to make small, interesting applets to spice up a Web page. You'll create just such an applet in this chapter.

People are used to seeing tickertape-like signs wherever they go. You can't escape them. There seen on major-league scoreboards, signs in front of buildings, and even some highway billboards. Even though it may seem like implementing a tickertape sign in Java would be difficult, it turns out to not be that bad.

The TickerTape applet should have the following features:

Over the course of the chapter, you'll go from a basic applet that just draws a word on-screen to a full-motion animated sign in three steps.

Design Issues

At its core, what does a tickertape sign do? These are a few of the basics:

  1. Displays a message

  2. Waits a specified period of time

  3. Clears the screen

  4. Displays the message slightly to the left of where it was before

  5. If the message has scrolled off the left of the screen, starts displaying it again at the right-hand side of the screen

Implicit in the fifth point is the fact that the message may not all be on the screen at any one time. Although this fact isn't obvious for small messages, if you have a message that is several sentences long, it probably won't fit on the screen all at once.

This point may seem minor, but as you will see later, it is important to consider as many of these issues as possible at the beginning of the design process so you can watch out for them later.

Specifying the Message

It wouldn't make much sense to have a TickerTape applet that users couldn't specify their own messages for. You can have this user-defined message retrieved in one of three ways:

  1. The message could be specified in the Java applet source. This option is the simplest to implement, but it would require recompiling the applet source every time you wanted to change the message.

  2. The applet could get the message through a parameter (discussed in Chapter 10). This option would allow the message to be changed by just changing the HTML source that contains the applet.

  3. The applet could retrieve the message as a separate text file from the server. This option has the advantage of being the most flexible, but it is also the hardest to implement.

For this applet, the second option makes the most sense. It allows the user of the applet to easily change the message of the applet without complicating matters too much.

Specifying the Font

Having the same font for everything can get boring. Modern operating systems have a variety of fonts at your disposal. Why not go ahead and use them? Using a different font can set off the applet from other elements in your page. Specifying the font as a parameter to the applet makes the font easy to change.

Changing the Animation Speed

The tickertape applet gives the illusion of the text moving by erasing and then redrawing the text quickly enough that the eye perceives that the text hasn't been erased and redrawn, but has actually moved. Using this fact to your advantage, you can change the speed at which the text scrolls by varying the delay in between redraws of the message. Thus, the shorter the delay, the "faster" the text moves; the longer the delay, the "slower" the text moves.

Getting Started

At the core of the TickerTape applet is the drawing of the text. Applets have a set amount of screen space that they are given to work with, as specified by the <APPLET> tag. Think of this area as a blank canvas upon which anything you like can be drawn.

If you are coming from an HTML background, you might think of text and graphics as completely separate things, with areas set apart for each. In Java applets, text and graphics do not have established areas. Unless you explicitly set up areas for text with components like TextArea, the area that can be drawn upon is completely blank. You have to specify exactly where you want to put your text with x and y coordinates.

In Java, the x and y coordinates start at the upper left hand side of the applet and go down and to the right in pixels until the edge of the applet is reached. Thus, if your applet was set to be 300 by 200, the upper left hand corner of the applet would be at x=0 and y=0. The lower right corner would be at x=299 and y=199.

The area that is available for drawing is known as a graphics context. Although that sounds complicated, it really isn't. All this term means is that you are given an object to paint upon when it's time to paint on the canvas,. That object is the graphics context, and it is represented by instances of the Graphics class.

To begin writing the applet, you start with a generic applet and add a String object to represent the message, as shown in the following code:

public class Ticker1 extends java.applet.Applet {

String message;

}

You haven't used strings before, but they're not too difficult to deal with. A string is an object that holds a sequence of characters. For example, 1234, hello, and this is a string are all strings.

Like all objects, declaring a variable as a string type doesn't make an instance of the object, just a reference. Therefore, you need to find a place to actually make the string. Because the string only needs to be made when the applet is first created, the init() method would be a good place to make the string:

public void init() {

message = new String("Test");

    }

This code declares a new string with the value Test. Now that you have a message, you need to place it on the screen. As mentioned earlier, you need a graphics context to paint on a screen. Whenever the screen needs to be painted, the paint() method of the applet is called. This method is called when the applet first starts up or when something has hidden the applet and then shown it again (like when another window has been dragged over the applet).

The code for drawing the message on-screen is as follows:

public void paint(Graphics g) {

        g.drawString(message, 0 ,30);

    }

You now have the graphics context mentioned earlier, and thus a canvas to draw upon. The next line is kind of confusing:

g.drawString(message, 0 , 30);

The Graphics class contains the drawString() method, which allows strings to be drawn. This method takes three arguments:

After you add the drawString() method, the string is drawn at the x and y point you specify. Listing 12.1 is the complete applet (shown in Figure 12.1).

Figure 12.1. Drawing text on an applet.

import java.awt.*;

import java.lang.*;

public class Ticker1 extends java.applet.Applet {

String message;

public void init() {

message = new String("Test");

    }

public void paint(Graphics g) {

        g.drawString(message, 0 ,30);

    }

}

The HTML for this applet is as follows:

<APPLET CODE="Ticker1.class" WIDTH=300 HEIGHT=40></APPLET>

Fine Control over Drawing the Text

The first step of the TickerTape applet allowed text to be drawn on- screen, but a few things in that process need to be refined:

In order to make these refinements, you need to add several new variables:

Adding Font Control

Java has made life relatively simple when it comes to fonts by providing a Font object that can be created and then used throughout the applet. The Font object's constructor has the following format:

Font(String name, int style, int size)

The parameters for the Font object have the following meaning:

For this applet, the text will always be plain and 12-point (although this could easily be made configurable later). The font name will eventually be user-specified, but for now, set it to be Courier by adding the following line to the init() method:

theFont = new Font("Courier", Font.PLAIN, 12);

Next, you need to specify the height of the font. You can retrieve this information through the getSize() method:

fontHeight = theFont.getSize();

When you want to specify a font to be used for drawing, you set the font for the graphics context. This is a bit counter-intuitive because you would expect to specify a font as part of the drawString() method. At any one time, a font is selected for the current graphics context, and all text is drawn in that font.

You set the font by adding the following line to the paint() method:

g.setFont(theFont);

Tracking the Text's Location and Position

To find the height and width of the applet, call the size() method of the applet and get the width and height properties of the applet:

height = size().height;

width  = size().width;

When the text is drawn, the x and y positions of the point at which the text should be drawn needs to be known. For the x position, you can start at an arbitrary place; for now, you'll start by drawing the text at 0 on the x axis.

Finding out where to place the text on the y-axis is a bit trickier. It should appear exactly halfway down the y-axis of the applet. The obvious way to figure out this position would be to just take the height of the applet and divide it by two. Unfortunately, this way will not work. Why not? The height of the characters in the font needs to be taken into consideration as well; otherwise, the message would appear off-center.

All you need to do to correct this is to subtract half the height of the font from halfway down the y-axis. This should position the message at exactly the halfway point:

g.drawString(message, currentPos, height/2 - fontHeight/2);

The applet now has many new features that prepare it to be animated. Rather than having information for the position of the applet hardcoded into the drawString() method, this information has now been pulled out into variables that can be changed. Also, the font to use is no longer the default but has also been pulled out into a variable. Listing 12.2 shows the complete code for step two of the TickerTape applet.

import java.awt.*;

import java.lang.*;

public class Ticker2 extends java.applet.Applet {

    String message;

    int width, height;

    int fontHeight;

    int currentPos = 0;

    Font theFont;

    public void init() {

        height = size().height;

        width  = size().width;

        theFont = new Font("Courier", Font.PLAIN, 12);

        fontHeight = theFont.getSize();

        message = new String("Test");

    }

    public void paint(Graphics g) {

        g.setFont(theFont);

        g.drawString(message, currentPos, height/2 - fontHeight/2);

    }

}

The HTML for this applet is as follows:

<APPLET CODE="Ticker2.class" WIDTH=300 HEIGHT=40></APPLET>

Animating the Text

Next you need to get the text moving. Unfortunately, the concepts you have dealt with up to this point are not sufficient to do this because there is no method in an applet that is called on a regular basis without user interaction. Therefore, you need to have an external way to for the applet do something repeatedly. Java provides this capability through threads.

The name thread is derived from the concept that what is being added is an independent thread of execution. Think of it as a mini-program that runs independently alongside your applet. Adding a thread to your program is a pretty straightforward process, as long as you take it step by step. Follow these steps:

  1. Add a variable of type Thread to hold the thread.

  2. Add implements Runnable to the applet definition.

  3. Program the thread's logic in the run() method of the applet.

  4. Start the thread when the applet is started.

  5. Stop the thread when the applet is stopped.

Follow this process to add a thread to animate the tickertape. First, you need a variable to hold the thread. Call it animator because that is what you need the thread to do:

Thread animator;

Second, add implements Runnable to the class definition for the applet:

public class Ticker3 extends java.applet.Applet implements Runnable {

This line lets the Java compiler know that this applet contains a thread. The compiler now expects a run() method to be included in the applet as well:

public void run() {

    while (animator != null) {

        try {

            Thread.sleep(100);

        } catch (InterruptedException e) {

            break;

        }

        currentPos = currentPos - 5;

        if (currentPos < -messageWidth)

            currentPos = width;

        repaint();

    }

}

This code looks much more complicated, so more explanation is needed. First, the thread is checked to see whether it is null:

while (animator != null) {

All this code means is that the block of code that follows this line keeps repeating until the animator variable contains nothing. When a thread is created, its value is set in a variable(in this case animator). When the thread is stopped, you set its value to null, and this loop ends. Changing the value to null is necessary to prevent the run() method from repeating forever.

Next, you need to make the thread delay for a period of time; otherwise, the text would move way too fast. You can create this delay by calling the sleep() method of the thread. Unfortunately, some methods in Java that cannot just be called because they may generate an error, and sleep() is one of those methods.

When a method generates an error, the error is known as an exception. When you do some work after the error is generated, that is known as handling the exception. In Java, you handle the exception by trying the operation and catching the exception if something bad happened. This concept is tricky, but a real-world example might make it more understandable. Say you wanted to divide 2 by 0. You would try to divide 2 by 0, but that's not possible. What do you do then? You catch the problem with another set of things to do.

The try/catch process for this thread is as follows:

try {

    Thread.sleep(100);

} catch (InterruptedException e) {

    break;

}

What is happening in this code is that you are trying to cause the thread to go to sleep for a while (in this case, 100 milliseconds). If for some reason the operating system won't let this happen, you break out of the loop and exit the thread. Why? If the thread cannot pause itself anymore, then it is worthless to you and should stop. This shouldn't ever happen, however.

After the thread has slept for a little bit, the text needs to be moved. To do this, subtract five pixels from the currentPos. This amount should be enough for the user to perceive that the text has moved:

currentPos = currentPos - 5;

You can do this forever, and the text will keep scrolling to the left. Up to a point this action is good, but when the text scrolls off the edge of the left-hand side of the applet, it needs to start again at the right-hand side. One way to do this would be to check whether currentPos is less than 0:

if (currentPos < 0)

This code would work, but the result would be disconcerting to the eye. Why? Remember that text is drawn from left to right. When currentPos is equal to 0, the left edge of the message starts to scroll off the left-hand side of the applet the next time through the loop. If you move the message back to the right-hand side of the screen now, the rest of the message will not scroll off the left-hand side.

This problem can be corrected by restarting the text on the right-hand side only when the entire message has scrolled off the left-hand side of the applet. How could you do this? One way would be to find out how wide the message is, and then only restart the message when the current position is less than the negative of that. In effect, all you are doing is moving where 0 used to be over to the left to the width of the message number of pixels. With this correction, everything will appear as it should:

if (currentPos < -messageWidth)

    currentPos = width;

First, check to see whether the current position has scrolled off too far to the left. If it has, you set the position to the width of the applet, effectively starting the process all over again. You should also have the applet start the message at the right-hand edge of the screen, so add the following line in the init() method:

currentPos = width;

You also need to compute the width of the message. First, add the variable messageWidth to the applet:

int messageWidth;

Now compute this variable in the init() method:

messageWidth = getFontMetrics(theFont).stringWidth(message);

You don't need to understand in detail what this line does, only that it computes how many pixels wide the message will be if drawn on the current component(in this case, the applet) with the font theFont.

Now that the currentPos has been updated to a new position to the left and all the checking that needs to be done has been done, it's time to redraw the text. In order to do this, two things need to happen:

  1. The applet screen must be cleared.

  2. The text must be drawn.

You can accomplish both of these steps with one line of code:

repaint();

What does repaint() do? It clears the screen, and then calls the paint() method. Because you changed the paint() method in the previous section to check currentPos for where to draw the text, you don't need to make any changes.

This is a good example of why it makes sense to keep things as general as possible when you program and avoid hardcoding in information as much as possible. By writing the drawString() method to check variables instead of relying on constant numbers, you don't have to go back and change anything.

Most of the work is done now. All that is left is to get the thread started when the applet starts up and stop the thread when the applet stops. You may remember from Chapter 10 that in addition to init(), which is called when the applet is loaded, start() and stop() are also called whenever the applet is started and stopped. You can use this fact to your advantage in this applet.

You only want the tickertape to animate while the applet is being shown, so start up the thread in the start() method:

public void start() {

    animator = new Thread(this);

    animator.start();

}

In this code, you are making a new thread each time the applet starts up. This process makes sense because the thread will be killed every time the applet stops. Making an instance of the thread creates the thread, but until you call start(), the thread just sits there. Calling start() gets things rolling.

When the applet is exited, stop() is called because you want to stop the thread when that happens:

public void stop() {

    if (animator != null) animator.stop();

    animator = null;

}

First, you check to see whether the variable animator contains a thread. The animator variable should always contain a thread because a thread should always be running before the stop() method of the applet is called. Because you will be calling a method of the animator object, it makes sense to be a little paranoid and make sure that a thread is running before stop() is called. You then call the stop() method of the animator, which stops the thread. Finally, you set the animator thread equal to null, which gets rid of the thread entirely.

The tickertape now moves along indefinitely when the applet is started. Listing 12.3 is the source code for this stage of the applet.

import java.awt.*;

import java.lang.*;

public class Ticker3 extends java.applet.Applet implements Runnable {

    String message;

    int width, height;

    int fontHeight;

    int messageWidth;

    int currentPos;

    Font theFont;

    Thread animator;

    public void init() {

        height = size().height;

        width  = size().width;

        currentPos = width;

        theFont = new Font("Courier", Font.PLAIN, 12);

        fontHeight = theFont.getSize();

        message = new String("Test");

        messageWidth = getFontMetrics(theFont).stringWidth(message);

    }

    public void start() {

        animator = new Thread(this);

        animator.start();

    }

    public void stop() {

        if (animator != null) animator.stop();

        animator = null;

    }

    public void run() {

        while (animator != null) {

            try {

                Thread.sleep(100);

            } catch (InterruptedException e) {

                break;

            }

            currentPos = currentPos - 5;

            if (currentPos < -messageWidth)

                currentPos = width;

            repaint();

        }

    }

    public void paint(Graphics g) {

        g.setFont(theFont);

        g.drawString(message, currentPos, height/2 - fontHeight/2);

    }

}

The HTML is as follows:

 <APPLET CODE="Ticker3.class" WIDTH=300 HEIGHT=40></APPLET>

Adding Parameters

The TickerTape applet is almost finished. All that you need to add now is the capability to pass in parameters. This capability is covered in detail in Chapter 10, so you will just be given the basics here.

First, you need the getParameterInfo() method to expose the parameters that will be passed. You need the following three parameters:

public String[][] getParameterInfo() {

    String[][] info = {

        {"message", "string", "message to display"},

        {"font", "string", "Font to use"},

        {"delay", "int", "delay in ms. between redrawing"},

    };

    return info;

}

First, get the parameter for the font. First, you add a String variable to the applet to hold the name called fontName. Then you get the parameter in the init() method:

fontName   = getParameter("font");

theFont = new Font(fontName, Font.PLAIN, 12);

Once you have the parameter, you can get that font instead of having it hardcoded. To do this, change Courier in the Font() constructor to fontName.

You do a similar thing for the message and for the delay:

delay  = Integer.valueOf(getParameter("delay")).intValue();

message = getParameter("message");

All the first line does is convert the String that getParameter() returns into an integer and stores it in delay. The second line gets the message to display.

You also need to change the sleep() method in the run() method to use delay:

Thread.sleep(delay);

You're finished!. The applet has the same functionality as in the last version, but with the added benefit of allowing parameters to be passed. Figure 12.2 shows the applet, and Listing 12.4 is the source code for the final version of the applet.

Figure 12.2. The final version of the TickerTape applet.

import java.awt.*;

import java.lang.*;

public class Ticker4 extends java.applet.Applet implements Runnable {

    String message;

    String fontName;

    int width, height;

    int fontHeight;

    int messageWidth;

    int currentPos;

    int delay;

    Font theFont;

    Thread animator;

    public String[][] getParameterInfo() {

        String[][] info = {

            {"message", "string", "message to display"},

            {"font", "string", "Font to use"},

            {"delay", "int", "delay in ms. between redrawing"},

        };

        return info;

    }

    public void init() {

        height = size().height;

        width  = size().width;

        currentPos = width;

        fontName   = getParameter("font");

        theFont = new Font(fontName, Font.PLAIN, 12);

        fontHeight = theFont.getSize();

        delay  = Integer.valueOf(getParameter("delay")).intValue();

        message = getParameter("message");

        messageWidth = getFontMetrics(theFont).stringWidth(message);

    }

    public void start() {

        animator = new Thread(this);

        animator.start();

    }

    public void stop() {

        if (animator != null) animator.stop();

            animator = null;

    }

    public void run() {

        while (animator != null) {

            try {

                Thread.sleep(delay);

            } catch (InterruptedException e) {

                break;

            }

            currentPos = currentPos - 5;

            if (currentPos < -messageWidth)

                currentPos = width;

            repaint();

        }

    }

    public void paint(Graphics g) {

        g.setFont(theFont);

        g.drawString(message, currentPos, height/2 - fontHeight/2);

    }

}

The HTML for this applet is as follows:

<APPLET CODE="Ticker4.class" WIDTH=300 HEIGHT=40>

<param name="message" value="Hello, and welcome to my applet.">

<param name="font" value="Courier">

<param name="delay" value="175">

</APPLET>

Summary

This chapter went step-by-step from an applet that just displayed the word Test on-screen all the way to a TickerTape applet that takes parameters and animates text. Although this process wasn't trivial, it was surprisingly straightforward compared to other languages.

This applet would be a great to modify yourself. Change it to allow the text to scroll from right to left instead of left to right. Put in a parameter to allow the text to be resized. The possibilities are endless!

Previous Page TOC Next Page