Previous Page TOC Next Page


— 13 —
The SlideShow Applet

You started with the basics of the Java language in Chapter 7 and moved on through the basics of Java syntax, the Applet Package, and the AWT. Now with all of those tools available to use, you have everything you need to design a full-scale applet, complete with a user interface. The applet that this chapter discusses is a SlideShow applet. This SlideShow applet has the following features:

This chapter discusses all the issues associated with designing the applet, which are similar to the that you will face when you design your own applets. This example should help you gain a better understanding of how careful planning can help make coding your applets easier and help you in the long run.

Design Issues

Before you even start coding anything, sit down and map out the specifics of your applet, and consider some of the issues that will be involved. Time invested in the planning stage will help you save time in the coding and give you some starting points should you run into trouble when you are designing your applet. So what are some of the issues that you have to deal with in an applet like the SlideShow applet? The following is a list of questions you need to answer:

Each of these broad issues raises some major issues along the way that you'll need to deal with, but now that you have some broad categories, you can start mapping out your applet.

File Support

The types of files that your applets can support are limited to the types of file support that are inherent in Java. After all, applets leverage the browser for image file support, so you have to work within that context when working with files. Currently, applets support two types of image files, GIF and JPEG. So even if you wanted to support more file types, you really couldn't without programming some very difficult image handling code in Java. Because these types also happen to be the standard image file formats on the Web, they are probably good choices anyway, so you're set for images.

As luck would have it, your sound choices are very narrow as well. Java only supports the AU sound format. So you can pretty much call this a moot issue as well. See how easy that can be! Unfortunately, not all of your decisions will be so easy.

User Parameters

Because this applet is a SlideShow, you want to allow users to be able to specify the images that they want shown in the applet. So you need to provide some mechanism for allowing users to set a parameter that specifies what image files to use. There are several ways you could do this:

  1. You could have the applet take any images that are in a particular directory. This option would be easy for the user, but would involve writing some complex code, as you would need to scan all of the files in the directory, make sure they were all valid image files, and then deal with a wide variety of filenames.

  2. You could have the applet read in a configuration file that was a list of the filenames to be used. This option would be similar to using a directory, and you would still have to deal with different filenames.

  3. You could have a parameter for each image. This option would allow the user to specify each image file individually, but it would also mean you would have to set a fixed number of images beforehand, which would limit the applet significantly.

  4. You could also have the user specify a base name for an image and then number each of his images so the filenames were sequential. That would mean a little more work for the user, but then you could allow the applet to contain the exact number of images selected by the user and have a flexible applet.

The SlideShow applet uses the fourth option. In this scheme, the user selects a base name for the image, such as myImage, and the images that the user wants to include in the SlideShow are named sequentially, such as myImage0.gif, myImage1.gif, myImage2.gif, and so on. This scheme means the user needs to rename some images, but it makes the parameters very straightforward and does not limit the total number of images.

You now know several parameters that you will need to include in the applet: the imagefile base name, the total number of images, and the image extension (GIF or JPG).

Now that you have the image parameters selected, you also need an audio parameter, the sound file name. Because the soundtrack is a background track, you only need to worry about one sound file, but you should have a sound file parameter so users can specify a sound file.

Some other user parameters should be added to the applet for more functionality. For example, you want to have a specified delay interval for the automatic slide show, and you also want to allow the user to set the caption for the applet. After you add those parameters, you have a list of the parameters (see Table 13.1) you will need to specify and deal with in the applet.

Parameter 


Function


imagefile

Base image filename (picture, for example)

type

Image file type(GIF, JPG, and so on)

images

Total number of image files

soundfile

Sound file name (example: audio.au)

delay

Delay in between images (in ms.)

caption

Title of the slide viewer

The parameters are a user interface of sorts; they do allow the user to have some input as to the function of the applet, but this applet has an actual user interface component as well.

User Interface

Because you want to give the SlideShow applet a fully functional user interface, you need to think about what kinds of controls you will need and how you want those controls to function. Your applet does not need too many controls, but there are some that would add functionality to the applet and make it a little more user-friendly:

