- 12 -
An LED Display Bean

This chapter carries you through the design and development of your third complete bean. At this point you should be starting to get more comfortable with custom bean development as you are no doubt gaining experience quickly. In this chapter you design and build an LED display bean, which is similar to the meter bar bean from Chapter 11, "A Meter Bar Bean," in that it is an output bean. The LED display bean is used to display numeric values in a form similar to the output of a calculator with an LED display. This bean admittedly has limited usefulness, but it can be used to jazz up the output of an application with ease. I have to admit that I'm a sucker for cool forms of output, even when they aren't totally necessary.

You'll find that the design and implementation of the LED display bean are very similar to those of the meter bar bean. For this reason, the discussion of the LED display bean is geared more toward the unique details of the bean, as opposed to rehashing all the same issues you dealt with in Chapters 10, "A Fancy Button Bean," and 11. You no doubt will notice throughout this chapter that the LED display bean is pretty simple in function. I deliberately wanted to lighten things up with this bean because this part of the book is difficult enough just based on the sheer amount of code.

You learn about the following issues in this chapter:

Designing the LED Display Bean

The LED display bean is a visual representation of the output of a calculator with an LED display. The bean isn't a calculator, but it does imitate the output style of calculators with LED displays. The LED display bean can be used in any situation in which you want to present a number with a little more impact than simply drawing it as text. For example, the bean would be perfect in games as a scoreboard, because it draws attention to the number being displayed.

The LED display bean enables you to adjust the number of digits that can be displayed but imposes a limit on them. Unlike the beans you created in Chapters 10 and 11, the LED display bean doesn't support the capability to change the background and foreground colors. This is because the bean always has a black background with green foreground digits; the digits are represented by images. Similar to the meter bar bean, the LED display bean doesn't support any type of user interface interaction such as mouse clicks or key presses, because the LED display bean is purely an output bean.

Now that you have an idea of what the LED display does, let's move on to some specifics by designing its properties, methods, and events. The rest of this section focuses on the design of these major functional parts of the LED display bean.

Properties

Like the other beans you've developed, the LED display bean derives from the AWT Canvas class. Recall that the Canvas class is derived from the Component class, which provides the base functionality typically required of graphical beans, including background and foreground colors, name, and font properties. Based on the description of the LED display bean, you might already be thinking that something is amiss with these inherited properties. If you're thinking some of them aren't necessary in this bean, then you are right on track! Because the LED display bean has a fixed background color (black), it has no use for the background color property. The bean also has no use for the foreground color or font properties because it draws its state purely by using GIF images representing each numeric digit. You'll use a technique similar to what you used with the meter bar bean to hide these unnecessary properties from the user.

The first property to address in the bean's design is the raised property, which determines how the 3D border of the bean is drawn. Remember this property from the meter bar bean? If raised is true, then the border is drawn to look raised from the screen. If not, the border is drawn to look inset. The raised property is a simple boolean property that gives the user a little more control over the appearance of the LED display bean.

I mentioned already that the bean provides a way to adjust the number of digits being displayed, along with limiting them. This sounds like an ideal place for a property. The number of digits property does the trick by specifying how many digits the bean is to display. Understand that these digits are drawn regardless of the numeric value being displayed. The number of digits property is functionally equivalent to the number of digits capable of being displayed on a calculator's LED display, because it determines the physical size of the bean.

The last property required of the bean is the actual numeric value being displayed. There are no surprises here, except to keep in mind that this property must be checked against the number of digits property to make sure it can be sufficiently displayed.

These properties are sufficient for modeling all the functionality requirements of the LED display bean. To recap, following are the properties required of the LED display bean:

Methods

Now that we've defined the properties, it's time to move on to the public methods required of the LED display bean. The best place to start is with the accessor methods for the bean, because you now know the bean's properties. All the properties in the bean are readable and writeable, so you know they all will require a pair of accessor methods.

