JavaEnterprise JavaSwingin' Java: Text components

Swingin’ Java: Text components


April 5, 1999
Swingin’ Java: Text components

by Dan Simon

So far in this series we’ve discussed how to trap events in Swing and how to safely change the GUI components in a multithreaded environment. In this article, we’ll begin to get into Swing’s feature-rich text components.

In the old days of the AWT, text components were more or less limited to the basic text field and text area components. With the advent of Swing, however, the playing field got a lot bigger. The Swing library provides a comprehensive series of components that allow the developer to deal with practically any text presentation issue in GUI development. There are components to handle single-line text, multi-line text, and multi-line styled text. Swings text components range from the simple JTextField to the wonderfully full-featured JTextPane. To begin our discussion of text handling in Swing, we’ll start with the mother of them all: JTextComponent.

JTextComponent is the parent for all text components in Swing. It contains many of the basic methods shared by all five of the concrete text components, such as assigning and getting content from the component, assigning/altering the document, changing the caret, keymaps and bindings, and much more. If you are going to work with Swing text components, you are going to need to be familiar with the methods defined in this parent class. Of particular note, methods such as

setText(String text) 
and

getText()
interface with the component’s Document.

The Document interface is the content model for Swing text components. Every Swing text component has an instance of Document associated with it. The Document contains both the content for its component as well as any style information for the rendering of the content. All content is stored in the Document’s Element array. Style information is stored in each Element as an AttributeSet. We’ll get into AttributeSets in the next article, when we discuss styled text components.

For now, the main point to understand about the Document interface is that it controls all content for a text component. Whether text is being inserted through keyboard input, cutting and pasting or programmatically, the Document receives all updates through its

insertString(int offset, String str, AttributeSet attr) 
method. This means that if you want to create a text component that is limited in the length or type of characters that it can contain, all you need to do is create a custom implementation of the Document interface and control the input through the insertString method. This process is made even easier by the PlainDocument class.

The PlainDocument class is an implementation of the Document interface that is used by text components that do not need to manage complex formatting styles. JTextField, JTextArea, and JPasswordField all use PlainDocument as their model. Since PlainDocument is, in essence, a “minimal” implementation of the Document interface, it is an ideal class to extend to provide custom Documents for your text components. Example 1 shows how you would extend PlainDocument to provide a Document that will accept only numeric characters and will limit its length to the number of characters specified in its constructor (a fixed-length numeric text field).

Example 1

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

public class FixedNumericDocument extends PlainDocument {

   private int maxLength = 9999;
   private boolean numericOnly

   public FixedNumericDocument(int maxLength, boolean numericOnly) {
      super();
      this.maxLength = maxLength;
      this.numericOnly = numericOnly;
   }

   //this is where we'll control all input to our document.  
   //If the text that is being entered passes our criteria, then we'll just call
   //super.insertString(...)
   public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {
      if (getLength() + str.length() > maxLength) {
         Toolkit.getDefaultToolkit().beep();
         return;
      }
      else {
         try {
            if (numericOnly) {
               //check if str is numeric only
               Integer.parseInt(str);
               //if we get here then str contains only numbers
               //so it's ok to insert
            }
            super.insertString(offset, str, attr);
         }
         catch(NumberFormatException exp) {
            Toolkit.getDefaultToolkit().beep();
            return;
         }
      }
      return;
   }
}

One of the three non-styled documents defined by Swing is the ever-present JTextField. Among the constructors for JTextField is one which allows you to specify the instance of Document to use. It is this constructor that we will use later to see our fixed-length numeric document in action.

The rendering of a JTextField on screen, specifically determining its preferred width, can be a bit tricky. The first thing that JTextField will check in determining its preferred width is whether the number of columns has been set. You can set the number of columns for JTextField in its constructor or explicitly through

setColumns(int columns) 
. Note that setting the text of a JTextField does not set the number of columns! If the columns have been set, the preferred width of the JTextField is determined by multiplying the number of columns by the width of the letter “m” in the JTextField’s current font.

If the number of columns has not been set, the JTextField next checks to see if the preferred size has been set. If so, it uses this value.

