Chapter 30

Sample Applets


CONTENTS


This chapter is the first of six chapters that cover applet programming. It introduces you to Java applets by way of several examples that illustrate different aspects of the classes and methods used to implement simple applets. Its purpose is to quickly get you up to speed in developing applets. Subsequent chapters fill in the details of the classes and methods that are introduced here. You should approach this chapter by trying to get a good general understanding of what is going on in the sample applets without dwelling on the details. You will learn the details when you study them in later chapters. This chapter assumes that you have covered the window-programming examples in Part IV, "Window Programming."

Hello Web!

The first program that you wrote in Chapter 4, "First Programs: Hello World! to BlackJack," was the HelloWorldApp program. It displayed the traditional Hello World! text to the console window. The main purpose of the program was to show you how to develop a simple Java program that actually produced some noticeable effect. The same rationale applies to the HelloWeb applet that you'll develop shortly. The program shows you how to write a simple applet that displays the text Hello Web! to the browser window. The code for the HelloWeb applet is shown in Listing 30.1.


Listing 30.1. The source code of the HelloWeb applet.

import java.applet.*;
import java.awt.*;

public class HelloWeb extends Applet {
  public void paint(Graphics g) {
  g.setFont(new Font("TimesRoman",Font.BOLD+Font.ITALIC,36));
  g.setColor(Color.blue);
  g.drawString("Hello Web!",10,80);
 }
}


Create the HelloWeb.java file in the \java\jdg\ch30 directory and compile it using javac HelloWeb.java.

In order to run the HelloWeb applet, you need to include it in a Web page. Using a text editor, create the HTML file shown in Listing 30.2 and save it as \java\jdg\ch30\helloweb.htm.


Listing 30.2. The helloweb.htm file.

<HTML>
<HEAD>
<TITLE>Hello Web!</TITLE>
</HEAD>
<BODY>
<APPLET CODE="HelloWeb.class" WIDTH=200 HEIGHT=200>
[HelloWeb applet]
</APPLET>
</BODY>
</HTML>


Now open the helloweb.htm file with your Java-enabled browser. I use Netscape 2.0; my browser display appears as shown in Figure 30.1.

Figure 30.1 : The hell.htm file, as displayed by a browser.

Although this example is rather trivial-you could have created the same results just using HTML-it provides a basic starting point from which to embark on your study of applets. Let's cover the HelloWeb.java applet first, and then examine the helloweb.htm file.

The HelloWeb applet, like all Java applets, extends the Applet class or one of its subclasses. HelloWeb consists of a single method-the paint() method, which you encountered several times in Part IV. Because the Applet class is a subclass of Panel, many of the window-programming techniques that you studied in Part IV come into play. The paint() method is passed a Graphics object that it uses to paint the screen. In this case, I set the current font to a bold, italic, 36-point TimesRoman font and set the drawing color to blue. I then use the drawString() method of the Graphics class to draw the text Hello Web! on the screen at position 10,80.

The helloweb.htm file is the Web page that is used to contain the HelloWeb applet. Web pages are written in the Hypertext Markup Language (HTML). Learning HTML, in itself, is the subject of many books. Here I'll use a common HTML document to hold the applets and not go off on a tangent about learning HTML.

The helloweb.htm file begins and ends with the <HTML> and </HTML> tags. These tags signify the start and finish of an HTML file. The HTML file consists of a head and a body. The head is contained between the <HEAD> and </HEAD> tags. Within the head is the title, which is displayed at the top of the browser window. The title is contained within the <TITLE> and </TITLE> tags.

The body of an HTML file is where all the Web page descriptions are located. The body is enclosed within the <BODY> and </BODY> tags. The body of this document, and of those used in the other examples of this chapter, consists of a single applet. The applet is denoted by the <APPLET> and </APPLET> tags.

Within the opening <APPLET> tag are several attributes. The CODE attribute has the value HelloWeb.class, which identifies the class file for this applet. Its WIDTH and HEIGHT attributes specify the dimensions of the applet, as displayed on the browser window.

The text [HelloWeb applet], appearing between the <APPLET> and </APPLET> tags, is displayed by browsers that are not capable of loading Java applets.

An Audio Player

The Applet class, as small as it is, provides a few very useful features for developing attractive Web pages. Among these are the capability to load and play audio files and to easily load and display images. The AudioPlayer applet, developed in this section, shows off these features. The source code for the AudioPlayer applet is shown in Listing 30.3.

Note
You need a sound board and speakers to run this applet.


Listing 30.3. The source code of the AudioPlayer applet.