Beyond accessor methods, you also have to consider the other public methods that the bean must provide. It turns out that the LED display bean needs only two additional public methods. The first of these, paint(), should be familiar to you, because it has played a critical role in both of the beans you created in Chapters 10 and 11. The other public method required of the bean is the getPreferredSize()method, which calculates and returns the preferred size of the bean based on its current state. This method is necessary because the LED display must be carefully sized around the digits being displayed. Recall that the fancy button bean from Chapter 10 uses this method to return the preferred size of the button, based on the label text and font. The LED display bean is performing a similar function, except that it calculates the preferred size based on the number of digits being displayed.

Following are the public methods required of the LED display bean:

Events

The LED display bean is used solely for output purposes, so there is no need for it to process events in any way. For this reason, the design of the LED display bean includes no provision for events.

Developing the LED Display Bean

With the design for the LED display bean behind you, it's time to jump into writing the actual code. You'll find that many aspects of the LED display code are similar to the code for the meter bar bean covered in Chapter 11. This is because both are output beans and therefore require a similar type of overhead. Regardless of the similarities, however, you still encounter some new challenges in the code for the LED display bean.

At some point you should check out the complete source code for the LED display bean, which is included on the accompanying CD-ROM. The main code for the LED display bean is in the LEDDisplay class, which appears on the CD-ROM in the file LEDDisplay.java.

Properties and Member Variables

The first place to start in developing the LED display bean is laying out the properties. Following are the member variable properties for the bean, based on the initial design:

private boolean raised;



private int     numDigits;



private int     value;



Not surprisingly, these member variables directly correspond to the properties mentioned in the previous section. Notice that they are all built-in Java types, which means that property editors are already provided by JavaBeans for each of them. As a result, you do not have to do any extra work to provide visual editing support for the bean's properties. Along with these properties, the bean also requires another member variable for internal use:

private transient Image[] digits;



The digits member variable is an array of images that represent the digits used in painting the bean. Each image in the array corresponds to a digit. For convenience and consistency, the digits are stored in the array according to their numeric representation. In other words, the array element at index four of the array is the image for the number four. This makes the code that paints the digits a little easier to follow. Figure 12.1 shows how the digit images look.

Figure 12.1. The digit images used in the LED display bean.

That wraps up the member variables for the LED display bean. The constructors are next!

Constructors

With the properties wrapped up, it's time to turn your attention toward the constructors for the LED display bean. The LED display bean provides two constructors: a default constructor and a detailed constructor that takes all three properties as arguments. Following is the code for both of these constructors:

public LEDDisplay() {



  this(false, 5, 0);



}







public LEDDisplay(boolean r, int n, int v) {



  // Allow the superclass constructor to do its thing



  super();







  // Load the digit images



  digits = new Image[10];



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



    String name = "LED" + i + ".gif";



    digits[i] = loadImage(name);



    if (digits[i] == null)



      System.err.println("Couldn't load image " + name + ".");



  }







  // Set properties



  setNumDigits(n);



  raised = r;



  value = v;



  setBackground(Color.black);







}



The first constructor simply calls the second constructor with default property values. The second constructor is responsible for actually initializing the bean. This constructor begins by calling the superclass constructor, which is standard fare in AWT derived classes. The constructor then moves on to loading the digit images into the digits image array. The loading of the images is handled by the loadImage() method, which you learn about in the "Support Methods" section a little later in this chapter. With the images loaded, the constructor then sets all the properties, including the inherited background color property.

You might be wondering why the setNumDigits()setter method was called instead of just setting the numDigits member variable manually. As you learn in the next section, the setNumDigits() method does a little more than just set the underlying member variable. It also resizes the bean to fit the number of digits being displayed.

Accessor Methods

The next part of the LED display bean is its accessor methods, which provide access to bean properties. You've already seen the accessor methods for the raised property in the meter bar bean, so let's just skip it. The next property is the number of digits property, whose accessor methods follow:





public int getNumDigits() {



  return numDigits;



}







public void setNumDigits(int n) {



  numDigits = Math.max(0, Math.min(8, n));  // maximum of 8 digits



  sizeToFit();







}



The getNumDigits()getter method is self-explanatory in that it simply returns the value of the numDigits member variable. The setNumDigits() setter method is a little more interesting, because it resizes the bean after setting the numDigits member. You resize the bean so the size of the LED display will closely fit the digits being displayed. The actual resizing is handled by the sizeToFit() method, about which you learn in the "Support Methods" section, later in this chapter.

The other set of accessor methods in the LED display bean belongs to the numeric value property. Following is the code for these methods:





public int getValue() {



  return value;



}







public void setValue(int v) {



  // Constrain to a maximum value of (10 ^ numDigits) - 1



  if (v <= (int)Math.pow(10.0, (double)numDigits) - 1)



    value = Math.max(0, v);



  repaint();







}



The getter method, getValue(), simply returns the value of the value member variable. The setter method, setValue(), sets the value of the value member, while ensuring that it doesn't overflow the LED display. This is important because you don't want to be able to set a value that can't sufficiently be displayed in the bean's display area. The method finishes up by repainting the bean to reflect the change in the LED display value.

Public Methods

There are a few public methods in the bean: paint() and getPreferredSize(). The source code for the paint()method follows:





public synchronized void paint(Graphics g) {



  int width = size().width;



  int height = size().height;







  // Paint the background with 3D effects



  g.setColor(getBackground());



  g.fillRect(1, 1, width - 2, height - 2);



  g.setColor(Color.lightGray);



  g.draw3DRect(0, 0, width - 1, height - 1, raised);







  // Paint the LED digits exp



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



    int div = (int)Math.pow(10.0, (double)(numDigits - i - 1));



    g.drawImage(digits[(value / div) % 10],



      3 + i * (2 + DIGIT_WIDTH), 3, this);



  }







}



The paint() method starts by getting the width and height of the bean, filling the background, and painting a 3D effect around the edges. This code should look familiar to you by now, because it is based on the code used to paint both of the beans you developed in Chapters 10 and 11. The LED display itself is painted next by drawing each digit in the numeric value being displayed. This is accomplished by looping through the number of digits being displayed and drawing the appropriate digit image for each one. If this digit image drawing code appears to be a little confusing at first, take a look at the following equation:

digit = (num / (10 ^ place)) % 10;



This equation describes how to determine a digit at any particular place in a number, which is what is required of the paint() method. As an example, if the number in question is 453 and you want to know what the middle digit is, you would use the following equation:

digit = (453 / (10 ^ 1)) % 10;



In this example, 10 ^ 1 results in 10, which simplifies the equation to this:

digit = (453 / 10) % 10;



The equation is further simplified by the result of 453 / 10, which is 45:

digit = 45 % 10;



Finally, 45 % 10 leaves a result of 5, which is the middle digit of the original number 453. Is it magic? Not quite. This is just an equation that looks messier in Java code than it really is. Nevertheless, it gets the job done.

The getPreferredSize()method is used to return the preferred size of the bean. You saw a version of this method in action in the fancy button bean. The version of getPreferredSize() in the LED display bean is internally different from the one in the fancy button bean, but it ultimately serves the same purpose. Following is the code for the getPreferredSize() method:





public Dimension getPreferredSize() {



  // Calculate the preferred size based on the label text



  return new Dimension(((2 + DIGIT_WIDTH) * numDigits) + 4,



    DIGIT_HEIGHT + 6);







}



The getPreferredSize() method calculates the size of the LED display, based on the number of digits being displayed and the size of the digit images. Notice that the calculation also includes some padding so that the images are spaced out and more visually appealing.

Support Methods

The LED display bean uses two private support methods that you haven't learned about yet: sizeToFit() and loadImage(). These two methods provide functionality that the bean needs only internally, which is why they are declared as private. You've actually used the sizeToFit() method before, in the fancy button bean. Even so, check out the code again just to make sure you remember how it works:





private void sizeToFit() {



  // Resize to the preferred size



  Dimension d = getPreferredSize();



  resize(d.width, d.height);



  Component p = getParent();



  if (p != null) {



    p.invalidate();



    p.layout();



  }







}



The sizeToFit() method is responsible for sizing the bean to closely fit the displayed digits. The getPreferredSize() method is called by sizeToFit() to get the optimal LED display size, which is then used to resize the bean.

