Workshop: Displaying a Danger Message

You can use Java to present news headlines and other information in different ways. One special effect you might see on a web page or graphical user interface is text that fades to black. You also might see the reverse—text that brightens from black to white. This hour's workshop uses the Font and Color classes to create text that cycles in brightness from dark to bright. The text looks like an alert about impending danger, so the panel will be called Danger. To make the class more useful, the text of the warning will be set as an argument to a constructor. The text that will be used in this example warns of a "Core Breach in Sector 12," but you can substitute other threatening text of similar length.

By the Way

If you're at a secure place in your life right now and can't think of any warning message fraught with impending doom, feel free to choose one of the following:

  • "Mother-in-law wants to visit"
  • "Boss approaching"
  • "We have no bananas"
  • "Karaoke contest in 10 minutes"

Create a new file in your word processor called Danger.java. Each section of the class will be described as you enter it. Begin with the following statements:

import java.awt.*;
import javax.swing.*;
public class Danger extends JPanel implements Runnable {
 String text = "No text has been specified";
 float hue = (float) 0.5;
 float saturation = (float) 0.8;
 float brightness = (float) 0.0;
 Font textFont = new Font("Dialog", Font.BOLD, 20);
 int textX;
 Thread runner;


The program begins like most Swing projects you create, by importing packages such as java.awt and javax.swing. The class statement defines Danger as a subclass of the JPanel class. It also uses the implements keyword to indicate that Danger implements the Runnable interface, which is required for all classes that function as threads. The next several lines define variables and objects that will be used in the class. The string variable text is created with a default value, and it will be used to store the text that should be displayed onscreen. Three floating-point variables are used to store values for a color using its Hue Saturation Brightness ratings. The (float) portion of each line converts the value that follows it into a floating-point number. This conversion must be done because the hue, saturation, and brightness variables must be of type float. The text of the panel will be displayed in 20-point Dialog bold. To do this, you need to create a Font object to store that font's values. The Font object, called textFont, is created for this purpose. Finally, the integer variable textX will be used when you're centering text from left-to-right on the panel, and a Thread object called runner is created to hold the thread that will run the Danger class. After inserting a blank line, continue entering the Danger class by entering the constructor method of the class:

public Danger(String warning) {
 text = warning;
 FontMetrics fm = getFontMetrics(textFont);
 textX = 200 - fm.stringWidth(text) / 2;
 runner = new Thread(this);
 runner.start();
}


The constructor is called when objects of this class are created, and then it is never handled again. It's a good place to set up some things that weren't set up as instance or class variables. The first thing that happens is that the contents of warning, the string sent to the constructor, are copied to the text instance variable. The FontMetrics class measures how wide a line of text will appear when it is displayed. Using the stringWidth() method of FontMetrics, you can place text at a different place in a container depending on how wide it is. The textX variable stores the horizontal position where the text should be displayed. The last thing that happens in the constructor is the creation of a new THRead object and a call to its start() method, which causes the thread to begin running. The thread is associated with the Danger class because this was used as an argument to the Thread() constructor. Now continue by entering the paintComponent() method of your class, which is called whenever the Danger panel needs to be updated. After leaving a blank line after the constructor method, enter the following:

public void paintComponent(Graphics comp) {
 Graphics2D comp2D = (Graphics2D) comp;
 comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 RenderingHints.VALUE_ANTIALIAS_ON);
 comp2D.setColor(Color.black);
 comp2D.fillRect(0, 0, 400, 200);
 Color textColor = Color.getHSBColor(hue, saturation,
 brightness);
 comp2D.setColor(textColor);
 comp2D.setFont(textFont);
 comp2D.drawString(text, textX, 30);
}


The paintComponent() method takes a Graphics object called comp as an argument, and then uses this object to cast a Graphics2D object called comp2D. The comp2D object holds all the information needed to display something onscreen, and it has several methods you'll use. The Color object called textColor is created using the HSB variables to select the color. The textColor object then becomes the current display color using the setColor() method of screen. Using the drawString() method of screen2D, the variable text is displayed at the (x,y) position of textX and 30. The color of the text is the current display color. The paintComponent() method handles most of the display work that takes place during the Danger app. All you have left to add is a method called pause(). Enter a blank line at the end of your program, and then continue with the following statements:

void pause(int duration) {
 try {
 Thread.sleep(duration);
 } catch (InterruptedException e) { }
}


The pause() method, which takes an argument called duration, pauses the thread by calling the Thread.sleep() method with an argument specifying the number of milliseconds to pause. When you are displaying changing graphics or text, you might need pauses of some kind to prevent things from changing too quickly. This pause() method shows one way to create these pauses. To finish off the Danger class, you need to add a run() method that controls the threaded animation of the text. Add the following statements:

public void run() {
 Thread thisThread = Thread.currentThread();
 while (runner == thisThread) {
 pause(75);
 brightness += 0.05;
 if (brightness > 1) {
 brightness = (float) 0.0;
 pause(75);
 }
 repaint();
 }
}


The run() method begins with the same two statements used in other threaded programs. A THRead object is created that holds the currently running thread, and it is compared to the runner object in a while loop. This while loop is what causes the animation of the text to take place. The pause(75) statement causes the animation to pause 75 milliseconds between each update of the panel. Without a pause of some kind, the text would flash different colors as quickly as a Java interpreter could handle it. You have to change the value of the brightness variable for the text to change in brightness. The program increases the variable .05 (a 5% change), and if the variable has reached the maximum brightness of 1.0, it is reset to 0.0. Whenever brightness must be reset to 0.0, the program calls the pause() method. The last thing that takes place in the run() method's while loop is a call to the repaint() statement. You use this statement any time you need to redraw the container because something has changed. Because the brightness variable changes each time through the while loop, you know there's a need to redisplay the text after every pause. The repaint() statement is a request for Java to call the paintComponent() method. Save the Danger.java file, which should resemble Listing 22.2. The only difference might be in the way you have indented methods and other statements. That does not have to be changed for the program to run, but indentation and other spacing can make a program easier to understand.

Listing 22.2. The Full Text of Danger.java
 1: import java.awt.*;
 2: import javax.swing.*;
 3:
 4: public class Danger extends JPanel implements Runnable {
 5: String text = "No text has been specified";
 6: float hue = (float) 0.5;
 7: float saturation = (float) 0.8;
 8: float brightness = (float) 0.0;
 9: Font textFont = new Font("Dialog", Font.BOLD, 20);
10: int textX;
11: Thread runner;
12:
13: public Danger(String warning) {
14: text = warning;
15: FontMetrics fm = getFontMetrics(textFont);
16: textX = 200 - fm.stringWidth(text) / 2;
17: runner = new Thread(this);
18: runner.start();
19: }
20:
21: public void paintComponent(Graphics comp) {
22: Graphics2D comp2D = (Graphics2D) comp;
23: comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
24: RenderingHints.VALUE_ANTIALIAS_ON);
25: comp2D.setColor(Color.black);
26: comp2D.fillRect(0, 0, 400, 200);
27: Color textColor = Color.getHSBColor(hue, saturation,
28: brightness);
29: comp2D.setColor(textColor);
30: comp2D.setFont(textFont);
31: comp2D.drawString(text, textX, 30);
32: }
33:
34: void pause(int duration) {
35: try {
36: Thread.sleep(duration);
37: } catch (InterruptedException e) {
38: // do nothing
39: }
40: }
41:
42: public void run() {
43: Thread thisThread = Thread.currentThread();
44: while (runner == thisThread) {
45: pause(75);
46: brightness += 0.05;
47: if (brightness > 1) {
48: brightness = (float) 0.0;
49: pause(75);
50: }
51: repaint();
52: }
53: }


After compiling the file with the javac compiler tool, you need to create an app or applet that contains the Danger panel. Create a new file with your word processor called DangerFrame.java, and enter the text of Listing 22.3 into the file.

Listing 22.3. The Full Text of DangerFrame.java
 1: import java.awt.*;
 2: import javax.swing.*;
 3:
 4: public class DangerFrame extends JFrame {
 5: public DangerFrame () {
 6: super("Warning!");
 7: setSize(400, 70);
 8: setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 9: Danger gb = new Danger("Core Breach in Sector 12");
10: add(gb);
11: setVisible(true);
12: }
13:
14: public static void main(String[] arguments) {
15: DangerFrame frame = new DangerFrame();
16: }
17: }


You can change the value in Line 9 to any other menacing sounding text, as long as it is similar in size to Core Breach in Sector 12. Compile and run the file to see the Danger panel in a frame (Screenshot).

Screenshot Displaying the Danger panel in an app.

Java ScreenShot


      
Comments