import java.applet.*;
import java.awt.*;
import java.net.*;

public class AudioPlayer extends Applet {
 AudioClip music;
 Image background;
 public void init() {
  try {
   music = getAudioClip(new URL("file:///spacemusic.au"));
   background = getImage(new URL("file:///space.gif"));
  }catch (MalformedURLException ex) {
  }
  setLayout(new BorderLayout());
  Panel buttons = new Panel();
  buttons.add(new Button("Play"));
  buttons.add(new Button("Stop"));
  buttons.add(new Button("Loop"));
  add("South",buttons);
 }
 public void stop() {
   music.stop();
 }
 public void paint(Graphics g) {
  g.drawImage(background,0,0,this);
 }
 public boolean handleEvent(Event event) {
  if(event.target instanceof Button) {
   if("Play".equals(event.arg)) {
    music.play();
    return true;
   }else if("Stop".equals(event.arg)) {
    music.stop();
    return true;
   }else if("Loop".equals(event.arg)) {
    music.loop();
    return true;
   }
  }
  return false;
 }
}


Create and compile AudioPlayer.java in the same manner as you did HelloWeb.java. The HTML file that is used to display the applet is shown in Listing 30.4. Note that the CODE, WIDTH, and HEIGHT attributes of the applet have been changed. You will need two additional files to run the applet. Copy the spacemusic.au file from the \java\demo\Animator\audio directory to your \java\jdg\ch30 directory. This is an audio file that is supplied with the Java Developer's Kit. It contains music that is described as "space music." Also copy the space.gif file from the \jdg\ch30 directory of the CD-ROM to your \java\jdg\ch30 directory.


Listing 30.4. The audio.htm file.

<HTML>
<HEAD>
<TITLE>Audio Player</TITLE>
</HEAD>
<BODY>
<APPLET CODE="AudioPlayer.class" WIDTH=300 HEIGHT=350>
[AudioPlayer applet]
</APPLET>
</BODY>
</HTML>


Open the audio.htm file with your Java-enabled browser. Your screen display should look similar to the one shown in Figure 30.2.

Figure 30.2 : The audio.htm file, as displayed by a Web browser.

Your browser loads the audio.htm file and then the AudioPlayer.class file. The applet itself loads the background image and an audio file. To play the audio file, click on the Play button. The space music will be played, using your sound board and speakers. When the end of the music file is reached, the sound ends. If you click on the Loop button, the music will be continuously played in a never-ending fashion. Clicking on the Stop button causes the music to cease.

The AudioPlayer class, for all its additional capabilities, is only slightly longer than the HelloWeb class. It declares two field variables, music and background, which are used to hold the audio file and background image. The music variable is declared as type AudioClip, which is an interface defined in the Java.applet package.

The AudioPlayer class contains four access methods: init(), stop(), paint(), and handleEvent(). You are already familiar with the purpose of paint() and handleEvent() from Part IV.

The init() method is invoked by the browser's runtime system when an applet is initially loaded. It performs any initialization required before the main part of the applet is executed. The stop() method is invoked when the execution of an applet is terminated as the result of an applet's Web page no longer being displayed by the browser. You never need to invoke init() or stop() directly. They are invoked by the runtime system.

The init() method of AudioPlayer begins by loading the audio and image files. The getAudioClip() method of the Applet class loads an audio file that is referenced by an URL. The file:///spacemusic.au URL is used to load the spacemusic.au file from the directory in which the audio.htm and AudioPlayer.class files are located. The file:///space.gif URL is used in a similar manner.

After the audio and image files are loaded, the layout of the applet is set to a BorderLayout object. A Panel object is created and assigned to the buttons variable. The Play, Stop, and Loop buttons are created and added to the buttons panel. The buttons panel is then added to the bottom of the applet display area.

The stop() method uses the stop() method of the AudioClip interface to stop the music when the applet is no longer being displayed by the browser.

The paint() method draws the space.gif image assigned to the background variable on the Graphics context of the applet's display area.

The handleEvent() method handles the three prominent events associated with the applet. These events are the clicking of the Play, Stop, and Loop buttons. When the Play button is clicked, the play() method of the AudioClip interface is invoked to play the audio clip. When the Stop button is clicked, the stop() method of the AudioClip interface is invoked to stop the music. Finally, when the Loop button is clicked, the loop() method of the AudioClip interface is invoked to cause the music to be played in a never-ending, looping fashion.

BlackJack Revisited

From the previous example, you have probably surmised that it's very easy to convert Java window programs to applets. This is possible because of the fact that an applet is nothing more than a souped-up panel. It is also relatively easy to convert console programs to applets.

One of the first Java programs that you wrote, way back in Chapter 4, was the BlackJackApp program. This program allowed you to play blackjack on the console display. Because you used a class- and object-oriented approach to developing BlackJackApp (even before you were formally introduced to classes and objects), this program is relatively easy to convert to a simple applet. The source code of the converted applet is shown in Listing 30.5.


Listing 30.5. The source code of the Blackjack.java applet.

import java.applet.*;
import java.awt.*;
import java.util.Random;

public class Blackjack extends Applet {
 static final int BET = 0;
 static final int PLAY = 1;
 static final int DEALER = 2;
 int state = BET;
 int money = 1000;
 int bet = 0;
 Deck deck = new Deck();
 Hand playersHand;
 Hand dealersHand;
 Label topLine = new Label("Welcome to Blackjack!",Label.CENTER);
 Label totalLine = new Label("You have $1000.",Label.CENTER);
 Label dealersLabel = new Label("Dealer's Hand",Label.CENTER);
 Label playersLabel = new Label("Your Hand",Label.CENTER);
 TextArea dealerText = new TextArea(9,20);
 TextArea playerText = new TextArea(9,20);
 Button hitButton = new Button("Hit");
 Button stayButton = new Button("Stay");
 Label betLabel = new Label("Enter your bet: ",Label.RIGHT);
 TextField betField = new TextField();
 GridBagLayout gridbag = new GridBagLayout();;
 GridBagConstraints constraints = new GridBagConstraints();
 public void init() {
  setLayout(gridbag);
  constraints.fill = GridBagConstraints.BOTH;
  addComponent(topLine,0,0);
  addComponent(totalLine,0,1);
  addComponent(dealersLabel,1,0);
  addComponent(playersLabel,1,1);
  dealerText.setEditable(false);
  playerText.setEditable(false);
  addComponent(dealerText,2,0);
  addComponent(playerText,2,1);
  addComponent(hitButton,3,0);
  addComponent(stayButton,3,1);
  addComponent(betLabel,4,0);
  addComponent(betField,4,1);
 }
 void addComponent(Component c,int y,int x) {
  constraints.gridx = x;
  constraints.gridy = y;
  gridbag.setConstraints(c, constraints);
  add(c);
 }
 public boolean handleEvent(Event event) {
  if(event.target instanceof TextField && event.id == Event.ACTION_EVENT) {
   if(state == BET){
    updateBet();
    return true;
   }
  }else if(event.target instanceof Button && event.id == Event.ACTION_EVENT) {
   if(state == PLAY) {
    if("Hit".equals(event.arg)) {
     playersHand.addCard(deck.deal());
     playersHand.show(playerText,false);
     if(!playersHand.under(22)) state = DEALER;
    }else if("Stay".equals(event.arg)) state = DEALER;
    if(state == DEALER) {
     while(dealersHand.mustHit())
      dealersHand.addCard(deck.deal());
     dealersHand.show(dealerText,false);
     showResults();
    }
   }
  }
  return false;
 }
 public void updateBet() {
  betField.setEditable(false);
  betLabel.setText("Bet: ");
  try {
   Integer i = new Integer(betField.getText());
   bet = i.intValue();
  } catch (NumberFormatException ex) {
   bet = 1;
  }
  betField.setText(String.valueOf(bet));
  initialDeal();
  if(playersHand.blackjack()) playerWins();
  else state = PLAY;
 }
 void initialDeal() {
  playersHand = new Hand();
  dealersHand = new Hand();
  for(int i = 0;i<2;++i) {
   playersHand.addCard(deck.deal());
   dealersHand.addCard(deck.deal());
  }
  dealersHand.show(dealerText,true);
  playersHand.show(playerText,false);
 }
 void openBetting() {
  betLabel.setText("Enter your bet: ");
  betField.setText("");
  betField.setEditable(true);
  state = BET;
 }
 void playerWins() {
  money += bet;
  topLine.setText("Player wins $"+bet+".");
  totalLine.setText("You have $"+money+".");
  openBetting();
 }
 void dealerWins() {
  money -= bet;
  topLine.setText("Player loses $"+bet+".");
  totalLine.setText("You have $"+money+".");
  openBetting();
 }
 void tie() {
  topLine.setText("Tie.");
  totalLine.setText("You have $"+money+".");
  openBetting();
 }
 void showResults() {
  if(playersHand.busted() && dealersHand.busted()) tie();
  else if(playersHand.busted()) dealerWins();
  else if(dealersHand.busted()) playerWins();
  else if(playersHand.bestScore() > dealersHand.bestScore()) playerWins();
  else if(playersHand.bestScore() < dealersHand.bestScore()) dealerWins();
  else tie();
 }
}