When you start to think about the user interface and adding user controls to your applets, you also need to consider how those controls should function and what would be most intuitive for the user. For example, for the Forward and Backward button you could have a toggle switch that changed the direction, or you could have a dial that you set to forward or backward. But having two distinct buttons, a forward button on the right and a backward button on the left, fits well with the image most people have of how slide projectors work. Additionally, you want users to be able to advance through the images on their own, and a button implies that level of functionality. In many cases, you will find that the simplest approach is often the best. In this case, a complex interface would have been silly and would have detracted from the value of the applet.

Checkboxes are used for the Automatic SlideShow option and the Mute option. The checkboxes are a simple way to indicate that a feature is on or off, and they provide a switching mechanism. Now you have the elements of the user interface, listed in Table 13.2, and you are ready to move on to the last stage of planning the applet.

Element


Function


Button 1

A button to move the images forward one at a time

Button 2

A button to move the images backward one at a time

Checkbox 1

A checkbox to turn off and on the Automatic SlideShow

Checkbox 2

A checkbox to mute the soundtrack

Starting the Applet

You are almost done with the planning stage of the applet. You have outlined the features you want the applet to include, the user interface for the applet, and how the user can configure different aspects of the applet. You only have a few more details to hammer out before you start coding the applet.

First, you need to decide how you want the applet to begin. Because this applet is designed to be somewhat like a photo gallery, you might decide that you want the applet to start off with sound and in the Automatic SlideShow mode. Starting the applet this way has two practical implications:

Later sections explain how you deal with each of these issues as you code the actual applet, but it's a good idea to identify such issues beforehand so you can plan ahead and begin thinking about possible solutions.

Coding the Applet

Now that you have mapped out some of the issues surrounding your applet, go ahead and start to code it. The beginnings of the code can be found in Listing 13.1.

/*

The SlideShow Applet

*/

import java.awt.*;

import java.lang.*;

import java.applet.*;

import java.net.URL;

public class SlideShow extends Applet implements Runnable {

    MediaTracker LoadImages;

    Image slides[];

    String imagefile, soundfile, type, caption;

    Thread AutoShow;

    int images, delay;

    int index = 0;

    Button forward, backward;

    Checkbox auto, sound;

    Label title;

      AudioClip clip;

    Panel marquee, control;

}

You start off with the standard range of import statements so that you have all the methods you will need at our disposal. Next, you declare the class, SlideShow. Notice that the code has implements Runnable after extends Applet. You are going to need to incorporate threads into this applet, so in addition to having the applet be an extension to the Applet class, you need to say that it implements the features or Runnable, which allows you to start a thread.

Next, you declare the variables. Let's take a look at each one of these and what they do:

The foundation of the applet is now built. You have declared all the variables and instances that you will be using and are ready to start reading in the parameters and loading the images.

Parsing the Parameters

The code that you are going to use to read in the parameters should look very similar to the code used to read in parameters in the Speaker applet in Chapter 10 and the TickerTape applet in Chapter 12. The methodology for reading in parameters doesn't vary much from applet to applet. In the SlideShow applet, you use the getParameterInfo() method to set up what parameters you will accept and what their function is, and then you use the getParameter() method to read the parameter values from the HTML file. The code used to accomplish this is in Listing 13.2.

public String[][] getParameterInfo() {

    String[][] info = {

        {"caption", "string", "Title of the Slide Viewer"},

        {"imagefile", "string", "Base Image File Name (ex. picture)"},

            {"soundfile", "string", "Sound File Name (ex. audio.au)"},

            {"images", "int", "Total number of image files"},

            {"delay", "int", "Delay in between images (in ms.)"},

            {"type", "string", "Image File Type(gif, jpg, etc)"},

    };

    return info;

}

public void init() {

    LoadImages = new MediaTracker(this);

    caption = getParameter("caption");

    imagefile = getParameter("imagefile");

    soundfile = getParameter("soundfile");

    type    = getParameter("type");

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

    slides = new Image[images];

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

}

Now that you have the parameters under control, you can move on to loading the images.


MediaTracker

When you are coding an application that uses several media files, such as image files, you want to make sure that your applet has loaded all the necessary files before it begins to execute. If an animation applet, for example, requires a series of images for the animation, you would not want the animation to start until the images were all loaded. Otherwise, the animation might appear jerky, or, even worse, it might skip frames and produce an incomplete animation. So if you have to load more than one or two media files, it would be very helpful to have a way to keep track of the loading status of those files and keep the applet from executing until those files have been loaded. Fortunately, Sun has provided just such a mechanism: the MediaTracker class.

You can find the full documentation for the MediaTracker class at the following URL:

http://www.javasoft.com/JDK1.0/api/java.awt.MediaTracker.html

This documentation details all the functionality that is provided with the MediaTracker class. As the name implies, MediaTracker is designed to be used with any kind of media file, sound or images. However, the JDK 1.0 release only supports image files. Nonetheless, the MediaTracker is a great way to make sure your files are loaded and ready to be used before your applet begins.

Some of the more useful methods from the MediaTracker class are described in the following paragraphs:

The addImage() method enables you to add an image file to the list of image files you want to keep track of.

The checkAll() and checkID() methods enables you to check whether your images have been loaded; you can either check for a specific image ID or check all the images.

The getErrorsAny() and getErrorsID()methods return any errors that have been generated during the image loading process. You can check errors for specific image IDs or all images.

The statusAll() and statusID() methods return the loading status of images being loaded. They can check the status of a particular image by image ID or all images currently in the list.

The waitForAll() and waitForID() methods start loading images that have been added using the addImage() method. You can invoke them to load a specific ID or all images and they will wait until the image(s) are loaded before allowing the applet to proceed.

Basically, you create an instance of the MediaTracker class that tracks your image files. You can then use some MediaTracker-specific methods to track each image as it is read into memory. MediaTracker notifies your applet when errors occur in loading the images and also lets your applet know when all the images are completely loaded so that the applet may execute. All in all, MediaTracker is a very useful class.

Loading the Images and MediaTracker

As mentioned before, you want to start the SlideShow applet in the Automatic SlideShow mode, so you need to make sure that the images you are going to be using have been loaded before you start running the SlideShow. To stay informed about the images, you are going to use a class called MediaTracker (see the preceding sidebar). The MediaTracker class enables you to monitor the loading status of your images and makes sure that you do not progress until all of the images have been loaded. In the code, this class looks like Listing 13.3.

LoadImages = new MediaTracker(this);

for (int i = 0; i < images; i++) {

    slides[i] = getImage(getDocumentBase(), imagefile + i + "." + type);

        if (i > 0)

            LoadImages.addImage(slides[i], 1);

        else

            LoadImages.addImage(slides[i], 0);

}

try {

    LoadImages.waitForID(0);

} catch (InterruptedException e) {

    System.out.println("Image Loading Failed!");

}

showStatus(imagefile + " Images Loaded");

index = 0;

repaint();

LoadImages.checkID(1,true);

This code starts off by declaring a new instance of the MediaTracker class called LoadImages. Then you use a for loop to add each of the user's images to the list of images to be tracked. Once all the images have been added, you use the waitForID() method to start loading the images. The try and catch terms are Java methods for handling errors. Finally, you use the checkID() method to see whether all the images are loading, and then the program can proceed.

Creating the User Interface

While you are still in the init() method, you can create the graphical user interface for this applet. When creating any graphical user interface, creating a usable layout can be quite a complex task. For example, the SlideShow applet uses three separate layouts embedded in each other to create the final GUI for the applet (see Figure 13.1).

You start out with a generic border layout, which is based on placing components at the compass points. You are only going to add an element to the South, which is displayed at the bottom of the screen. The layout you add to the South is called marquee because that's where all the applet information will be displayed. The marquee panel is also based on a border layout. This way, you can add the title to the North, and it will be unaffected by any of the user controls. Finally, you are going to create a new panel called control where you will place the buttons and so forth. This layout uses a simple flow layout so the elements are displayed left to right. You will then add the control panel to the South of the marquee panel and be left with the final output (see Figure 13.2). The code to produce this output can be found in Listing 13.4.

Figure 13.1. The panels used for the SlideShow applet.

