The Java TM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search

Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components

## How to Use Text Fields

> A text field is a basic text control that lets the user enter a small amount of text. When the user indicates that text entry is complete (usually by pressing Return), the text field fires an action event. Generally you use the `JTextField` class to provide text fields. If you need to provide a password field -- an editable text field that doesn't show the characters the user types -- use the `JPasswordField` class instead. This section discusses both text fields and password fields.

> If you want a text field that also provides a menu of strings to choose from, consider using an editable combo box. If you need to obtain more than one line of input from the user, then you should use one of the classes that implements a general-purpose text area.

> The applet below displays a basic text field and a text area. The text field is editable; the text area isn't. When the user presses Return in the text field, the applet copies the text field's contents to the text area, and then selects all the text in the text field.

> This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.

> You can find the source for the program in `TextDemo.java`. Here's the code from `TextDemo` that creates the text field in the applet:

> >     textField = new JTextField(20);
> >     textField.addActionListener(this);
> >     ...
> >     contentPane.add(textField);

> The integer argument passed to the `JTextField` constructor, `20` in the example, indicates the number of columns in the field. This number is used along with metrics provided by the field's current font to calculate the field's preferred width. It does not limit the number of characters the user can enter. To do that, you need to implement a custom document, as described in General Rules for Using Text Components.

> The next lines of code register the applet as an action listener for the text field and add the text field to the applet's content pane. Here's the `actionPerformed` method that handles action events from the text field:

> >     public void actionPerformed(ActionEvent evt) {
> >         String text = textField.getText();
> >         textArea.append(text + newline);
> >         textField.selectAll();
> >     }

> Notice the use of `JTextField`'s `getText` method to retrieve the text currently contained by the text field. The text returned by this method does not include a newline character for the Return key that fired the action event.

> This example illustrates using a basic, off-the-shelf text field for entering textual data and performing a task when the text field fires an action event. This is sufficient for many programs. Other programs, however, need more advanced behavior. As a subclass of `JTextComponent`, `JTextField` can be configured and customized. One common customization is to provide a text field whose contents are validated.

> This section covers the following advanced text field topics. To get most out of the information, you need to understand the material presented in the previous section, General Rules for Using Text Components.

> - Creating a Validated Text Field
> - Using a Document Listener on a Text Field
> - Laying Out Label-Text Field Pairs
> - Providing a Password Field
> - The Text Field and Password Field API
> - Examples that Use Text Fields and Password Fields

### Creating a Validated Text Field

> Many programs require users to enter textual data of a certain type or format. For example, a program might provide a text field for entering a date, a decimal number, or a phone number. The contents of such a text field must be validated before being used for any purpose. A text field can be action-validated or change-validated.

> The data in an action-validated field is checked each time the field fires an action event (each time the user presses the Return key). An action-validated field might, at any given point in time, contain invalid data. However, the data is validated before it's used for anything. To create an action-validated field, provide an action listener for your field and implement its `actionPerformed` method as follows:

> - Use `getText` to get the contents of the text field, or `getPassword` if you're using a password field.
> - Evaluate the value returned.
> - If the value is valid, do whatever task or calculation is required. If the value is invalid, report an error and return without performing a task or calculation.
> `PasswordDemo`, described later in this section, action-validates a password field in this manner.

> The data in a change-validated field is checked each time the field changes. A field that is change-validated can never contain invalid data because every change (keystroke, cut, copy, and so on) that might cause the data to be invalid is rejected. To create a change-validated text field you need to provide a custom document for your text field. If you aren't familiar with documents yet, see Concepts: About Documents.

> > Warning: Do not use a document listener for change validation. By the time a document listener has been notified of a change, it's too late, the change has already taken place. See the last couple of paragraphs in Listening for Changes on a Document for more information.

> The application shown in the following figure has three change-validated text fields. The user enters loan information into the first three text fields. Each time the user types a character, the program validates the input and updates the result in the fourth text field.

> > Try this:
> > 1. Compile and run the application. The source file is `TextFieldDemo.java`. You will also need `WholeNumberField.java`, `DecimalField.java`, and `FormattedDocument.java`.
> >    See Getting Started with Swing if you need help compiling or running this application.
> > 2. Enter information into the text fields and see the results.
> >    If you attempt to enter invalid data, the program beeps.
> > 3. Try to type into the fourth text field.
> >    You can't because it isn't editable. However, you can select the text.
> > 4. Resize the window.
> >    Note how the labels and text fields remain aligned. Laying Out Label-Text Field Pairs talks more about this feature of the program.

> The Years field is an instance of `WholeNumberField`, which is a subclass of `JTextField`. By overriding the `createDefaultModel` method, `WholeNumberField` establishes a custom `Document` subclass -- an instance of `WholeNumberDocument` -- as the document for each `WholeNumberField` created:

> >     protected Document createDefaultModel() {
> >         return new WholeNumberDocument();
> >     }

> Here's the implementation of `WholeNumberDocument`:

> >     protected class WholeNumberDocument extends PlainDocument {

> >         public void insertString(int offs, String str, AttributeSet a)
> >                         throws BadLocationException {

> >             char[] source = str.toCharArray();
> >             char[] result = new char[source.length];
> >             int j = 0;

> >             for (int i = 0; i < result.length; i++) {
> >     	    if (Character.isDigit(source[i]))
> >     	        result[j++] = source[i];
> >     	    else {
> >     	        toolkit.beep();
> >     	        System.err.println("insertString: " + source[i]);
> >     	    }
> >             }
> >             super.insertString(offs, new String(result, 0, j), a);
> >         }
> >     }

> This class overrides the `insertString` method, which is called every time any string or character is about to be inserted into the document. `WholeNumberDocument`'s implementation of `insertString` evaluates each character to be inserted into the text field. If the character is a digit, the document allows it to be inserted. Otherwise, the method beeps and prints an error message. Thus `WholeNumberDocument` allows the numbers in the range 0, 1, 2, ...

> An interesting implementation detail is that our custom document class does not have to override the `remove` method. The `remove` method is called each time a character or group of characters is removed from the text field. Because removing a digit from an integer cannot produce an invalid result, this class does not pay attention to removals.

> The other two input fields in the example, as well as the uneditable Monthly Payment field, are all instances of `DecimalField`, a custom `JTextField` subclass. `DecimalField` uses a custom document, `FormattedDocument`, that allows only data of a particular format to be entered.

> `FormattedDocument` has no knowledge of the actual format of its content. Instead, `FormattedDocument` relies on a format, an instance of a subclass of `Format`, to accept or reject a proposed change. The text field that uses the `FormattedDocument` must specify which format the `FormattedDocument` uses.

> The Loan Amount and Monthly Payment text fields use a `NumberFormat` object created like this:

> >     moneyFormat = NumberFormat.getNumberInstance();

> The following code creates the APR text field's format:

> >     percentFormat = NumberFormat.getNumberInstance();
> >     percentFormat.setMinimumFractionDigits(3);

> As the code shows, the same class (`NumberFormat`) can support different formats. Furthermore, `Format` and its subclasses are locale-sensitive, so a decimal field can be made to support formats for specific countries and regions. Refer to Formatting in the internationalization trail for detailed information about formats.

> Here is `FormattedDocument`'s implementation of `insertString`:

> >     public void insertString(int offs, String str, AttributeSet a)
> >                     throws BadLocationException {

> >         String currentText = getText(0, getLength());
> >         String beforeOffset = currentText.substring(0, offs);
> >         String afterOffset = currentText.substring(offs,
> >                                              currentText.length());
> >         String proposedResult = beforeOffset + str + afterOffset;

> >         try {
> >             format.parseObject(proposedResult);
> >             super.insertString(offs, str, a);
> >         } catch (ParseException e) {
> >             Toolkit.getDefaultToolkit().beep();
> >             System.err.println("insertString: could not parse: "
> >                                + proposedResult);
> >         }
> >     }

> The method uses the format to parse the result of the proposed insertion. If the result is properly formatted, this method calls its superclass's `insert` method to do the insertion. If the result is not properly formatted, the program beeps.

> In addition to overriding `insertString`, `FormattedDocument` also overrides the `remove` method. Recall that the `remove` method is called each time a character or group of characters is to be removed from the document.

> >     public void remove(int offs, int len)
> >         throws BadLocationException
> >     {
> >         String currentText = getText(0, getLength());
> >         String beforeOffset = currentText.substring(0, offs);
> >         String afterOffset = currentText.substring(len + offs,
> >                                              currentText.length());
> >         String proposedResult = beforeOffset + afterOffset;

> >         try {
> >             if (proposedResult.length() != 0)
> >                 format.parseObject(proposedResult);
> >             super.remove(offs, len);
> >         } catch (ParseException e) {
> >             Toolkit.getDefaultToolkit().beep();
> >             System.err.println("remove: could not parse: "
> >                                + proposedResult);
> >         }
> >     }

> The `FormattedDocument` implementation of the `remove` method is similar to its implementation of the `insertString` method. The format parses the result of the proposed change and performs the removal or not, depending on whether the result is valid.

> > Note: The solution provided by this example is not a general solution for all types of formats. Some formats -- most notably `DateFormat` -- can't be change-validated simply by calling the `parseObject` method. Here's an example to help you understand why. Suppose you have a text field that contains the date "May 25, 1996" and want to change it to "June 11, 1996". You would select May and begin typing "June". As soon as you've typed the "J", the field won't parse because "J 25, 1996" is not a valid date even though it's a valid change. A number of solutions are possible for dates and other types of data where a partially completed change creates an invalid result. You can change the change-validation such that it rejects all definitely invalid changes (typing "X" into a date for example) but allows all possibly valid changes. Or you can switch to an action-validated field.

### Using a Document Listener on a Text Field

> If you can't use a document listener for field validation, what can you use it for? Use it to listen to, but not interfere with, changes to the document's content. The loan calculator uses the following document listener to update the monthly payment after every change:

> >     class MyDocumentListener implements DocumentListener {
> >         public void insertUpdate(DocumentEvent e) {
> >             calculateValue(e);
> >         }
> >         public void removeUpdate(DocumentEvent e) {
> >             calculateValue(e);
> >         }
> >         public void changedUpdate(DocumentEvent e) {
> >             // we won't ever get this with a PlainDocument
> >         }
> >         private void calculateValue(DocumentEvent e) {
> >             Document whatsup = e.getDocument();
> >             if (whatsup.getProperty("name").equals("amount"))
> >                 amount = amountField.getValue();
> >             else if (whatsup.getProperty("name").equals("rate"))
> >                 rate = rateField.getValue();
> >             else if (whatsup.getProperty("name").equals("numPeriods"))
> >                 numPeriods = numPeriodsField.getValue();
> >             payment = computePayment(amount, rate, numPeriods);
> >             paymentField.setValue(payment);
> >         }
> >     }

> This is an appropriate use of a document listener. For general information about document listeners, see Listening for Changes on a Document and How to Write a Document Listener.

### Laying Out Label-Text Field Pairs

> This section describes how the label and the text fields in the example are aligned and requires some knowledge of layout managers.

> Rows of label and text field pairs such as those found in the loan calculator are quite common on preference panels and panels that implement forms. Here's the code that lays out the label and text field pairs.

> >     . . .
> >     //Layout the labels in a panel
> >     JPanel labelPane = new JPanel();
> >     labelPane.setLayout(new GridLayout(0, 1));
> >     labelPane.add(amountLabel);
> >     labelPane.add(rateLabel);
> >     labelPane.add(numPeriodsLabel);
> >     labelPane.add(paymentLabel);

> >     //Layout the text fields in a panel
> >     JPanel fieldPane = new JPanel();
> >     fieldPane.setLayout(new GridLayout(0, 1));
> >     fieldPane.add(amountField);
> >     fieldPane.add(rateField);
> >     fieldPane.add(numPeriodsField);
> >     fieldPane.add(paymentField);

> >     //Put the panels in another panel, labels on left,
> >     //text fields on right
> >     JPanel contentPane = new JPanel();
> >     contentPane.setBorder(BorderFactory.createEmptyBorder(20, 20,
> >                                                           20, 20));
> >     contentPane.setLayout(new BorderLayout());
> >     contentPane.add(labelPane, BorderLayout.CENTER);
> >     contentPane.add(fieldPane, BorderLayout.EAST);

> >     setContentPane(contentPane);
> >     . . .

> You may be surprised to find that the labels are laid out without reference to the text fields and, in fact, are in a different panel, yet align correctly with them. This is a side effect of the layout managers used by the program.

> As the diagram illustrates, the program uses two `GridLayout` managers, one to lay out the column of labels and one for the column of text fields. `GridLayout` guarantees that all of its components are the same size, so all of the text fields are the same height and all of the labels are the same height. But the text fields are not the same height as the labels. This is achieved with a third layout manager, a `BorderLayout`. With just two components at East and Center, `BorderLayout` guarantees the columns are the same height. Now the labels and text fields are the same height, and thus, they are aligned.

> Another way to get labels and text fields to align is to use the AWT's most flexible, complex layout manager: `GridBagLayout`. Refer to the `TextSamplerDemo` program for an example. In particular, look at this handy method, which you can probably copy verbatim into your programs:

> >     addLabelTextRows(JLabel[] labels,
> >     		 JTextField[] textFields,
> >     		 GridBagLayout gridbag,
> >     		 Container container)

### Providing a Password Field

> Swing provides the `JPasswordField` class, a subclass of `JTextField`, to use in place of a text field for password entry. For security reasons, a password field doesn't show the characters the user types. Instead the field displays another character such as an asterisk '*'. As another security precaution, the password field stores its value as an array of characters, rather than as a string.

> `PasswordDemo` brings up a small window to prompt the user to type in a password:

> Here's the code from `PasswordDemo` that creates and sets up the password field.

> >     JPasswordField passwordField = new JPasswordField(10);
> >     passwordField.setEchoChar('#');
> >     passwordField.addActionListener(new ActionListener() {
> >         ...
> >     });

> As with text fields, the argument passed into the `JPasswordField` constructor indicates that the field should be 10 columns wide. By default a password field displays an asterisk '*' for each character typed. The call to `setEchoChar` changes it to a pound sign '#'. Finally, the code adds an action listener to the password field, which action-validates the value typed in by the user. Here's the implementation of the action listener's `actionPerformed` method:

> >     public void actionPerformed(ActionEvent e) {
> >         JPasswordField input = (JPasswordField)e.getSource();
> >         char[] password = input.getPassword();
> >         if (isPasswordCorrect(password)) {
> >     	JOptionPane.showMessageDialog(f,
> >     	    "Success! You typed the right password.");
> >         } else {
> >     	JOptionPane.showMessageDialog(f,
> >     	    "Invalid password. Try again.",
> >     	    "Error Message",
> >     	    JOptionPane.ERROR_MESSAGE);
> >         }
> >     }

> The `getPassword` returns a character array. Password information should not be stored or passed around in strings because strings are not secure.

> A program using a password field typically validates the password before completing any actions requiring the password. This program calls a private method, `isPasswordCorrect`, that compares the value returned by `getPassword` to a value stored in a character array.

> > Note: The `getPassword` method did not exist in Swing 1.0.3 and earlier releases. In those releases, you have to use `getText`. In that case, your program should convert the value returned from `getText` to a character array upon return. You should not store the value in a `String`. In releases that contain the `getPassword` method, `getText` has been deprecated.

### The Text Field and Password Field API

> The following tables list the commonly used `JTextField` and `JPasswordField` constructors and methods. Other methods you are likely to call are defined in `JTextComponent`. Refer to the API tables in Summary of Text.

> You might also invoke methods on a text field or password field that it inherits from its other ancestors, such as `setPreferredSize`, `setForeground`, `setBackground`, `setFont`, and so on. See The JComponent Class for tables of commonly used inherited methods.

> The API for using text fields and password fields falls into these categories:

> - Setting or Getting the Field's Contents
> - Fine Tuning the Field's Appearance
> - Implementing the Field's Functionality

> Setting or Getting the Field's Contents
> Method or Constructor | Purpose
> --- | ---
> `JTextField() JTextField(String) JTextField(String, int) JTextField(int) JTextField(Document, String, int)` | Create a text field. When present, the `int` argument specifies the desired width in columns. The `String` argument contains the field's initial text. The `Document` argument provides a custom document for the field.
> `JPasswordField() JPasswordField(String) JPasswordField(String, int) JPasswordField(int) JPasswordField(Document, String, int)` | Create a password field. When present, the `int` argument specifies the desired width in columns. The `String` argument contains the field's initial text. The `Document` argument provides a custom document for the field.
> `void setText(String) String getText()` | Set or get the text displayed by the text field. Note that `getText` is deprecated for password fields in Swing 1.0.3 and higher releases.
> `char[] getPassword()` (in `JPasswordField`) | Set or get the text displayed by the text field. Note that this method does not exist in Swing 1.0.3 and lower releases.

> Fine Tuning the Field's Appearance
> Method or Constructor | Purpose
> --- | ---
> `void setEditable(boolean) boolean isEditable()` | Set or get whether the user can edit the text in the text field.
> `void setColumns(int); int getColumns()` | Set or get the number of columns displayed by the text field. This is really just a hint for computing the field's preferred width.
> `int getColumnWidth()` | Get the width of the text field's columns. This value is established implicitly by the font.
> `void setHorizontalAlignment(int); int getHorizontalAlignment()` | Set or get how the text is aligned horizontally within its area. You can use `JTextField.LEFT`, `JTextField.CENTER`, and `JTextField.RIGHT` for arguments.
> `void setEchoChar(char) char getEchoChar()` (in `JPasswordField`) | Set or get the echo character -- the character displayed instead of the actual characters typed by the user.

> Implementing the Field's Functionality
> Method or Constructor | Purpose
> --- | ---
> `void addActionListener(ActionListener) void removeActionListener(ActionListener)` | Add or remove an action listener.
> `Document createDefaultModel()` | Override this method to provide a subclass with a custom document.

### Examples that Use Text Fields and Password Fields

> This table shows the examples that use `JTextField` or `JPasswordField` and where those examples are described.

> Example | Where Described | Notes
> --- | --- | ---
> `TextDemo` | This section | An applet that uses a basic text field with an action listener.
> `TextFieldDemo` | This section | Uses and provides implementations for two different types of change-validated fields.
> `PasswordDemo` | This section | Uses an action-validated password field.
> `ControlPane`, `Utilities` | Let's Play | Uses a grid bag layout to align labels with text fields. See the `addParameterRow` method in `Utilities`. Part of the BINGO player application.
> `CustomDialog.java` | How to Make Dialogs | Includes a text field whose value is checked. Part of `DialogDemo` (under the More Dialogs tab).

| Start of Tutorial > Start of Trail > Start of Lesson | Search