class Deck {
 // Variable declarations
 int cards[]; // Array of 52 cards
 int topCard; // 0-51 (index of card in deck)
 Random random;

 // Method declarations
 public Deck() { // Constructor
  cards = new int[52];
  for(int i = 0;i<52;++i) cards[i] = i;
  topCard = 0;
  random = new Random();
  shuffle();
 }

 public void shuffle() {
  // Repeat 52 times
  for(int i = 0;i<52;++i) {
   // Randomly exchange two cards in the deck.
   int j = randomCard();
   int k = randomCard();
   int temp = cards[j];
   cards[j] = cards[k];
   cards[k] = temp;
  }
 }

 int randomCard() {
  int r = random.nextInt();
  if(r<0) r = 0-r;
  return r%52;
 }

 Card deal() {
  if(topCard>51) {
   shuffle();
   topCard = 0;
  }
  Card card = new Card(cards[topCard]);
  ++topCard;
  return card;
 }
} // End of Deck class

class Hand {
 // Variable declarations
 int numCards;
 Card cards[];
 static int MaxCards = 12;

 //Method declarations
 public Hand() { // Constructor
  numCards = 0;
  cards = new Card[MaxCards];
 }

 void addCard(Card c) {
  cards[numCards] = c;
  ++numCards;
 }

 void show(TextArea t,boolean hideFirstCard) {
  String results = "";
  for(int i = 0;i<numCards;++i) {
   if(i == 0 && hideFirstCard) results += "Hidden\n";
   else results += cards[i].value+" of "+cards[i].suite+"\n";
  }
  t.setText(results);
 }

boolean blackjack() {
 if(numCards == 2) {
  if(cards[0].iValue == 1 && cards[1].iValue == 10) return true;
  if(cards[1].iValue == 1 && cards[0].iValue == 10) return true;
 }
 return false;
}

boolean under(int n) {
 int points = 0;
 for(int i = 0;i<numCards;++i) points += cards[i].iValue;
 if(points<n) return true;
 else return false;
}

int bestScore() {
 int points = 0;
 boolean haveAce = false;
 for(int i = 0;i<numCards;++i) {
  points += cards[i].iValue;
  if(cards[i].iValue == 1) haveAce = true;
 }
 if(haveAce) {
  if(points+10 < 22) points += 10;
 }
 return points;
}

boolean mustHit() {
 if(bestScore()<17) return true;
 else return false;
}

 boolean busted() {
  if(!under(22)) return true;
  else return false;
 }
} // End of Hand class

class Card {
 // Variable declarations
 int iValue; // Numeric value corresponding to card.
 String value; // "A" "2" through "9" "T" "J" "Q" "K"
 String suite; // "S" "H" "C" "D"

 // Method declarations
 public Card(int n) { // Constructor
  int iSuite = n/13;
  iValue = n%13+1;
  switch(iSuite) {
   case 0:
    suite = "Spades";
    break;
   case 1:
    suite = "Hearts";
    break;
   case 2:
    suite = "Clubs";
    break;
   default:
    suite = "Diamonds";
  }
  if(iValue == 1) value = "Ace";
  else if(iValue == 10) value = "Ten";
  else if(iValue == 11) value = "Jack";
  else if(iValue == 12) value = "Queen";
  else if(iValue == 13) value = "King";
  else value = Integer.toString(iValue);
  if(iValue>10) iValue = 10;
 }

 int getValue() {
  return iValue;
 }
} // End of Card class


Create and compile Blackjack.java in the same manner as the previous examples. The HTML file that is used to display the applet is shown in Listing 30.6. Note that the CODE, WIDTH, and HEIGHT attributes of the applet have been changed.


Listing 30.6. The blackjack.htm file.

<HTML>
<HEAD>
<TITLE>Blackjack</TITLE>
</HEAD>
<BODY>
<APPLET CODE="Blackjack.class" WIDTH=400 HEIGHT=400>
[Blackjack applet]
</APPLET>
</BODY>
</HTML>


Open the blackjack.htm file with your browser. Your browser should display the applet, as shown in Figure 30.3.

Figure 30.3 : The Black initial display.

The top text line in the applet welcomes you to the game and tells you that you have $1000 to gamble. The Dealer's Hand and Your Hand text boxes are initially blank. Below them are the Hit and Stay buttons, which are initially disabled. To start the game, enter a bet, as shown in Figure 30.4.

Figure 30.4 : Entering a bet.

When you enter a bet, the label to the right of the text field changes from Enter your bet: to Bet:. The text field is grayed and can no longer be edited. This prevents you from changing your bet during the play of a hand.

The initial hands for the dealer and the player are displayed after the bet is entered. The first card of the dealer is hidden. You can now use the Hit and Stay buttons to obtain more cards or to stay with what you were dealt and let the dealer draw. Click on the Hit button until you are finished drawing cards. If you bust (go over 21), your hand will be finished and the dealer will draw his cards.

After you click Stay or bust, the dealer draws his hand and a win, loss, or tie results. Your available money is then updated based on your winnings. The Blackjack applet is more forgiving than the BlackJackApp program in Chapter 4: You can continue playing after you've run out of money. You can even bet $0 or negative values.

Go ahead and play the game for a while to become familiar with its operation.

Although the Blackjack applet may appear to be quite large, it reuses all of the classes developed for the original BlackJackApp program, with minor modifications.

The Blackjack program declares 21 field variables. Most of these are used to create its GUI interface. The BET, PLAY, and DEALER constants are used to maintain the state of the user's interaction with the game. The state variable is set to the initial BET state.

The money, bet, deck, playersHand, and dealersHand variables are declared and used in the same manner as in the BlackJackApp program.

Five labels are created and assigned to the topLine, totalLine, dealersLabel, playersLabel, and betLabel variables. The topLine and totalLine variables are displayed at the top of the applet. They display the welcome string and the amount of money that the player has available. The dealersLabel and playersLabel variables are used to label the text areas containing the dealer's and user's hands. The betLabel variable prompts the user to enter his or her bet.

The dealerText and playerText variables are used to refer to the TextArea objects that display the hands of the dealer and player. The betField text field is used to enter and display the user's bet.

The hitButton and stayButton variables are used to draw another card or to turn the draw over to the dealer.

The gridbag and constraints variables refer to the GridBagLayout and GridBagConstraints objects used to lay out the screen's display.

The init() method sets the layout to a GridBagLayout, invoking the addComponent() method to add the GUI components to the applet. The addComponent() method adds a component to an x,y location within the gridbag layout.

The handleEvent() method handles the user-interface events that drive the operation of the applet. It handles the event generated when the user enters text in the Bet: text field by invoking the updateBet() method. This method then sets up the rest of the applet's operation. The handleEvent() method handles the clicking of the Hit and Stay buttons by first making sure that the applet is in the Play state. This prevents the user from clicking on these buttons at arbitrary times during the applet's execution.

The Hit button is handled by adding a card to the player's hand, using the same approach as in BlackJackApp. The show() method of the Hand class is modified to display the player's hand to the TextArea object referenced by the playerText variable. If the user busts, the state is set to DEALER, preventing the user from drawing another card.

The Stay button is handled by setting the state variable to DEALER.

When the state is set to DEALER, the dealer is dealt additional cards using the same code as in BlackJackApp. The showResults() method is modified to update the applet's display labels and not the console window.

The updateBet() method is a new method that is added to support the applet conversion. It disables editing of the text field and changes the Label object referenced by the betLabel variable from Enter your bet: to Bet:. It then checks the bet for a valid value and redisplays it in the disabled text field. The initialDeal() method is invoked to deal an initial hand to the player and dealer. If the user is dealt blackjack, the hand is ended and the user is given his winnings. Otherwise, the state variable is set to play to enable the operation of the Hit and Stay buttons.

The initialDeal() method remains unchanged from the original BlackJackApp program.

The openBetting() method is used to change to the initial BET state. It sets the Enter your bet: prompt and enables the text field to be edited.

The playerWins(), dealerWins(), and tie() methods have been modified to display their results within the applet instead of on the console window. They also invoke the openBetting() method to return to the BET state.

The showResults() method remains unchanged from the original BlackJackApp program.

No changes were made to the Deck or Card classes. The show() method of the Hand class was modified to display the dealer's and player's hands to the TextArea objects.

The changes required to convert from the original BlackJackApp program to the Blackjack applet were minimal. This illustrates the power of object-oriented programming and the reusability of the components that are created using this approach.

Summary

This chapter introduces you to applet programming by way of several sample programs that illustrate the classes and methods used to implement simple applets. You should now be comfortable with applets, especially if you have worked through the window programming examples of Part IV. Chapter 31, "Developing Applets," lays the framework for developing applets by expanding on the basics introduced here.