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:
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.
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:
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:
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.
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.
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!
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.
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.
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.
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.
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.
// 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.
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:
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.
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.
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.