Listing 13.4. Establishing a user interface.

setLayout(new BorderLayout());

forward = new Button("Next");

backward = new Button("Previous");

auto = new Checkbox("AutoCycle Images");

auto.setState(true);

sound = new Checkbox("Sound On");

sound.setState(true);

title = new Label(caption);

Panel marquee = new Panel();

marquee.setLayout(new BorderLayout());

marquee.add("North", title);

Panel control = new Panel();

control.setLayout(new FlowLayout());

control.add(auto);

control.add(sound);

control.add(backward);

control.add(forward);

setFont(new Font("Helvetica", Font.BOLD, 18));

add("South", marquee);

setFont(new Font("Helvetica", Font.PLAIN, 14));

marquee.add("South", control);

}


Figure 13.2. Shows the final layout of the SlideShow applet elements.

Now that you have the layout established, the last thing you need to do is to make the elements you've just laid out functional. Do this by adding the event handling code in Listing 13.5 to the init() method.

Listing 13.5. Event handling in the SlideShow applet.

public boolean action(Event evt, Object what) {

    if (evt.target == sound) {

        if (sound.getState() == true)

            clip.loop();

        else

            clip.stop();

        return true;

    } else if (evt.target == backward) {

        if (index !=0) {

            index--;

            repaint();

        }

        return true;

    } else if (evt.target == forward) {

        index++;

        repaint();

        return true;

       } else

        return false;

In this code, you use a series of if and else if statements to monitor any events that might be generated by the applet, and compare those events to the layout elements. The Sound checkbox starts or stops the sound. The Forward and Backward buttons repaint the screen using the repaint() method and then set the index counter forward or backward as is appropriate.

Drawing the Images

Drawing the images on-screen in the applet can be a bit confusing. There are three methods that you are going to use to accomplish this task, paint(), repaint(), and update(). They all work together in an odd way (see Figure 13.3), each one calling the other. In the start() method, you are going to use repaint() to put an image on-screen. The repaint() method calls the update() method, not the paint() method as the name might imply. The update() method then clears the screen and calls paint(), which places the new image on-screen.

Figure 13.3. The relationship between the paint(), repaint(), and update() methods.

Redefining all these methods may seem a bit odd, but if you didn't, there would be problems when one method called the other, and you wouldn't see the slides. The following code redefines update() to paint the slide:

public void update(Graphics g) {

    try {

        paint;

    } catch (ArrayIndexOutOfBoundsException e) {

        if(index < 0)

            index = 0;

        else if (index > images)

            index = images;

        System.out.println("No more Images!");

    }

}

There really is not much to the update() method. Most of that code is just there in case you have an error. The try section accomplishes the work in the method, and the catch portion only exists to deal with an error if one is generated. (In this case, an error is generated if the user tries to advance past the beginning or end of the slides.)

You are also going to subclass the paint() method. There are two reasons for doing this. First, you use the paint() method to check for error messages from the MediaTracker and replace any bad images with a black screen. That way, you don't break the applet if an image is broken. Secondly, you need to tell the paint() method what image to place on the screen. You do this by calling up an image from the array of slides, using the [index] number to call up as specific image on-screen. The end result is a paint() method that replaces any broken images and shows the slides correctly:

public void paint(Graphics g) {

    if (LoadImages.isErrorAny()) {

        g.setColor(Color.black);

        g.fillRect(0, 0, size().width, size().height);

        return;

    }

    g.drawImage(slides[index], 0, 0, this);

    }

With those methods defined, you are almost out of the woods. Now you need to setup the thread that operates the Automatic SlideShow.

The AutoShow Thread

Because you want the applet to run in two modes, AutoShow mode and user-controlled mode, you need to have two different applets running. One lets the user control what images are on-screen, and the other automatically cycles through the images. Whenever you have two distinct functions like this, you should consider using a thread.

As mentioned in the TickerTape applet in Chapter 12, threads function like an applet within an applet, but they have their own distinct qualities. Because this example ( see Listing 13.6) is a standard thread, which doesn't do anything too fancy, it's defined in run():

Listing 13.6. The AutoShow thread.

public void run() {

    Thread running = Thread.currentThread();

    while (AutoShow==running) {

        try {

            Thread.sleep(delay);

        } catch (InterruptedException e) {

            break;

        }

        if (auto.getState() == true) {

            if (LoadImages.checkID(1,true))

                synchronized (this) {

            if (index == (slides.length - 1))

                index = 0;

            else

                index++;

            }

            repaint();

        }

    }

}

The code for the thread is not too extensive, but it does quite a bit. First, you need to let the applet know what thread is the current thread, and then you can refer to that thread as running. You actually start the thread from within the start() method, but this running label enables you to keep tabs on your thread a little better. You then use a while loop so that as long as the thread is running, it will carry out the rest of the instructions in the thread definition.

Inside the while loop, you first set the thread to sleep for a delay period. That delay period is the delay that you allowed the user to specify in the parameters as the time in between slides. The first thing the thread does is sleep for the delay whenever it is running, which is what creates the delay in between images. The thread completes its instructions, then sleeps for the delay, and then attempts to carry out its instructions again.

The instructions for the thread are contained in the if statements following the try. In these statements, the thread checks to see whether the Auto checkbox is checked. If it's not, the thread doesn't do anything. However, if it is checked, you are in the AutoShow mode, and the thread first checks to see that the images are loaded correctly, and then displays the first image. The thread also sets the index number to the next method, and then the whole loop process repeats for as long as the Auto checkbox is checked.

start() and stop()

Now that you have the AutoShow thread set up, you need to make sure that it starts and that the applet puts an image on-screen and begins to play the soundtrack. Use the repaint() method to show an image, start the clip playing, and launch the thread called AutoShow:

public void start() {

    images = 0;

    repaint();

    clip.loop();

    AutoShow = new Thread(this);

    AutoShow.start();

}

As always, the stop() method is just for cleanup. In particular, the stop() method for the SlideShow applet makes sure that the AutoShow thread is no longer running:

public void stop() {

    index = 0;

    repaint();

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

    AutoShow= null;

}

That's all there is to it! The code does seem like a lot when it's all broken up into sections, but the assembled final code is shown in Listing 13.7. When you see the code all together, you can see that you have a very functional robust applet with very little code.

/*

The SlideShow Applet

*/

import java.awt.*;

import java.lang.*;

import java.applet.*;

import java.net.URL;

public class SlideShow extends Applet implements Runnable {

    MediaTracker LoadImages;

    Image slides[];

    String imagefile, soundfile, type, caption;

    Thread AutoShow;

    int images, delay;

    int index = 0;

    Button forward, backward;

    Checkbox auto, sound;

    Label title;

      AudioClip clip;

    Panel marquee, control;

    public String[][] getParameterInfo() {

        String[][] info = {

            {"caption", "string", "Title of the Slide Viewer"},

            {"imagefile", "string", "Base Image File Name (ex. picture)"},

                  {"soundfile", "string", "Sound File Name (ex. audio.au)"},

                    {"images", "int", "Total number of image files"},

                  {"delay", "int", "Delay in between images (in ms.)"},

                  {"type", "string", "Image File Type(gif, jpg, etc)"},

        };

        return info;

    }

    public void init() {

    //Parse the parameters from the HTML File

    LoadImages = new MediaTracker(this);

    caption = getParameter("caption");

    imagefile = getParameter("imagefile");

    soundfile = getParameter("soundfile");

    type    = getParameter("type");

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

    slides = new Image[images];

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

    //Use MediaTracker to load the images

    for (int i = 0; i < images; i++) {

        slides[i] = getImage(getDocumentBase(), imagefile + i + "." + type);

            if (i > 0)

                LoadImages.addImage(slides[i], 1);

            else

                LoadImages.addImage(slides[i], 0);

    }

    try {

        LoadImages.waitForID(0);

    } catch (InterruptedException e) {

        System.out.println("Image Loading Failed!");

    }

    showStatus(imagefile + " Images Loaded");

    index = 0;

    repaint();

    LoadImages.checkID(1,true);

    clip = getAudioClip(getDocumentBase(), soundfile);

    //Create the SlideViewer layout

    setLayout(new BorderLayout());

    forward = new Button("Next");

    backward = new Button("Previous");

    auto = new Checkbox("AutoCycle Images");

    auto.setState(true);

    sound = new Checkbox("Sound On");

    sound.setState(true);

    title = new Label(caption);

    Panel marquee = new Panel();

    marquee.setLayout(new BorderLayout());

    marquee.add("North", title);

    Panel control = new Panel();

    control.setLayout(new FlowLayout());

    control.add(auto);

    control.add(sound);

    control.add(backward);

    control.add(forward);

    setFont(new Font("Helvetica", Font.BOLD, 18));

    add("South", marquee);

    setFont(new Font("Helvetica", Font.PLAIN, 14));

    marquee.add("South", control);

    }

    //Monitor Checkboxes and Buttons for Actions

    public boolean action(Event evt, Object what) {

        if (evt.target == sound) {

            if (sound.getState() == true)

                clip.loop();

            else

                clip.stop();

            return true;

        } else if (evt.target == backward) {

            if (index !=0) {

                index--;

                repaint();

            }

            return true;

        } else if (evt.target == forward) {

            index++;

            repaint();

            return true;

           } else

            return false;

    }

    public void start() {

        images = 0;

        repaint();

        clip.loop();

        AutoShow = new Thread(this);

        AutoShow.start();

    }

    public void stop() {

        index = 0;

        repaint();

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

        AutoShow= null;

    }

    public void run() {

        Thread running = Thread.currentThread();

        while (AutoShow==running) {

            try {

                Thread.sleep(delay);

            } catch (InterruptedException e) {

                break;

            }

                if (auto.getState() == true) {

                    if (LoadImages.checkID(1,true))

                        synchronized (this) {

                            if (index == (slides.length - 1))

                                index = 0;

                            else

                                index++;

                            }

                        repaint();

                    }

        }

       }

    //Update is called by repaint()

    public void update(Graphics g) {

        try {

            paint;

        } catch (ArrayIndexOutOfBoundsException e) {

            if(index < 0)

                index = 0;

            else if (index > images)

                index = images;

            System.out.println("No more Images!");

        }

    }

    //Paint the slide image on the screen

    //And Account for missing images

    public void paint(Graphics g) {

        if (LoadImages.isErrorAny()) {

            g.setColor(Color.black);

            g.fillRect(0, 0, size().width, size().height);

            return;

        }

        g.drawImage(slides[index], 0, 0, this);

    }

}

The HTML file for the SlideShow applet contains the following:

<html>

<applet code="SlideShow.class"  width=400 height=250>

<param name="caption" value="A Sample Photo Album">

<param name="imagefile" value="image">

<param name="soundfile" value="song.au">

<param name="images" value="5">

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

<param name="type" value="gif">

</applet>

</html>

Now you have a user-configurable SlideShow applet (see Figures 13.4 through 13.6) that uses user parameters, contains a graphical user interface, and implements threads. That is no small task, and in many programming languages, you could wade though several books before being able to accomplish such a task.

Figure 13.4. The SlideShow applet in action with the automatic show and sound enabled.

Figure 13.5. The SlideShow applet with sound disabled.

Figure 13.6. The SlideShow applet in user-controlled mode.

Summary

Now you have a solid background of Java from both a user's perspective and a programmer's perspective. You have a sense of Java's history and some ideas about what is possible with Java. Perhaps that's all you needed, and now you can go out and find applets to integrate with your Web pages and do so with a better understanding of what's happening behind the scenes. Maybe you've decided that you'd like to take your applets into your own hands and start working on projects of your own. Whatever direction you choose, the authors of this book hope we've provided you with the information necessary to get you excited about Java and make the best decision on how Java is going to work for you.

The applets that we've included in this book are not finished applets—there's no such thing as a finished applet. There is always room to improve them and add your own features. If you have ideas to make them even better applets, feel free to add you own code and make them function as you see fit. We hope you've enjoyed learning about Java and that you will find a place for it on your Web site. No matter what you choose to do with Java, have fun!

Previous Page TOC Next Page