The loadImage() method is also important to the internal functioning of the LED display bean. Following is its code:





public Image loadImage(String name) {



  try {



    URL url = getClass().getResource(name);



    return (Image)url.getContent();



  }



  catch (Exception e) {



    return null;



  }







}



This method returns an Image object corresponding to the string name of an image. This is accomplished by first constructing a URL (Uniform Resource Locator) based on the name of the image. This URL is then used to load the image using the getContent() method.

Additional Overhead

You've now covered all the source code for the LED display bean itself. Based on your newly gained bean development experience, however, you probably suspect that the development of this bean isn't quite finished. The LED display bean still requires a bean information class to specify icons that graphically represent the bean in application builder tools. The bean information class is also required to hide some of the bean's inherited properties that aren't applicable to the bean. Listing 12.1 contains the complete source code for the LEDDisplayBeanInfo class.

Listing 12.1. The complete source code for the LEDDisplayBeanInfo bean information class (LEDDisplayBeanInfo.java).





// LEDDisplayBeanInfo Class



// LEDDisplayBeanInfo.java







package PJB.Source.Chap12.LEDDisplay;







// Imports



import java.beans.*;







public class LEDDisplayBeanInfo extends SimpleBeanInfo {



  // Get the appropriate icon



  public java.awt.Image getIcon(int iconKind) {



    if (iconKind == BeanInfo.ICON_COLOR_16x16) {



      java.awt.Image img = loadImage("LEDDisplayIcon16.gif");



      return img;



    }



    if (iconKind == BeanInfo.ICON_COLOR_32x32) {



      java.awt.Image img = loadImage("LEDDisplayIcon32.gif");



      return img;



    }



    return null;



  }







  // Explicit declare the properties



  public PropertyDescriptor[] getPropertyDescriptors() {



    try {



      PropertyDescriptor



        // Inherited properties



        foreground = new PropertyDescriptor("foreground",



        ÂLEDDisplay.class),



        background = new PropertyDescriptor("background",



        ÂLEDDisplay.class),



        font = new PropertyDescriptor("font", LEDDisplay.class),



        name = new PropertyDescriptor("name", LEDDisplay.class),



        // New properties



        raised = new PropertyDescriptor("raised",



        ÂLEDDisplay.class),



        numDigits = new PropertyDescriptor("numDigits",



        ÂLEDDisplay.class),



        value = new PropertyDescriptor("value", LEDDisplay.class);







      // Hide the foreground, background, and font properties



      foreground.setHidden(true);



      background.setHidden(true);



      font.setHidden(true);







      PropertyDescriptor[] pd = { foreground, background, font,



      Âname, raised,



        numDigits, value };



      return pd;



    }



    catch (IntrospectionException e) {



      throw new Error(e.toString());



    }



  }







}



The first method in the LEDDisplayBeanInfo class, getIcon(), is almost identical to the versions of the method you provided in the beans you developed in Chapters 10 and 11. The only difference is the specification of the GIF files for the icons themselves. These icons are provided as GIF 89a images and are shown in Figure 12.2.

Figure 12.2. The bean information icons for the LED display bean.

The second method used in the LEDDisplayBeanInfo class is getPropertyDescriptors(), which is used to explicitly specify the properties exposed by the bean. The purpose of implementing this method is to exclude the foreground, background, and font properties from being exposed by the bean. This is necessary because these inherited properties have no significance to the LED display bean. The getPropertyDescriptors() method requires you to declare every property that is to be exposed by the bean. Excluding these properties is simply a process of calling the setHidden() method on the PropertyDescriptor objects corresponding to each of them.

Like the other beans you've developed, the LED display bean also requires a manifestation file to be included in the JAR file that contains the bean. The code for the LED display bean's manifestation file, LEDDisplay.mf, follows:





Manifest-Version: 1.0



Name: PJB/Source/Chap12/LEDDisplay/LEDDisplay.class



Java-Bean: True



The only difference between this manifest file and the others you've seen is the name of the bean class and its package association.


CAUTION: Before you compile the LED display bean, make sure you have the CLASSPATH environment variable properly set so that the compiler can locate its support classes. In other words, make sure the root path above the bean package hierarchy is included in CLASSPATH. This directory is the parent directory of the PJB directory.


NOTE: The complete source code and related resources for the LED display bean are located on the accompanying CD-ROM.

Testing the LED Display Bean

As in all custom beans, the LED display bean must be compressed into a JAR file and added to the BeanBox through a few steps before it can be tested. Just to recap, these steps follow:

1. Create a JAR file that contains the bean and its resources, using the jar utility (via the beanjar.bat batch file you learned about in
Chapter 10).

2.
Copy the JAR file containing the bean to the jars directory beneath your BDK installation.

3.
Execute the run.bat batch file to launch the BeanBox with the new bean added to the ToolBox.

When you perform these steps, the BeanBox should appear with your LED display bean added to the ToolBox. Figure 12.3 shows how the ToolBox looks with the bean added.

Figure 12.3. The BeanBox ToolBox window with the LED display bean added.

Notice in the figure that the LED display bean has been added to the end of the ToolBox, complete with the 16x16 icon you specified in the bean information class. Try out the bean by adding it to the BeanBox; just click it in the ToolBox and then click the main container window. Figure 12.4 shows the newly added LED display bean.

Figure 12.4. The BeanBox main container window with an LED display bean added.

Now turn your attention to the PropertySheet window and see how the properties look for the bean. Figure 12.5 shows the PropertySheet window for the LED display bean.

Figure 12.5. The BeanBox PropertySheet window for the LED display bean.

The PropertySheet window shows all the properties for the bean, including the inherited name property and the properties you defined. Notice that the background, foreground, and font properties are all missing, thanks to the method you overrode in the bean information class. Try using the PropertySheet window to modify the number of digits and value properties to see how the LED display changes. Figure 12.6 shows the bean with these properties modified.

Figure 12.6. The BeanBox main container with a modified LED display bean.

As you can see, the LED display bean provides an interesting way to present numeric values without too much effort. I hope you are beginning to see how powerful beans can be, especially considering the ease with which they can be designed and built.

Enhancing the LED Display Bean

The last topic of this chapter covers some ways in which you can improve the LED display bean. No bean is ever really perfect; I think there's always room for improvement in some form. Following is a list of possible improvements that you could make to the LED display bean:

The first suggestion involves modifying the code in the paint() method so that it doesn't draw leading zeros. In other words, only the digits specifically required to display a number are drawn. As an example, if the number 242 is being displayed with the number of display digits set to 5, it would be drawn as 242 instead of 00242. In this case, the two leftmost digits would be left blank. This improvement actually makes the bean behave more like a calculator's LED display.

The second suggestion is to provide other sets of images in different colors. This change would require a new property to represent the color of the digits. As an alternative, you could change the entire drawing mechanism for the bean so that it doesn't rely on images at all. With this approach, you could enable the inherited foreground property and allow the LED digits to be drawn in any color.

The final suggestion involves supporting more than just numbers in the display. Most LED displays in the real world support decimal places, and some (usually in clocks) support colons for separating hours and minutes when displaying time. You could make this enhancement through a few different approaches. One of them is to provide a mode property that places the bean in either time mode or numeric mode. In time mode, it would automatically insert colons in the display between pairs of digits. Of course, you would need to create a new image for the colon.

To support decimal places, you would probably need to change the value property from an integer to a float. You then would need to alter the drawing code for the bean to check for the decimal point within the floating point value and draw it accordingly. Again, you would need to create an image for the decimal point.

Summary

This chapter adds to your bean development skills by presenting you with another complete bean construction project. In this chapter you tackled an LED display bean that imitates the visual output of a calculator with an LED display. You began the chapter with the initial design of the bean, just as you did in Chapters 10 and 11 with other beans. You then moved on to develop the actual code for the bean, after which you put the bean to the test in the BeanBox. You wrapped up the chapter by covering some ways you could improve the LED display bean.

If you're starting to get weary from all this bean development, don't give up just yet. Chapter 13, "An Audio Player Bean," finishes this hands-on part of the book by leading you through the development of an audio player bean, which provides an easy approach to playing audio clips.