If neither the number of columns nor the preferred size have been set, the JTextField will allow the TextUI to determine its preferred size. The default behavior of the TextUI is to return a preferred size that will allow all of the text in the JTextField to be displayed.

Like JTextField, JPasswordField is another of the non-styled text components defined in Swing. JPasswordField is almost identical in behavior to JTextField with only a couple of exceptions. For one, JPasswordField displays only its echo character (“*” by default) rather than the actual content, allowing passwords to be kept private. The echo character can be easily set on JPasswordField through the

setEchoChar(char echoChar)
method.

The other difference between JPasswordField and JTextField is that the

getText()
method is deprecated in JPasswordField (in the interest of security). To get the password that has been entered into a JPasswordField, it is recommended that you use the

getPassword()
method.

The final non-styled text component defined by Swing is the ever-useful JTextArea. Unlike JTextField, JTextArea allows you to display multiple lines of text. As such, it contains settings for turning auto line wrapping on and off —

setLineWrap(boolean wrap)
— and the style for line wrap if it’s on —

setWrapStyleWord(boolean wrapWords)
. The latter method tells the JTextArea whether to wrap on word breaks or characters.

Unlike its cousin java.awt.TextArea, JTextArea does not contain direct support for scrolling. Instead, it is expected that you will add it into a JScrollPane if you desire scrolling support.

Example 2 shows how to use all three of these components with our custom document from Example 1.

Example 2

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

public class Example2 extends JFrame {

   public Example2() {
      super("Swingin' Java -- Example 2");
   }

   public static void main(String[] args) {
      Example2 frame = new Example2();

      //create a JTextField that is six characters long
      JTextField textField = new JTextField(new FixedNumericDocument(6), "", 6);

      //create a JPasswordField that is 8 characters long (still numeric only)
      JPasswordField passwordField = new JPasswordField(new FixedNumericDocument(8), "", 8);
      //set the echo character to be "#"
      passwordField.setEchoChar('#');

      //finally, we'll create a JTextArea that will contain up to 256 numeric characters
      JTextArea textArea = new JTextArea(new FixedNumericDocument(256));
      //add the text area into a scroll pane
      JScrollPane scroll = new JScrollPane(textArea);
      //give the scroll pane a reasonable size
      scroll.setPreferredSize(new Dimension(150,100));
      //just to demonstrate, we'll turn on line wrapping
      textArea.setLineWrap(true);
      //now we'll tell it to wrap whole words only (don't break on characters)
      textArea.setWrapStyleWord(true);


      //create a panel to contain everything
      JPanel panel = new JPanel();

      panel.setLayout(new GridBagLayout());
      GridBagConstraints gbc = new GridBagConstraints();

      //initialize the constraints parameters
      gbc.gridx=0;
      gbc.gridy=0;
      gbc.anchor=gbc.EAST;
      gbc.fill=gbc.NONE;
      gbc.insets = new Insets(5,5,5,5);

      panel.add(new JLabel("JTextField:"), gbc);
      gbc.gridx=1;
      gbc.anchor=gbc.WEST;
      panel.add(textField, gbc);

      gbc.gridy=1;
      panel.add(passwordField, gbc);
      gbc.gridx=0;
      gbc.anchor=gbc.EAST;
      panel.add(new JLabel("JPasswordField:"), gbc);
      
      gbc.gridy=2;
      gbc.anchor=gbc.NORTHEAST;
      panel.add(new JLabel("JTextArea:"), gbc);
      gbc.gridx=1;
      gbc.anchor=gbc.WEST;
      //add the scroll pane, which already contains the text area
      panel.add(scroll, gbc); 


      //set the panel as our content pane
      frame.setContentPane(panel);

      //just to be nice, we'll shut down properly
      frame.addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
      frame.pack();
      frame.setVisible(true);
   }
}

In our next article, we’ll discuss the final two Swing text components — JEditorPane and JTextPane — which allow you to display formatted, multi-line text.

About the author

Dan Simon is a Sun Certified Java Developer with Osage Systems Group Inc., in Wood Dale, Ill.


Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories