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.
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.
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.
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:
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.
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 |
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.
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.
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.
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.
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 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.
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.
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.
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 appletsthere'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!