Text Fields and an Introduction to Event Handling with Nested Classes

Normally, a user interacts with an application's GUI to indicate the tasks that the application should perform. For example, when you write an e-mail in an e-mail application, clicking the Send button tells the application to send the e-mail to the specified e-mail addresses. GUIs are event driven. When the user interacts with a GUI component, the interactionknown as an eventdrives the program to perform a task. Some common events (user interactions) that might cause an application to perform a task include clicking a button, typing in a text field, selecting an item from a menu, closing a window and moving the mouse. The code that performs a task in response to an event is called an event handler and the overall process of responding to events is known as event handling.

In this section, we introduce two new GUI components that can generate eventsJTextFields and JPasswordFields (package javax.swing). Class JTextField extends class JTextComponent (package javax.swing.text), which provides many features common to Swing's text-based components. Class JPasswordField extends JTextField and adds several methods that are specific to processing passwords. Each of these components is a single-line area in which the user can enter text via the keyboard. Applications can also display text in a JTextField (see the output of Fig. 11.10). A JPasswordField shows that characters are being typed as the user enters them, but hides the actual characters with an echo character, assuming that they represent a password that should remain known only to the user.

When the user types data into a JTextField or a JPasswordField, then presses Enter, an event occurs. Our next example demonstrates how a program can perform a task when that event occurs. The techniques shown here are applicable to all GUI components that generate events.

The application of Fig. 11.9 and Fig. 11.10 uses classes JTextField and JPasswordField to create and manipulate four text fields. When the user types in one of the text fields, then presses Enter, the application displays a message dialog box containing the text the user typed. You can only type in the text field that is "in focus." A component receives the focus when the user clicks the component. This is important because the text field with the focus is the one that generates an event when the user presses Enter. In this example, when the user presses Enter in the JPasswordField, the password is revealed. We begin by discussing the setup of the GUI, then discuss the event-handling code.

Figure 11.9. JTextFields and JPasswordFields.

(This item is displayed on pages 523 - 525 in the print version)

1 // Fig. 11.9: TextFieldFrame.java 2 // Demonstrating the JTextField class. 3 import java.awt.FlowLayout; 4 import java.awt.event.ActionListener; 5 import java.awt.event.ActionEvent; 6 import javax.swing.JFrame; 7 import javax.swing.JTextField; 8 import javax.swing.JPasswordField; 9 import javax.swing.JOptionPane; 10 11 public class TextFieldFrame extends JFrame 12 { 13 private JTextField textField1; // text field with set size 14 private JTextField textField2; // text field constructed with text 15 private JTextField textField3; // text field with text and size 16 private JPasswordField passwordField; // password field with text 17 18 // TextFieldFrame constructor adds JTextFields to JFrame 19 public TextFieldFrame() 20 { 21 super( "Testing JTextField and JPasswordField" ); 22 setLayout( new FlowLayout() ); // set frame layout 23 24 // construct textfield with 10 columns 25 textField1 = new JTextField( 10 ); 26 add( textField1 ); // add textField1 to JFrame 27 28 // construct textfield with default text 29 textField2 = new JTextField( "Enter text here" ); 30 add( textField2 ); // add textField2 to JFrame 31 32 // construct textfield with default text and 21 columns 33 textField3 = new JTextField( "Uneditable text field", 21 ); 34 textField3.setEditable( false ); // disable editing 35 add( textField3 ); // add textField3 to JFrame 36 37 // construct passwordfield with default text 38 passwordField = new JPasswordField( "Hidden text" ); 39 add( passwordField ); // add passwordField to JFrame 40 41 // register event handlers 42 TextFieldHandler handler = new TextFieldHandler(); 43 textField1.addActionListener( handler ); 44 textField2.addActionListener( handler ); 45 textField3.addActionListener( handler ); 46 passwordField.addActionListener( handler ); 47 } // end TextFieldFrame constructor 48 49 // private inner class for event handling 50 private class TextFieldHandler implements ActionListener 51 { 52 // process text field events 53 public void actionPerformed( ActionEvent event ) 54 { 55 String string = ""; // declare string to display 56 57 // user pressed Enter in JTextField textField1 58 if ( event.getSource() == textField1 ) 59 string = String.format( "textField1: %s", 60 event.getActionCommand() ); 61 62 // user pressed Enter in JTextField textField2 63 else if ( event.getSource() == textField2 ) 64 string = String.format( "textField2: %s", 65 event.getActionCommand() ); 66 67 // user pressed Enter in JTextField textField3 68 else if ( event.getSource() == textField3 ) 69 string = String.format( "textField3: %s", 70 event.getActionCommand() ); 71 72 // user pressed Enter in JTextField passwordField 73 else if ( event.getSource() == passwordField ) 74 string = String.format( "passwordField: %s", 75 new String( passwordField.getPassword() ) ); 76 77 // display JTextField content 78 JOptionPane.showMessageDialog( null, string ); 79 } // end method actionPerformed 80 } // end private inner class TextFieldHandler 81 } // end class TextFieldFrame

Lines 39 import the classes and interfaces we use in this example. Class TextFieldFrame extends JFrame and declares three JTextField variables and a JPasswordField variable (lines 1316). Each of the corresponding text fields is instantiated and attached to the TextFieldFrame in the constructor (lines 1947).

Creating the GUI

Line 22 sets the layout of the TextFieldFrame to FlowLayout. Line 25 creates textField1 with 10 columns of text. The width in pixels of a text column is determined by the average width of a character in the text field's current font. When text is displayed in a text field and the text is wider than the text field itself, a portion of the text at the right side is not visible. If you are typing in a text field and the cursor reaches the right edge of the text field, the text at the left edge is pushed off the left side of the text field and will no longer be visible. Users can use the left and right arrow keys to move through the complete text even though the entire text will not be visible at one time. Line 26 adds textField1 to the JFrame.

Line 29 creates textField2 with the initial text "Enter text here" to display in the text field. The width of the text field is determined by the width of the default text specified in the constructor. Line 30 adds textField2 to the JFrame.

Line 33 creates textField3 and calls the JTextField constructor with two argumentsthe default text "Uneditable text field" to display and the number of columns (21). The width of the text field is determined by the number of columns specified. Line 34 uses method setEditable (inherited by JTextField from class JTextComponent) to make the text field uneditablei.e., the user cannot modify the text in the text field. Line 35 adds textField3 to the JFrame.

Line 38 creates passwordField with the text "Hidden text" to display in the text field. The width of the text field is determined by the width of the default text. When you execute the application, notice that the text is displayed as a string of asterisks. Line 39 adds passwordField to the JFrame.

Steps Required to Set Up Event Handling for a GUI Component

This example should display a message dialog containing the text from a text field when the user presses Enter in that text field. Before an application can respond to an event for a particular GUI component, you must perform several coding steps:

1.

Create a class that represents the event handler.

 

2.

Implement an appropriate interface, known as an event-listener interface, in the class from Step 1.

 

 

3.

Indicate that an object of the class from Steps 1 and 2 should be notified when the event occurs. This is known as registering the event handler.

 

Using a Nested Class to Implement an Event Handler

All the classes discussed so far were so-called top-level classesthat is, the classes were not declared inside another class. Java allows you to declare classes inside other classesthese are called nested classes. Nested classes can be static or non-static. Non-static nested classes are called inner classes and are frequently used for event handling.

Software Engineering Observation 11.2

An inner class is allowed to directly access its top-level class's variables and methods, even if they are private.

Before an object of an inner class can be created, there must first be an object of the top-level class that contains the inner class. This is required because an inner-class object implicitly has a reference to an object of its top-level class. There is also a special relationship between these objectsthe inner-class object is allowed to directly access all the instance variables and methods of the outer class. A nested class that is static does not require an object of its top-level class and does not implicitly have a reference to an object of the top-level class. As you will see in Chapter 12, the Java 2D graphics API uses static nested classes extensively.

The event handling in this example is performed by an object of the private inner class TextFieldHandler (lines 5080). This class is private because it will be used only to create event handlers for the text fields in top-level class TextFieldFrame. As with other members of a class, inner classes can be declared public, protected or private.

GUI components can generate a variety of events in response to user interactions. Each event is represented by a class and can be processed only by the appropriate type of event handler. In most cases, the events a GUI component supports are described in the Java API documentation for that component's class and its superclasses. When the user presses Enter in a JTextField or JPasswordField, the GUI component generates an ActionEvent (package java.awt.event). Such an event is processed by an object that implements the interface ActionListener (package java.awt.event). The information discussed here is available in the Java API documentation for classes JTextField and ActionEvent. Since JPasswordField is a subclass of JTextField, JPasswordField supports the same events.

To prepare to handle the events in this example, inner class TextFieldHandler implements interface ActionListener and declares the only method in that interfaceaction-Performed (lines 5379). This method specifies the tasks to perform when an ActionEvent occurs. So inner class TextFieldHandler satisfies Steps 1 and 2 listed earlier in this section. We'll discuss the details of method actionPerformed shortly.

Registering the Event Handler for Each Text Field

In the TextFieldFrame constructor, line 42 creates a TextFieldHandler object and assigns it to variable handler. This object's actionPerformed method will be called automatically when the user presses Enter in any of the GUI's text fields. However, before this can occur, the program must register this object as the event handler for each text field. Lines 4346 are the event-registration statements that specify handler as the event handler for the three JTextFields and the JPasswordField. The application calls JTextField method addActionListener to register the event handler for each component. This method receives as its argument an ActionListener object, which can be an object of any class that implements ActionListener. The object handler is an ActionListener, because class TextFieldHandler implements ActionListener. After lines 4346 execute, the object handler listens for events. Now, when the user presses Enter in any of these four text fields, method actionPerformed (line 5379) in class TextFieldHandler is called to handle the event. If an event handler is not registered for a particular text field, the event that occurs when the user presses Enter in that text field is consumedi.e., it is simply ignored by the application.

Software Engineering Observation 11.3

The event listener for an event must implement the appropriate event-listener interface.

Common Programming Error 11.2

Forgetting to register an event-handler object for a particular GUI component's event type causes events of that type to be ignored.

 

Details of Class TextFieldHandler's actionPerformed Method

In this example, we are using one event-handling object's actionPerformed method (lines 5379) to handle the events generated by four text fields. Since we'd like to output the name of each text field's instance variable for demonstration purposes, we must determine which text field generated the event each time actionPerformed is called. The GUI component with which the user interacts is the event source. In this example, the event source is one of the text fields or the password field. When the user presses Enter while one of these GUI components has the focus, the system creates a unique ActionEvent object that contains information about the event that just occurred, such as the event source and the text in the text field. The system then passes this ActionEvent object in a method call to the event listener's actionPerformed method. In this example, we display some of that information in a message dialog. Line 55 declares the String that will be displayed. The variable is initialized with the empty stringa string containing no characters. The compiler requires this in case none of the branches of the nested if in lines 5875 executes.

ActionEvent method getSource (called in lines 58, 63, 68 and 73) returns a reference to the event source. The condition in line 58 asks, "Is the event source textField1?" This condition compares the references on either side of the == operator to determine whether they refer to the same object. If they both refer to textField1, then the program knows that the user pressed Enter in textField1. In this case, lines 5960 create a String containing the message that line 78 will display in a message dialog. Line 60 uses ActionEvent method getActionCommand to obtain the text the user typed in the text field that generated the event.

If the user interacted with the JPasswordField, lines 7475 use JPasswordField method getPassword to obtain the password and create the String to display. This method returns the password as an array of type char that is used as an argument to a String constructor to create a string containing the characters in the array.

Class TextFieldTest

Class TextFieldTest (Fig. 11.10) contains the main method that executes this application and displays an object of class TextFieldFrame. When you execute the application, note that even the uneditable JTextField(textField3) can generate an ActionEvent. To test this, click the text field to give it the focus, then press Enter. Also note that the actual text of the password is displayed when you press Enter in the JPasswordField. Of course, you would normally not display the password!

Figure 11.10. Test class for TextFieldFrame.

(This item is displayed on pages 525 - 526 in the print version)

1 // Fig. 11.10: TextFieldTest.java 2 // Testing TextFieldFrame. 3 import javax.swing.JFrame; 4 5 public class TextFieldTest 6 { 7 public static void main( String args[] ) 8 { 9 TextFieldFrame textFieldFrame = new TextFieldFrame(); 10 textFieldFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 11 textFieldFrame.setSize( 325, 100 ); // set frame size 12 textFieldFrame.setVisible( true ); // display frame 13 } // end main 14 } // end class TextFieldTest  

This application used a single object of class TextFieldHandler as the event listener for four text fields. Starting in Section 11.9, you will see that it is possible to declare several event-listener objects of the same type and register each individual object for a separate GUI component's event. This technique enables us to eliminate the if...else logic used in this example's event handler by providing separate event handlers for each component's events.

Категории