Text Input

   

Core Java™ 2: Volume I - Fundamentals
By Cay S. Horstmann, Gary Cornell
Table of Contents
Chapter 9.  User Interface Components with Swing


We are finally ready to start introducing the Swing user interface components. We'll start with components that let a user input and edit text. In Java, two components are used to get text input: text fields and text areas. The difference between them is that a text field can accept only one line of text and a text area can accept multiple lines of text. The classes are called JTextField for single-line input and JTextArea for multiple lines of text.

Both of these classes inherit from a class called JTextComponent. You will not be able to construct a JTextComponent yourself since it is an abstract class. On the other hand, as is so often the case in Java, when you go searching through the API documentation, you may find that the methods you are looking for are actually in the parent class JTextComponent rather than in the derived class. For example, the methods that get or set the text in a text field or text area are actually methods in JTextComponent.

javax.swing.text.JTextComponent 1.2

graphics/api_icon.gif
  • void setText(String t)

    changes the text of a text component.

    Parameters:

    t

    The new text

  • String getText()

    returns the text contained in this text component.

  • void setEditable(boolean b)

    determines whether the user can edit the contents of the JTextComponent.

Text Fields

The usual way to add a text field to a window is to add it to a panel or other container just as you would a button:

 JPanel panel = new JPanel(); JTextField textField = new JTextField("Default input", 20); panel.add(textField); 

This code adds a text field and initializes the text field by placing the string "Default input" inside it. The second parameter of this constructor sets the width. In this case, the width is 20 "columns." Unfortunately, a column is a rather imprecise measurement. One column is the expected width of one character in the font you are using for the text. The idea is that if you expect the inputs to be n characters or less, you are supposed to specify n as the column width. In practice, this measurement doesn't work out too well, and you should add 1 or 2 to the maximum input length to be on the safe side. Also, keep in mind that the number of columns is only a hint to the AWT that gives the preferred size. If the layout manager needs to grow or shrink the text field, it can adjust its size. The column width that you set in the JTextField constructor is not an upper limit on the number of characters the user can enter. The user can still type in longer strings, but the input scrolls when the text exceeds the length of the field. Users tend to find scrolling text fields irritating, so you should size the fields generously. If you need to reset the number of columns at run time, you can do that with the setColumns method.

graphics/exclamatory_icon.gif

After changing the size of a text box with the setColumns method, you need to call the validate method of the surrounding container.

 textField.setColumns(10); panel.validate(); 

The validate method recomputes the size and layout of all components in a container. After you use the validate method, the layout manager repaints the container, and the changed size of the text field will be visible.

In general, you want to let the user add text (or edit the existing text) in a text field. Quite often these text fields start out blank. To make a blank text field, just leave out the string as a parameter for the JTextField constructor:

 JTextField textField = new JTextField(20); 

You can change the contents of the text field at any time by using the setText method from the TextComponent parent class mentioned in the previous section. For example:

 textField.setText("Hello!"); 

And, as was also mentioned in the previous section, you can find out what the user typed by calling the getText method. This method returns the exact text that the user typed. To trim any extraneous leading and trailing spaces from the data in a text field, apply the trim method to the return value of getText:

 String text = textField.getText().trim(); 

To change the font in which the user text appears, use the setFont method.

Let us put a few text fields to work. Figure 9-12 shows the running application. The program shows a clock, and there are two text fields for entering the hours and minutes. Whenever the contents of the text fields change, the clock is updated.

Figure 9-12. Text field example

graphics/09fig12.gif

To track every change in the text field requires a bit of an effort. First of all, note that it is not a good idea to monitor keystrokes. Some keystrokes (such as the arrow keys) don't change the text. And, depending on the look and feel, there may be mouse actions that result in text changes. As you saw in the beginning of this chapter, the Swing text field is implemented in a rather general way: the string that you see in the text field is just a visible manifestation (the view) of an underlying data structure (the model). Of course, for a humble text field, there is no great difference between the two. The view is a displayed string, and the model is a string object. But the same architecture is used in more advanced editing components to present formatted text, with fonts, paragraphs, and other attributes that are internally represented by a more complex data structure. The model for all text components is described by the Document interface, which covers both plain text and formatted text (such as HTML). The point is that you can ask the document (and not the text component) to notify you whenever the data has changed, by installing a document listener:

 textField.getDocument().addDocumentListener(listener); 

When the text has changed, one of the following three methods is called:

 void insertUpdate(DocumentEvent e) void removeUpdate(DocumentEvent e) void changedUpdate(DocumentEvent e) 

The first two methods are called when characters have been inserted or removed. The third method is not called at all for text fields. For more complex document types, it would be called when some other change, such as a change in formatting, has occurred. Unfortunately, there is no single callback to tell you that the text has changed usually you don't care so much how it has changed. And there is no adapter class either. Thus, your document listener must implement all three methods. Here is what we do in our sample program:

 private class ClockFieldListener implements DocumentListener {    public void insertUpdate(DocumentEvent e) { setClock(); }    public void removeUpdate(DocumentEvent e) { setClock(); }    public void changedUpdate(DocumentEvent e) {} } 

The setClock method uses the getText method to obtain the current user input strings from the text fields. Unfortunately, that is what we get: strings. We need to convert the strings to integers, using the familiar, if cumbersome, incantation:

 int hours = Integer.parseInt(hourField.getText().trim()); int minutes = Integer.parseInt(minuteField.getText().trim()); 

But this code won't work right when the user types a noninteger string, such as "two", into the text field or even leaves the field blank. For now, we catch the NumberFormatException that the parseInt method throws, and we simply don't update the clock when the text field entry is not a number. In the next section, you will see how you can prevent the user from entering invalid input in the first place.

graphics/notes_icon.gif

Instead of listening to document events, you can also add an action event listener to a text field. The action listener is notified whenever the user presses the ENTER key. We don't recommend this approach since users don't always remember to press ENTER when they are done entering data. If you use an action listener, you should also install a focus listener so that you can track when the user leaves the text field.

Example 9-2 TextTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.geom.*;   4. import javax.swing.*;   5. import javax.swing.event.*;   6.   7. public class TextTest   8. {   9.    public static void main(String[] args)  10.    {  11.       TextTestFrame frame = new TextTestFrame();  12.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  13.       frame.show();  14.    }  15. }  16.  17. /**  18.    A frame with two text fields to set a clock.  19. */  20. class TextTestFrame extends JFrame  21. {  22.    public TextTestFrame()  23.    {  24.       setTitle("TextTest");  25.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  26.  27.       Container contentPane = getContentPane();  28.  29.       DocumentListener listener = new ClockFieldListener();  30.  31.       // add a panel with text fields  32.  33.       JPanel panel = new JPanel();  34.  35.       hourField = new JTextField("12", 3);  36.       panel.add(hourField);  37.       hourField.getDocument().addDocumentListener(listener);  38.  39.       minuteField = new JTextField("00", 3);  40.       panel.add(minuteField);  41.       minuteField.getDocument().addDocumentListener(listener);  42.  43.       contentPane.add(panel, BorderLayout.SOUTH);  44.  45.       // add the clock  46.  47.       clock = new ClockPanel();  48.       contentPane.add(clock, BorderLayout.CENTER);  49.    }  50.  51.    /**  52.       Set the clock to the values stored in the text fields.  53.    */  54.    public void setClock()  55.    {  56.       try  57.       {  58.          int hours  59.             = Integer.parseInt(hourField.getText().trim());  60.          int minutes  61.             = Integer.parseInt(minuteField.getText().trim());  62.          clock.setTime(hours, minutes);  63.       }  64.       catch (NumberFormatException e) {}  65.       // don't set the clock if the input can't be parsed  66.    }  67.  68.    public static final int DEFAULT_WIDTH = 300;  69.    public static final int DEFAULT_HEIGHT = 300;  70.  71.    private JTextField hourField;  72.    private JTextField minuteField;  73.    private ClockPanel clock;  74.  75.    private class ClockFieldListener implements DocumentListener  76.    {  77.       public void insertUpdate(DocumentEvent e) { setClock(); }  78.       public void removeUpdate(DocumentEvent e) { setClock(); }  79.       public void changedUpdate(DocumentEvent e) {}  80.    }  81. }  82.  83. /**  84.    A panel that draws a clock.  85. */  86. class ClockPanel extends JPanel  87. {  88.    public void paintComponent(Graphics g)  89.    {  90.       // draw the circular boundary  91.   92.       super.paintComponent(g);  93.       Graphics2D g2 = (Graphics2D)g;  94.       Ellipse2D circle  95.          = new Ellipse2D.Double(0, 0, 2 * RADIUS, 2 * RADIUS);  96.       g2.draw(circle);  97.  98.       // draw the hour hand  99. 100.       double hourAngle 101.          = Math.toRadians(90 - 360 * minutes / (12 * 60)); 102.       drawHand(g2, hourAngle, HOUR_HAND_LENGTH); 103. 104.       // draw the minute hand 105. 106.       double minuteAngle 107.          = Math.toRadians(90 - 360 * minutes / 60); 108.       drawHand(g2, minuteAngle, MINUTE_HAND_LENGTH); 109.    } 110. 111.    public void drawHand(Graphics2D g2, 112.       double angle, double handLength) 113.    { 114.       Point2D end = new Point2D.Double( 115.          RADIUS + handLength * Math.cos(angle), 116.          RADIUS - handLength * Math.sin(angle)); 117.       Point2D center = new Point2D.Double(RADIUS, RADIUS); 118.       g2.draw(new Line2D.Double(center, end)); 119.    } 120. 121.    /** 122.       Set the time to be displayed on the clock 123.       @param h hours 124.       @param m minutes 125.    */ 126.    public void setTime(int h, int m) 127.    { 128.       minutes = h * 60 + m; 129.       repaint(); 130.    } 131. 132.    private double minutes = 0; 133.    private double RADIUS = 100; 134.    private double MINUTE_HAND_LENGTH = 0.8 * RADIUS; 135.    private double HOUR_HAND_LENGTH = 0.6 * RADIUS; 136. } 

javax.awt.Component 1.0

graphics/api_icon.gif
  • void validate()

    recomputes the size of a component or the size and layout of the components in a container.

javax.swing.JTextField 1.2

graphics/api_icon.gif
  • JTextField(int cols)

    constructs an empty JTextField with a specified number of columns.

    Parameters:

    cols

    The number of columns in the field

  • JTextField(String text, int cols)

    constructs a new JTextField with an initial string and the specified number of columns.

    Parameters:

    text

    The text to display

     

    cols

    The number of columns

  • void setColumns(int cols)

    tells the text field the number of columns it should use.

    Parameters:

    cols

    The number of columns

javax.swing.text.Document 1.2

graphics/api_icon.gif
  • int getLength()

    returns the number of characters currently in the document.

  • String getText(int offset, int length)

    returns the text contained within the given portion of the document.

    Parameters:

    offset

    The start of the text

     

    length

    The length of the desired string

  • void addDocumentListener(DocumentListener listener)

    registers the listener to be notified when the document changes.

javax.swing.event.DocumentEvent 1.2

graphics/api_icon.gif
  • Document getDocument()

    gets the document that is the source of the event.

javax.swing.event.DocumentListener 1.2

graphics/api_icon.gif
  • void changedUpdate(DocumentEvent e)

    is called whenever an attribute or set of attributes changes.

  • void insertUpdate(DocumentEvent e)

    is called whenever there was an insert into the document.

  • void removeUpdate(DocumentEvent e)

    is called whenever a portion of the document has been removed.

Password Fields

Password fields are a special kind of text field. To avoid nosy bystanders being able to glance at a password, the characters that the user entered are not actually displayed. Instead, each typed character is represented by an echo character, typically an asterisk (*). The Swing set supplies a JPasswordField class that implements such a text field.

The password field is another example of the power of the model-view-controller architecture pattern. The password field uses the same model to store the data as a regular text field, but its view has been changed to display all characters as echo characters.

javax.swing.JPasswordField 1.2

graphics/api_icon.gif
  • JPasswordField(String text, int columns)

    constructs a new password field.

    Parameters:

    text

    The text to be displayed, null if none

     

    columns

    The number of columns

  • void setEchoChar(char echo)

    sets the echo character for this password field. This is advisory; a particular look and feel may insist on its own choice of echo character. A value of 0 resets the echo character to the default.

    Parameters:

    echo

    The echo character to display instead of the text characters

  • char[] getPassword()

    returns the text contained in this password field. For stronger security, you should overwrite the contents of the returned array after use. (The password is not returned as a String because a string would stay in the virtual machine until it is garbage-collected.)

Formatted Input Fields

In the last example program, we wanted the program user to type numbers, not arbitrary strings. That is, the user is allowed to enter only digits 0 through 9 and a hyphen ( ). The hyphen, if present at all, must be the first symbol of the input string.

On the surface, this input validation sounds task simple. We can install a key listener to the text field, and then consume all key events that aren't digits or a hyphen. Unfortunately, this simple approach, although commonly recommended as a method for input validation, does not work well in practice. First, not every combination of the valid input characters is a valid number. For example, --3 and 3-3 aren't valid, even though they are made up from valid input characters. But, more importantly, there are other ways of changing the text that don't involve typing character keys. Depending on the look and feel, certain key combinations can be used to cut, copy, and paste text. For example, in the Metal look and feel, the CTRL+V key combination pastes the contents of the paste buffer into the text field. That is, we also need to monitor that the user doesn't paste in an invalid character. Clearly, trying to filter keystrokes to ensure that the content of the text field is always valid begins to look like a real chore. This is certainly not something that an application programmer should have to worry about.

Perhaps surprisingly, before SDK 1.4, there were no components for entering numeric values. Starting with the first edition of Core Java, we supplied an implementation for an IntTextField, a text field for entering a properly formatted integer. In every new edition, we changed the implementation to take whatever limited advantage we could from the various half-baked validation schemes that were added to each version of the SDK. Finally, in SDK 1.4, the Swing designers faced the issues head-on and supplied a versatile JFormattedTextField class that can be used not just for numeric input, but also for dates, and for even more esoteric formatted values such as IP addresses.

Integer Input

Let's get started with an easy case first: a text field for integer input.

 JFormattedTextField intField = new JFormattedTextField(    NumberFormat.getIntegerInstance()); 

As with any text field, you can set the number of columns:

 intField.setColumns(6); 

Set a default value with the setValue method. That method takes an Object parameter, so you'll need to wrap the default int value into an Integer object:

 intField.setValue(new Integer(100)); 

Typically, users will supply inputs in multiple text fields and then click a button to read all values. When the button is clicked, you can get the value that the user supplied with the getValue method. That method returns an Object result, and you need to cast it into the appropriate type. The JFormattedTextField returns an object of type Long if the user edited the value. However, if the user made no changes, the original Integer object is returned. Therefore, you should cast the return value to the common superclass Number:

 Number value = (Number)intField.getValue(); int v = value.intValue(); 

The formatted text field is not very interesting until you consider what happens when a user provides illegal input. That is the topic of the next section.

Behavior on Loss of Focus

Consider what happens when a user supplies input to a text field. The user types input and eventually decides to leave the field, perhaps by clicking on another component with the mouse. Then the text field loses focus. The I-beam cursor is no longer visible in the text field, and keystrokes are directed towards a different component.

When the formatted text field loses focus, the formatter looks at the text string that the user produced. If the formatter knows how to convert the text string to an object, the text is valid. Otherwise it is invalid. You can use the isEditValid method to check if the current contents of the text field are valid.

The default behavior on loss of focus is called "commit or revert." If the text string is valid, it is committed. The formatter converts it to an object. That object becomes the current value of the field (that is, the return value of the getValue method that you saw in the preceding section). The value is then converted back to a string, which becomes the text string that is visible in the field. For example, the integer formatter recognizes the input 123456 as valid, sets the current value to new Long(123456) and then converts it back into a string with a decimal comma: 123,456.

Conversely, if the text string is invalid, then the current value is not changed and the text field reverts to the string that represents the old value. For example, if the user enters a bad value, such as x123 into an integer first text field, then the old value is restored when the text field loses focus.

graphics/notes_icon.gif

The integer formatter regards a text string as valid if it starts with an integer. For example, 1234x is a valid string. It is converted to the number 1234, which is then formatted back to the string 1,234.

You can set other behaviors with the setFocusLostBehavior method. The "commit" behavior is subtly different from the default. If the text string is invalid, then both the text string and the field value stay unchanged they are now out of synch. The "persist" behavior is even more conservative. Even if the text string is valid, neither the text field nor the current value are changed. You would need to call commitEdit, setValue, or setText to bring them back in synch. Finally, there is a "revert" behavior that doesn't ever seem to be useful. Whenever focus is lost, the user input is disregarded, and the text string reverts to the old value.

graphics/notes_icon.gif

Generally, the "commit or revert" default behavior is reasonable. There is just one potential problem. Suppose a user enters a string " 1234", with a leading space, into an integer text field in a dialog box and then clicks the Ok button. The leading space makes the number invalid, and the field value reverts to the old value. The action listener of the Ok button retrieves the field value and closes the dialog. The user never knows that the new value has been rejected. In this situation, it may be more appropriate to select the "commit" behavior, and have the Ok button listener check that all field edits are valid before closing the dialog.

Filters

This basic functionality of formatted text fields is straightforward and sufficient for most uses. However, you can add a couple of refinements. Perhaps you want to prevent the user from entering non-digits altogether. You achieve that behavior with a document filter. Recall that in the model-view-controller architecture, the controller translates input events into commands that modify the underlying document of the text field, that is, the text string that is stored in a PlainDocument object. For example, whenever the controller processes a command that causes text to be inserted into the document, it calls the "insert string" command. The string to be inserted can be either a single character or the contents of the paste buffer. A document filter can intercept this command and modify the string or cancel the insertion altogether. Here is the code for a filter that analyzes the string to be inserted and inserts only the characters that are digits or a - sign.

 class IntFilter extends DocumentFilter {    public void insertString(FilterBypass fb, int offset,       String string, AttributeSet attr)       throws BadLocationException    {       StringBuffer buffer = new StringBuffer();       for (int i = buffer.length() - 1; i >= 0; i--)       {          char ch = buffer.charAt(i);          if (!Character.isDigit(ch) && ch != '-')             buffer.deleteChar(i);       }       super.insertString(fb, offset,          buffer.toString(), attr);    } } 

You should also override the replace method of the DocumentFilter class it is called when text is selected and then replaced. The implementation of the replace method is straightforward see the program at the end of this section.

Now you need to install the document filter. Unfortunately, there is no straightforward method to do that. You need to override the getDocumentFilter method of a formatter class, and pass an object of that formatter class to the JFormattedTextField. The integer text field uses an InternationalFormatter that is initialized with NumberFormat.getIntegerInstance(). Here is how you install a formatter to yield the desired filter:

 JFormattedTextField intField    = new JFormattedTextField(new       InternationalFormatter(          NumberFormat.getIntegerInstance())       {          protected DocumentFilter getDocumentFilter()          {             return filter;          }          private DocumentFilter filter = new IntFilter();       }); 

graphics/notes_icon.gif

The SDK documentation states that the DocumentFilter class was invented to avoid subclassing. Until SDK 1.3, filtering in a text field was achieved by extending the PlainDocument class and overriding the insertString and replace methods. Now the PlainDocument class has a pluggable filter instead. That is a splendid improvement. It would have been even more splendid if the filter had also been made pluggable in the formatter class. Alas, it was not, and we must subclass the formatter.

Try out the FormatTest example program at the end of this section. The third text field has a filter installed. You can insert only digits or the minus ('-') character. Note that you can still enter invalid strings such as "1-2-3". In general, it is impossible to avoid all invalid strings through filtering. For example, the string "-" is invalid, but a filter can't reject it since it is a prefix of a legal string "-1". Even though filters can't give perfect protection, it makes sense to use them to reject inputs that are obviously invalid.

graphics/exclamatory_icon.gif

Another use for filtering is to turn all characters of a string to uppercase. Such a filter is easy to write. In the insertString and replace methods of the filter, convert the string to be inserted to uppercase, and then invoke the superclass method.

Verifiers

There is another potentially useful mechanism to alert users to invalid inputs. You can attach a verifier to any JComponent. If the component loses focus, then the verifier is queried. If the verifier reports the contents of the component to be invalid, the component immediately regains focus. The user is thus forced to fix the contents before supplying other inputs.

A verifier must extend the abstract InputVerifier class and define a verify method. It is particularly easy to define a verifier that checks formatted text fields. The isEditValid method of the JFormattedTextField class calls the formatter and returns true if the formatter can turn the text string into an object. Here is the verifier.

 class FormattedTextFieldVerifier extends InputVerifier {    public boolean verify(JComponent component)    {       JFormattedTextField field = (JFormattedTextField)component;       return field.isEditValid();    } } 

You can attach it to any JFormattedTextField:

 intField.setInputVerifier(new FormattedTextFieldVerifier()); 

However, a verifier is not entirely foolproof. If you click on a button, then the button notifies its action listeners before an invalid component regains focus. The action listeners can then get an invalid result from the component that failed verification. There is a reason for this behavior: users may want to hit a "Cancel" button without first having to fix an invalid input.

The fourth text field in the example program has a verifier attached. Try entering an invalid number (such as x123) and hit the TAB key or click with the mouse on another text field. Note that the field immediately regains focus. However, if you hit the Ok button, the action listener calls getValue, which reports the last good value.

Other Standard Formatters

Besides the integer formatter, the JFormattedTextField supports several other formatters. The NumberFormat class has static methods

 GetNumberInstance GetCurrencyInstance getPercentInstance 

that yield formatters of floating-point numbers, currency values, and percentages. For example, you can obtain a text field for the input of currency values by calling

 JFormattedTextField currencyField = new JFormattedTextField(    NumberFormat.getCurrencyInstance()); 

To edit dates and times, call one of the static methods

 GetDateInstance GetTimeInstance getDateTimeInstance 

of the DateFormat class. For example,

 JFormattedTextField dateField = new JFormattedTextField(    DateFormat.getDateInstance()); 

This field edits a date in the default or "medium" format such as

 Feb 24, 2002 

You can choose a "short" format such as

 2/24/02 

by calling

 DateFormat.getDateInstance(DateFormat.SHORT) 

instead.

graphics/notes_icon.gif

By default, the date format is "lenient." That is, an invalid date such as February 31, 2002, is rolled over to the next valid date, March 3, 2002. That behavior may be surprising to your users. In that case, call setLenient(false) on the DateFormat object.

The DefaultFormatter can format objects of any class that has a constructor with a string parameter and a matching toString method. For example, the URL class has a URL(String) constructor that can be used to construct an URL from a string, such as

 URL url = new URL("http://java.sun.com"); 

Therefore, you can use the DefaultFormatter to format URL objects. The formatter calls toString on the field value to initialize the field text. When the field loses focus, the formatter constructs a new object of the same class as the current value, using the constructor with a String parameter. If that constructor throws an exception, then the edit is not valid. You can try that out in the example program, by entering an URL that does not start with a prefix such as "http:".

graphics/notes_icon.gif

By default, the DefaultFormatter is in overwrite mode. That is different from the other formatters and not very useful. Call setOverwriteMode(false) to turn overwrite mode off.

Finally, the MaskFormatter is useful for fixed-size patterns that contain some constant and some variable characters. For example, social security numbers (such as 078-05-1120) can be formatted with a

 new MaskFormatter("###-##-####") 

The # symbol denotes a single digit. Table 9-2 shows the symbols that you can use in a mask formatter.

Table 9-2. MaskFormatter symbols

#

A digit

?

A letter

U

A letter, converted to uppercase

L

A letter, converted to lowercase

A

A letter or digit

H

A hexadecimal digit [0-9A-Fa-f]

*

Any character

'

Escape character to include a symbol in the pattern

You can restrict the characters that can be typed into the field by calling one of the methods

 setValidCharacters setInvalidCharacters 

of the MaskFormatter class. For example, to read in a letter grade (such as A+ or F), you could use

 MaskFormatter formatter = new MaskFormatter("U*"); formatter.setValidCharacters("ABCDF+- "); 

However, there is no way of specifying that the second character cannot be a letter.

Note that the string that is formatted by the mask formatter has exactly the same length as the mask. If the user erases characters during editing, then they are replaced with the placeholder character. The default placeholder character is a space, but you can change it with the setPlaceholderCharacter method, for example

 formatter.setPlaceholderCharacter('0'); 

By default, a mask formatter is in overtype mode, which is quite intuitive try it out in the example program. Also note that the caret position jumps over the fixed characters in the mask.

The mask formatter is very effective for rigid patterns such as social security numbers or American telephone numbers. However, note that no variation is permitted in the mask pattern at all. For example, you cannot use a mask formatter for international telephone numbers that have a variable number of digits.

Custom Formatters

If none of the standard formatters is appropriate, it is fairly easy to define your own formatter. Consider 4-byte IP addresses such as

 130.65.86.66 

You can't use a MaskFormatter because each byte might be represented by one, two, or three digits. Also, we want to check in the formatter that each byte's value is at most 255.

To define your own formatter, extend the DefaultFormatter class and override the methods

 String valueToString(Object value) Object stringToValue(String text) 

The first method turns the field value into the string that is displayed in the text field. The second method parses the text that the user typed and turns it back into an object. If either method detects an error, it should throw a ParseException.

In our example program, we store an IP address in a byte[] array of length 4. The valueToString method forms a string that separates the bytes with periods. Note that byte values are signed quantities between -128 and 127. To turn negative byte values into unsigned integer values, you add 256.

 public String valueToString(Object value) throws ParseException {    if (!(value instanceof byte[]))       throw new ParseException("Not a byte[]", 0);    byte[] a = (byte[])value;    if (a.length != 4)        throw new ParseException("Length != 4", 0);    StringBuffer buffer = new StringBuffer();    for (int i = 0; i < 4; i++)    {       int b = a[i];       if (b < 0) b += 256;       buffer.append(String.valueOf(b));       if (i < 3) buffer.append('.');    }    return buffer.toString(); } 

Conversely, the stringToValue method parses the string and produces a byte[] object if the string is valid. If not, it throws a ParseException.

 public Object stringToValue(String text) throws ParseException {    StringTokenizer tokenizer = new StringTokenizer(text, ".");    byte[] a = new byte[4];    for (int i = 0; i < 4; i++)    {       int b = 0;       try       {          b = Integer.parseInt(tokenizer.nextToken());       }       catch (NumberFormatException e)       {          throw new ParseException("Not an integer", 0);       }       if (b < 0 || b >= 256)          throw new ParseException("Byte out of range", 0);       a[i] = (byte)b;    }    return a; } 

Try out the IP address field in the sample program. If you enter an invalid address, the field reverts to the last valid address.

The program in Example 9-3 shows various formatted text fields in action (see Figure 9-13). Click on the Ok button to retrieve the current values from the fields.

Figure 9-13. The FormatTest program

graphics/09fig13.gif

graphics/notes_icon.gif

The "Swing Connection" online newsletter has a short article describing a formatter that matches any regular expression. See http://java.sun.com/products/jfc/tsc/articles/reftf/.

Example 9-3 FormatTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.lang.reflect.*;   4. import java.net.*;   5. import java.text.*;   6. import java.util.*;   7. import javax.swing.*;   8. import javax.swing.text.*;   9.  10. /**  11.    A program to test formatted text fields  12. */  13. public class FormatTest  14. {  15.    public static void main(String[] args)  16.    {  17.       FormatTestFrame frame = new FormatTestFrame();  18.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  19.       frame.show();  20.    }  21. }  22.  23. /**  24.    A frame with a collection of formatted bext fields and  25.    a button that displays the field values.  26. */  27. class FormatTestFrame extends JFrame  28. {  29.    public FormatTestFrame()  30.    {  31.       setTitle("FormatTest");  32.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  33.  34.       Container contentPane = getContentPane();  35.  36.       JPanel buttonPanel = new JPanel();  37.       okButton = new JButton("Ok");  38.       buttonPanel.add(okButton);  39.       contentPane.add(buttonPanel, BorderLayout.SOUTH);  40.  41.       mainPanel = new JPanel();  42.       mainPanel.setLayout(new GridLayout(0, 3));  43.       contentPane.add(mainPanel, BorderLayout.CENTER);  44.  45.       JFormattedTextField intField = new JFormattedTextField(  46.          NumberFormat.getIntegerInstance());  47.       intField.setValue(new Integer(100));  48.       addRow("Number:", intField);  49.  50.       JFormattedTextField intField2 = new JFormattedTextField(  51.          NumberFormat.getIntegerInstance());  52.       intField2.setValue(new Integer(100));  53.       intField2.setFocusLostBehavior(JFormattedTextField.COMMIT);  54.       addRow("Number (Commit behavior):", intField2);  55.  56.       JFormattedTextField intField3  57.          = new JFormattedTextField(new  58.             InternationalFormatter(  59.                NumberFormat.getIntegerInstance())  60.             {  61.                protected DocumentFilter getDocumentFilter()  62.                {  63.                   return filter;  64.                }  65.                private DocumentFilter filter  66.                   = new IntFilter();  67.             });  68.       intField3.setValue(new Integer(100));  69.       addRow("Filtered Number", intField3);  70.  71.       JFormattedTextField intField4 = new JFormattedTextField(  72.          NumberFormat.getIntegerInstance());  73.       intField4.setValue(new Integer(100));  74.       intField4.setInputVerifier(  75.          new FormattedTextFieldVerifier());  76.       addRow("Verified Number:", intField4);  77.  78.       JFormattedTextField currencyField = new  79.          JFormattedTextField(NumberFormat.getCurrencyInstance());  80.       currencyField.setValue(new Double(10));  81.       addRow("Currency:", currencyField);  82.  83.       JFormattedTextField dateField = new JFormattedTextField(  84.          DateFormat.getDateInstance());  85.       dateField.setValue(new Date());  86.       addRow("Date (default):", dateField);  87.  88.       DateFormat format =  89.          DateFormat.getDateInstance(DateFormat.SHORT);  90.       format.setLenient(false);  91.       JFormattedTextField dateField2 = new JFormattedTextField(  92.          format);  93.       dateField2.setValue(new Date());  94.       addRow("Date (short, not lenient):", dateField2);  95.  96.       try  97.       {  98.          DefaultFormatter formatter = new DefaultFormatter();  99.          formatter.setOverwriteMode(false); 100.          JFormattedTextField urlField = new JFormattedTextField( 101.             formatter); 102.          urlField.setValue(new URL("http://java.sun.com")); 103.          addRow("URL:", urlField); 104.       } 105.       catch (MalformedURLException exception) 106.       { 107.          exception.printStackTrace(); 108.       } 109. 110.       try 111.       { 112.          MaskFormatter formatter 113.             = new MaskFormatter("###-##-####"); 114.          formatter.setPlaceholderCharacter('0'); 115.          JFormattedTextField ssnField 116.             = new JFormattedTextField(formatter); 117.          ssnField.setValue("078-05-1120"); 118.          addRow("SSN Mask:", ssnField); 119.       } 120.       catch (ParseException exception) 121.       { 122.          exception.printStackTrace(); 123.       } 124. 125.       JFormattedTextField ipField 126.          = new JFormattedTextField(new IPAddressFormatter()); 127.       ipField.setValue(new byte[] { (byte)130, 65, 86, 66 }); 128.       addRow("IP Address:", ipField); 129.    } 130. 131.    /** 132.       Adds a row to the main panel. 133.       @param labelText the label of the field 134.       @param field the sample field 135.    */ 136.    public void addRow(String labelText, 137.       final JFormattedTextField field) 138.    { 139.       mainPanel.add(new JLabel(labelText)); 140.       mainPanel.add(field); 141.       final JLabel valueLabel = new JLabel(); 142.       mainPanel.add(valueLabel); 143.       okButton.addActionListener(new 144.          ActionListener() 145.          { 146.             public void actionPerformed(ActionEvent event) 147.             { 148.                Object value = field.getValue(); 149.                if (value.getClass().isArray()) 150.                { 151.                   StringBuffer buffer = new StringBuffer(); 152.                   buffer.append('{'); 153.                   for (int i = 0; i < Array.getLength(value); 154.                        i++) 155.                   { 156.                      if (i > 0) buffer.append(','); 157.                      buffer.append( 158.                         Array.get(value, i).toString()); 159.                   } 160.                   buffer.append('}'); 161.                   valueLabel.setText(buffer.toString()); 162.                } 163.                else 164.                   valueLabel.setText(value.toString()); 165.             } 166.          }); 167.    } 168. 169.    public static final int DEFAULT_WIDTH = 500; 170.    public static final int DEFAULT_HEIGHT = 250; 171. 172.    private JButton okButton; 173.    private JPanel mainPanel; 174. } 175. 176. /** 177.    A filter that restricts input to digits and a '-' sign. 178. */ 179. class IntFilter extends DocumentFilter 180. { 181.    public void insertString(FilterBypass fb, int offset, 182.       String string, AttributeSet attr) 183.       throws BadLocationException 184.    { 185.       int i = 0; 186.       while (i < string.length()) 187.       { 188.          char ch = string.charAt(i); 189.          if (Character.isDigit(ch) || ch == '-') i++; 190.       } 191.       super.insertString(fb, offset, 192.          string.substring(0, i), attr); 193.    } 194. 195.    public void replace(FilterBypass fb, int offset, 196.       int length, String string, AttributeSet attr) 197.       throws BadLocationException 198.    { 199.       if (string != null) 200.       { 201.          int i = 0; 202.          while (i < string.length()) 203.          { 204.             char ch = string.charAt(i); 205.             if (Character.isDigit(ch) || ch == '-') i++; 206.          } 207.          string = string.substring(0, i); 208.       } 209.       super.replace(fb, offset, length, string, attr); 210.    } 211. } 212. 213. /** 214.    A verifier that checks whether the contents of 215.    a formatted text field is valid. 216. */ 217. class FormattedTextFieldVerifier extends InputVerifier 218. { 219.    public boolean verify(JComponent component) 220.    { 221.       JFormattedTextField field = (JFormattedTextField)component; 222.       return field.isEditValid(); 223.    } 224. } 225. 226. /** 227.    A formatter for 4-byte IP addresses of the form a.b.c.d 228. */ 229. class IPAddressFormatter extends DefaultFormatter 230. { 231.    public String valueToString(Object value) 232.       throws ParseException 233.    { 234.       if (!(value instanceof byte[])) 235.          throw new ParseException("Not a byte[]", 0); 236.       byte[] a = (byte[])value; 237.       if (a.length != 4) 238.          throw new ParseException("Length != 4", 0); 239.       StringBuffer buffer = new StringBuffer(); 240.       for (int i = 0; i < 4; i++) 241.       { 242.          int b = a[i]; 243.          if (b < 0) b += 256; 244.          buffer.append(String.valueOf(b)); 245.          if (i < 3) buffer.append('.'); 246.       } 247.       return buffer.toString(); 248.    } 249. 250.    public Object stringToValue(String text) throws ParseException 251.    { 252.       StringTokenizer tokenizer = new StringTokenizer(text, "."); 253.       byte[] a = new byte[4]; 254.       for (int i = 0; i < 4; i++) 255.       { 256.          int b = 0; 257.          try 258.          { 259.             b = Integer.parseInt(tokenizer.nextToken()); 260.          } 261.          catch (NumberFormatException e) 262.          { 263.             throw new ParseException("Not an integer", 0); 264.          } 265.          if (b < 0 || b >= 256) 266.             throw new ParseException("Byte out of range", 0); 267.          a[i] = (byte)b; 268.       } 269.       return a; 270.    } 271. } 272. 

javax.swing.JFormattedTextField 1.4

graphics/api_icon.gif
  • JFormattedTextField(Format fmt)

    constructs a text field that uses the specified format.

  • JFormattedTextField(JFormattedTextField.AbstractFormatter formatter)

    constructs a text field that uses the specified formatter. Note that DefaultFormatter and InternationalFormatter are subclasses of JFormattedTextField.AbstractFormatter.

  • Object getValue()

    returns the current valid value of the field. Note that this may not correspond to the string that is being edited.

  • void setValue(Object value)

    attempts to set the value of the given object. The attempt fails if the formatter cannot convert the object to a string.

  • void commitEdit()

    attempts to set the valid value of the field from the edited string. The attempt may fail if the formatter cannot convert the string.

  • boolean isEditValid()

    checks whether the edited string represents a valid value.

  • void setFocusLostBehavior(int behavior)

  • int getFocusLostBehavior()

    set or get the "focus lost" behavior. Legal values for behavior are the constants COMMIT_OR_REVERT, REVERT, COMMIT, and PERSIST of the JFormattedTextField class.

javax.text.DateFormat 1.1

graphics/api_icon.gif
  • static DateFormat getDateInstance()

  • static DateFormat getDateInstance(int dateStyle)

  • static DateFormat getTimeInstance()

  • static DateFormat getTimeInstance(int timeStyle)

  • static DateFormat getDateTimeInstance()

  • static DateFormat getDateTimeInstance(int dateStyle, int timeStyle)

    These methods return formatters that yield the date, time, or both date and time of Date objects. Legal values for dateStyle and timeStyle are the constants SHORT, MEDIUM, LONG, FULL, and DEFAULT of the DateFormat class.

javax.swing.JFormattedTextField.AbstractFormatter 1.4

graphics/api_icon.gif
  • abstract String valueToString(Object value)

    converts a value to an editable string. Throws a ParseException if value is not appropriate for this formatter.

  • abstract Object stringToValue(String s)

    converts a string to a value. Throws a ParseException if s is not in the appropriate format.

  • DocumentFilter getDocumentFilter()

    Override this method to provide a document filter that restricts inputs into the text field. A return value of null indicates that no filtering is needed.

javax.swing.text.DefaultFormatter 1.3

graphics/api_icon.gif
  • void setOverwriteMode(boolean mode)

  • boolean getOverwriteMode()

    set or get the overwrite mode. If mode is true, then new characters overwrite existing characters when editing text.

javax.swing.text.DocumentFilter 1.4

graphics/api_icon.gif
  • void insertString(DocumentFilter.FilterBypass bypass, int offset, String text, AttributeSet attrib)

    This method is invoked before inserting a string into a document. You can override the method and modify the string. You can disable insertion by not calling super.insertString, or by calling bypass methods to modify the document without filtering.

    Parameters:

    bypass

    an object that allows you to execute edit commands that bypass the filter

     

    offset

    the offset at which to insert the text

     

    text

    the characters to insert

     

    attrib

    the formatting attributes of the inserted text

  • void replace(DocumentFilter.FilterBypass bypass, int offset, int length, String text, AttributeSet attrib)

    This method is invoked before replacing a part of a document with a new string. You can override the method and modify the string. You can disable replacement by not calling super.replace, or by calling bypass methods to modify the document without filtering.

    Parameters:

    bypass

    an object that allows you to execute edit commands that bypass the filter

     

    offset

    the offset at which to insert the text

     

    length

    the length of the part to be replaced

     

    text

    the characters to insert

     

    attrib

    the formatting attributes of the inserted text

  • void remove(DocumentFilter.FilterBypass bypass, int offset, int length)

    This method is invoked before removing a part of a document with a new string. Get the document by calling bypass.getDocument() if you need to analyze the effect of the removal.

    Parameters:

    bypass

    an object that allows you to execute edit commands that bypass the filter

     

    offset

    the offset of the part to be removed

     

    length

    the length of the part to be removed

javax.swing.text.MaskFormatter 1.4

graphics/api_icon.gif
  • MaskFormatter(String mask)

    constructs a mask formatter with the given mask. See Table 9-2 for the symbols in a mask.

  • void setValidCharacters(String characters)

  • String getValidCharacters()

    set or get the valid editing characters. Only the characters in the given string are accepted for the variable parts of the mask.

  • void setInvalidCharacters(String characters)

  • String getInvalidCharacters()

    set or get the invalid editing characters. None of the characters in the given string are accepted as input.

  • void setPlaceholderCharacter(char ch)

  • char getPlaceholderCharacter()

    set or get the placeholder character that is used for variable characters in the mask that the user has not yet supplied. The default placeholder character is a space.

  • void setPlaceholder(String s)

  • String getPlaceholder()

    set or get the placeholder string. Its tail end is used if the user has not supplied all variable characters in the mask. If it is null or shorter than the mask, then the placeholder character is used to fill remaining inputs.

  • void setValueContainsLiteralCharacters(boolean b)

  • boolean getValueContainsLiteralCharacters()

    set or get the "value contains literal characters" flag. If this flag is true, then the field value contains the literal (non-variable) parts of the mask. If it is false, then the literal characters are removed. The default is true.

Text Areas

Sometimes, you need to collect user input that is more than one line long. As mentioned earlier, you use the JTextArea component for this collection. When you place a text area component in your program, a user can enter any number of lines of text, using the ENTER key to separate them. Each line ends with a '\n' as far as Java is concerned. If you need to break up what the user enters into separate lines, you can use the StringTokenizer class (see Chapter 12). Figure 9-14 shows a text area at work.

Figure 9-14. A text area

graphics/09fig14.gif

In the constructor for the JTextArea component, you specify the number of rows and columns for the text area. For example:

 textArea = new JTextArea(8, 40); // 8 lines of 40 columns each contentPane.add(textArea); 

where the columns parameter works as before and you still need to add a few more columns for safety's sake. Also, as before, the user is not restricted to the number of rows and columns; the text simply scrolls when the user inputs too much. You can also use the setColumns method to change the number of columns, and the setRows method to change the number of rows. These numbers only indicate the preferred size the layout manager can still grow or shrink the text area.

If there is more text than the text area can display, then the remaining text is simply clipped. You can avoid clipping long lines by turning on line-wrapping:

 textArea.setLineWrap(true); // long lines are wrapped 

This wrapping is a visual effect only; the text in the document is not changed no '\n' characters are inserted into the text.

In Swing, a text area does not have scroll bars. If you want scroll bars, you have to insert the text area inside a scroll pane. Then, insert the scroll pane inside the content pane.

 textArea = new JTextArea(8, 40); JScrollPane scrollPane = new JScrollPane(textArea); contentPane.add(scrollPane, BorderLayout.CENTER); 

The scroll pane now manages the view of the text area. Scroll bars automatically appear if there is more text than the text area can display, and they vanish again if text is deleted and the remaining text fits inside the area. The scrolling is handled internally in the scroll pane your program does not need to process scroll events.

graphics/exclamatory_icon.gif

This is a general mechanism that you will encounter many times when working with Swing to add scroll bars to a component, put them inside a scroll pane.

Example 9-4 is the complete code for the text area demo. This program simply lets you edit text in a text area. Click on "Insert" to insert a sentence at the end of the text. Click the second button to turn line-wrapping on and off. (Its name toggles between "Wrap" and "No wrap.") Of course, you can simply use the keyboard to edit the text in the text area. Note how you can highlight a section of text, and how you can cut, copy, and paste with the CTRL+X, CTRL+C, and CTRL+V keys. (Keyboard shortcuts are specific to the look and feel. These particular key combinations work for the Metal, Windows, and Mac look and feel.)

graphics/notes_icon.gif

The JTextArea component displays plain text only, without special fonts or formatting. To display formatted text (such as HTML or RTF), you can use the JEditorPane and JTextPane classes. These classes are discussed in Volume 2.

Example 9-4 TextAreaTest.java
  1. import java.awt.*;  2. import java.awt.event.*;  3. import javax.swing.*;  4.  5. public class TextAreaTest  6. {  7.    public static void main(String[] args)  8.    {  9.       TextAreaFrame frame = new TextAreaFrame(); 10.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11.       frame.show(); 12.    } 13. } 14. 15. /** 16.    A frame with a text area and buttons for text editing 17. */ 18. class TextAreaFrame extends JFrame 19. { 20.    public TextAreaFrame() 21.    { 22.       setTitle("TextAreaTest"); 23.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 24. 25.       Container contentPane = getContentPane(); 26. 27.       buttonPanel = new JPanel(); 28. 29.       // add button to append text into the text area 30. 31.       JButton insertButton = new JButton("Insert"); 32.       buttonPanel.add(insertButton); 33.       insertButton.addActionListener(new 34.          ActionListener() 35.          { 36.             public void actionPerformed(ActionEvent event) 37.             { 38.                textArea.append("The quick brown fox " 39.                   + "jumps over the lazy dog. "); 40.             } 41.          }); 42. 43.       // add button to turn line wrapping on and off 44. 45.       wrapButton = new JButton("Wrap"); 46.       buttonPanel.add(wrapButton); 47.       wrapButton.addActionListener(new 48.          ActionListener() 49.          { 50.             public void actionPerformed(ActionEvent evt) 51.             { 52.                boolean wrap = !textArea.getLineWrap(); 53.                textArea.setLineWrap(wrap); 54.                scrollPane.validate(); 55.                wrapButton.setText(wrap ? "No Wrap" : "Wrap"); 56.             } 57.          }); 58. 59.       contentPane.add(buttonPanel, BorderLayout.SOUTH); 60. 61.       // add a text area with scroll bars 62. 63.       textArea = new JTextArea(8, 40); 64.       scrollPane = new JScrollPane(textArea); 65. 66.       contentPane.add(scrollPane, BorderLayout.CENTER); 67.    } 68. 69.    public static final int DEFAULT_WIDTH = 300; 70.    public static final int DEFAULT_HEIGHT = 300; 71. 72.    private JTextArea textArea; 73.    private JScrollPane scrollPane; 74.    private JPanel buttonPanel; 75.    private JButton wrapButton; 76. } 

javax.swing.JTextArea 1.2

graphics/api_icon.gif
  • JTextArea(int rows, int cols)

    constructs a new text area.

    Parameters:

    rows

    The number of rows

     

    cols

    The number of columns

  • JTextArea(String text, int rows, int cols)

    constructs a new text area with an initial text.

    Parameters:

    text

    The initial text

     

    rows

    The number of rows

     

    cols

    The number of columns

  • void setColumns(int cols)

    tells the text area the preferred number of columns it should use.

    Parameters:

    cols

    The number of columns

  • void setRows(int rows)

    tells the text area the preferred number of rows it should use.

    Parameters:

    rows

    The number of rows

  • void append(String newText)

    appends the given text to the end of the text already in the text area.

    Parameters:

    newText

    The text to append

  • void setLineWrap(boolean wrap)

    turns line-wrapping on or off.

    Parameters:

    wrap

    true if lines should be wrapped

  • void setWrapStyleWord(boolean word)

    If word is true, then long lines are wrapped at word boundaries. If it is false, then long lines are broken without taking word boundaries into account.

  • void setTabSize(int c)

    sets tab stops every c columns. Note that the tabs aren't converted to spaces, but they cause alignment with the next tab stop.

    Parameters:

    c

    The number of columns for a tab stop

javax.swing.JScrollPane 1.2

graphics/api_icon.gif
  • JScrollPane(Component c)

    creates a scroll pane that displays the contents of the specified component. Scroll bars are supplied when the component is larger than the view.

    Parameters:

    c

    The component to scroll

Labels and Labeling Components

Labels are components that hold text. They have no decorations (for example, no boundaries). They also do not react to user input. You can use a label to identify components. For example, unlike buttons, text components have no label to identify them. To label a component that does not itself come with an identifier:

  1. Construct a JLabel component with the correct text.

  2. Place it close enough to the component you want to identify so that the user can see that the label identifies the correct component.

The constructor for a JLabel lets you specify the initial text or icon, and optionally, the alignment of the contents. You use constants from the SwingConstants interface to specify alignment. That interface defines a number of useful constants such as LEFT, RIGHT, CENTER, NORTH, EAST, and so on. The JLabel class is one of several Swing classes that implements this interface. Therefore, you can specify a left-aligned label either as:

 JLabel label = new JLabel("Text", SwingConstants.LEFT); 

or

 JLabel label = new JLabel("Text", JLabel.LEFT); 

The setText and setIcon methods let you set the text and icon of the label at run time.

graphics/exclamatory_icon.gif

Beginning with J2SE 1.3, you can use both plain and HTML text in buttons, labels, and menu items. We don't recommend HTML in buttons it interferes with the look and feel. But HTML in labels can be very effective. Simply surround the label string with <HTML>. . .</HTML>, like this:

 label = new JLabel("<HTML><B>Required</B> entry:</HTML>"); 

Fair warning the first component with an HTML label takes some time to be displayed because the rather complex HTML rendering code must be loaded.

Labels can be positioned inside a container like any other component. This means you can use the techniques you have seen before to place labels where you need them. For example, if you look at Figure 9-15, you can see how one of the text fields is preceded by a label with the text "with."

Figure 9-15. Testing text editing

graphics/09fig15.gif

javax.swing.JLabel 1.2

graphics/api_icon.gif
  • JLabel(String text)

    constructs a label with left-aligned text.

    Parameters:

    text

    The text in the label

  • JLabel(Icon icon)

    constructs a label with a left-aligned icon.

    Parameters:

    icon

    The icon in the label

  • JLabel(String text, int align)

    Parameters:

    text

    The text in the label

     

    align

    One of SwingConstants.LEFT, SwingConstants.CENTER, or SwingConstants.RIGHT

  • JLabel(String text, Icon icon, int align)

    constructs a label with both text and an icon. The icon is to the left of the text.

    Parameters:

    text

    The text in the label

     

    icon

    The icon in the label

     

    align

    One of SwingConstants.LEFT, SwingConstants.CENTER, or SwingConstants.RIGHT

  • void setText(String text)

    Parameters:

    text

    The text in the label

  • void setIcon(Icon icon)

    Parameters:

    icon

    The icon in the label

Selecting and Editing Text

The text field and text area classes inherit methods from the JTextComponent superclass to select (highlight) the text contained in the component. They can also check for text that is currently selected.

First, there is the selectAll method, which highlights all the text in the field. You would use this method when presenting users with an input that they either will want to use exactly as provided or that they won't want to use at all. In the latter case, they can just type their own input, and the first keystroke replaces the selection.

The select method selects a part of the text. The arguments of select are the same as for substring: the first index is the start of the substring; the last is one more than the end. For example, t.select(10, 15) selects the tenth to fourteenth characters in the text control. End-of-line markers count as one character.

The getSelectionStart and getSelectionEnd methods return the start and end of the current selection, and getSelectedText returns the highlighted text. How users highlight text is system dependent. In the Metal look and feel, you can use the mouse or the standard SHIFT + arrow keys.

The JTextArea class contains a number of methods to modify the contents of a text area. You can append text at the end, insert text in the middle, and replace text. To delete text, simply replace the text to be deleted with an empty string. Example 9-5 shows how to implement a simple find-and-replace feature. In the program illustrated in Figure 9-15, each time you click on the Replace button, the first match of the text in the first field is replaced by the text in the second field. This is not a very realistic application, but you could use this feature to correct spelling or typing errors in URLs.

Example 9-5 TextEditTest.java
  1. import java.awt.*;  2. import java.awt.event.*;  3. import javax.swing.*;  4.  5. public class TextEditTest  6. {  7.    public static void main(String[] args)  8.    {  9.       TextEditFrame frame = new TextEditFrame(); 10.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11.       frame.show(); 12.    } 13. } 14. 15. /** 16.    A frame with a text area and components for search/replace. 17. */ 18. class TextEditFrame extends JFrame 19. { 20.    public TextEditFrame() 21.    { 22.       setTitle("TextEditTest"); 23.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 24. 25.       Container contentPane = getContentPane(); 26. 27.       JPanel panel = new JPanel(); 28. 29.       // add button, text fields and labels 30. 31.       JButton replaceButton = new JButton("Replace"); 32.       panel.add(replaceButton); 33.       replaceButton.addActionListener(new ReplaceAction()); 34. 35.       from = new JTextField("brown", 8); 36.       panel.add(from); 37. 38.       panel.add(new JLabel("with")); 39. 40.       to = new JTextField("purple", 8); 41.       panel.add(to); 42.       contentPane.add(panel, BorderLayout.SOUTH); 43. 44.       // add text area with scroll bars 45. 46.       textArea = new JTextArea(8, 40); 47.       textArea.setText 48.          ("The quick brown fox jumps over the lazy dog."); 49.       JScrollPane scrollPane = new JScrollPane(textArea); 50.       contentPane.add(scrollPane, BorderLayout.CENTER); 51.    } 52. 53.    public static final int DEFAULT_WIDTH = 400; 54.    public static final int DEFAULT_HEIGHT = 200; 55. 56.    private JTextArea textArea; 57.    private JTextField from; 58.    private JTextField to; 59. 60.    /** 61.       The action listener for the replace button. 62.    */ 63.    private class ReplaceAction implements ActionListener 64.    { 65.       public void actionPerformed(ActionEvent event) 66.       { 67.          String f = from.getText(); 68.          int n = textArea.getText().indexOf(f); 69.          if (n >= 0 && f.length() > 0) 70.             textArea.replaceRange(to.getText(), n, 71.                n + f.length()); 72.       } 73.    } 74. } 

javax.swing.text.JTextComponent 1.2

graphics/api_icon.gif
  • void selectAll()

    selects all text in the component.

  • void select(int selStart,int selEnd)

    selects a range of text in the component.

    Parameters:

    selStart

    The first position to select

     

    selEnd

    One past the last position to select

  • int getSelectionStart()

    returns the first position of the selected text.

  • int getSelectionEnd()

    returns one past the last position of the selected text.

  • String getSelectedText()

    returns the selected text.

  • void insert(String str, int pos)

    inserts a string into the text area.

    Parameters:

    str

    The text to insert

     

    pos

    The position at which to insert (0 = first position; new lines count as one character)

  • void replaceRange(String str, int start, int end)

    replaces a range of text with another string.

    Parameters:

    str

    The new text

     

    start

    The start position of the text to be replaced

     

    end

    One past the end position of the text to be replaced


       
    Top
     



    Core Java 2(c) Volume I - Fundamentals
    Building on Your AIX Investment: Moving Forward with IBM eServer pSeries in an On Demand World (MaxFacts Guidebook series)
    ISBN: 193164408X
    EAN: 2147483647
    Year: 2003
    Pages: 110
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net