Saturday, November 21, 2009

Section 8.4. Example: A Toggle Button











[Page 365]

8.4. Example: A Toggle Button


The ability to extend an existing class is one of the most powerful features of object-oriented programming. It allows objects to reuse code defined in the superclasses without having to redefine or recompile the code. As we saw in Chapter 4, a programmer-defined applet, such as GreeterApplet, uses the public methods defined for JApplets, JPanels, Containers, Components, and Objects simply because it is a subclass of JApplet (Fig. 4.11). By the same token, it can use all of the public and protected instance variables and constants defined in these classes by simply referring to them in its own code.



Reusing code




In this section, we present an example of how inheritance can be used to extend and customize the functionality of a Java library class. As we saw in Chapter 4, a JButton is a GUI component that can be associated with a particular action by implementing the ActionListener interface. For example, we used a JButton in the GreeterApplet to generate a greeting to the user.


In this section, we will design a more sophisticated button. We will call it a ToggleButton and define it as a JButton subclass that toggles its label whenever it is clicked, in addition to carrying out some kind of associated action.



Problem decomposition




A light switch behaves similarly to a ToggleButton in this sense. Whenever you flick a light switch, it changes its label from "on" to "off," but it also turns the lights on or off. Although different switches are associated with different lights, every light switch toggles its label each time it is clicked. So let's design a ToggleButton that behaves like a light switch.


The main idea in our design is that a ToggleButton is a JButton that has two labels. By default, a JButton has just a single label. Thus, because of the type of behavior we want to elicit, we need to define ToggleButton as a subclass of JButton with two String variables that will serve as its alternative labels (Fig. 8.8). Note that we give it a constructor method that will allow us to provide the initial value of its two label strings. Another important feature of a ToggleButton is that it should act as its own ActionListener so that it can toggle its label whenever it is clicked. Therefore, it must also implement the ActionListener interface.




Figure 8.8. A ToggleButton is a JButton with two labels.







The complete definition of ToggleButton is given in Figure 8.9. Note how we have defined its constructor. Recall that the JButton class has a constructor method with the signature JButton(String), which allows us to set a JButton's label during instantiation. We need to do the same thing with one of ToggleButton's two labels. That is, when we create a ToggleButton, we want to initialize its label to one of its two alternative labels (here, "On" or "Off").




[Page 366]

Figure 8.9. Definition of the ToggleButton class.





import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ToggleButton extends JButton implements ActionListener {
private String label1; // Toggle between two labels
private String label2;

public ToggleButton(String l1, String l2) { // Constructor
super(l1); // Use l1 as the default label
label1 = l1;
label2 = l2;
addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
String tempS = label1; // Swap the labels
label1 = label2;
label2 = tempS;
setText(label1);
} // actionPerformed()
} // ToggleButton class




Because constructor methods are not inherited by the subclass, we want to invoke the superclass's constructor in the ToggleButton() constructor using the super keyword. This must be done as the first statement in the ToggleButton() constructor. By passing l1 to the superconstructor, we are making the first string that the user gives us the default label for our ToggleButton. This will be the label that appears on the button when it is first displayed in the applet.


Note also in the ToggleButton() constructor that the ToggleButton is designated as its own ActionListener, so whenever it is clicked, its actionPerformed() method will be invoked. The actionPerformed() method exchanges the button's current label for its other label. Swapping two values in memory is a standard programming practice used in many different algorithms. In order to do it properly, you must use a third variable to temporarily store one of the two values you are swapping. The comments in actionPerformed() provide a step-by-step trace of the values of the three variables involved.



Swapping algorithm




Java Programming Tip: Swapping Values

It is necessary to use a temporary variable whenever you are swapping two values of any type in memory. The temporary variable holds the first value while you overwrite it with the second value.



The first statement in actionPerformed() creates a temporary String variable named tempS and assigns it the value of label1. Recall that label1 was the button's initial label. To make this example easier to follow, let's suppose that initially label1 is "off" and label2 is "on". After line 1 is executed, both tempS and label1 contain "off" as their value. Line 2 then assigns label2's value to label1. Now both label1 and label2 store "on" as their value. In line 3 we assign tempS's value to label2. Now label2 stores "off" and label1 stores "on", and we have effectively swapped their original values.



Swapping values requires a temporary variable





[Page 367]

The next time we invoke actionPerformed(), label1 and label2 will have their opposite values initially. Swapping them a second time will assign them their initial values again. We can continue toggling their values in this way indefinitely. To complete the method, the last statement in actionPerformed() assigns label1's current value as the new ToggleButton's label.


Now that we have seen that a ToggleButton toggles its label between two values, what about performing an associated action? To do this, we need a design involving multiple event handlers, one to handle the toggling of the button's label and the other to handle its associated action (Fig 8.10). In this design, lightSwitch has two listeners that respond to its events: the lightSwitch itself, as a result of the actionPerformed() method in its class, and the ToggleApplet, as a result of the actionPerformed() method in this class.




Figure 8.10. The ToggleButton has two ActionListeners. When the button is clicked, the JVM will call each listener's actionPerformed() method, and each listener will take its own independent action.








Multiple event handlers




The implementation of this design is given by ToggleApplet, an applet that uses a ToggleButton (Fig. 8.11). Like the applet we designed in Chapter 4, this applet extends the JApplet class and implements the ActionListener interface. In this example we use a ToggleButton to simulate a light switch. Note that we assign the applet itself as an ActionListener for the lightSwitch, so that when lightSwitch is clicked, the applet displays the message "The light is on" or "The light is off" in its status bar (Fig. 8.12). This is a somewhat trivial action, but it illustrates that a ToggleButton both toggles its own label and carries out some associated action.


[Page 368]

Figure 8.11. Definition of the ToggleApplet class.

(This item is displayed on page 367 in the print version)





import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ToggleApplet extends JApplet implements ActionListener {
private ToggleButton lightSwitch;

public void init() {
lightSwitch = new ToggleButton ("off","on");
getContentPane().add(lightSwitch);
lightSwitch.addActionListener(this);
} // init()

public void actionPerformed(ActionEvent e) {
showStatus("The light is " + lightSwitch.getText());
} // actionPerformed()
} // ToggleApplet class






Figure 8.12. When clicked, ToggleApplet button causes "The light is on" or "The light is off" to appear in the applet's status bar.







The ToggleButton design satisfies several key design principles of object-oriented programming. First and foremost, it uses inheritance to extend the functionality of the predefined JButton classthe extensibility principle. Second, it encapsulates a ToggleButton's essential behavior within the ToggleButton class itselfthe modularity principle. Finally, it hides the mechanism by which a ToggleButton manages its labelsthe information-hiding principle.



Object-oriented design principles




Effective Design: Inheritance

Inheritance enables you to specialize an object's behavior. A ToggleButton does everything a JButton does, plus it can toggle its own label.




Self-Study Exercises


Exercise 8.9

Write a code segment (not a whole method) to swap two boolean variables, b1 and b2.

Exercise 8.10

Suppose you are designing an applet that plays a card game, and you want a single button that can be used both to deal the cards and to collect the cards. Write a code segment that creates this type of button, adds it to the applet, and designates the applet as its ActionListener.













No comments: