epubjs
Version:
Render ePub documents in the browser, across many devices
370 lines (350 loc) • 83.5 kB
HTML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Text Components</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Text Components"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-18-SECT-1"/>Text Components</h1></div></div></div><p><a id="idx11027" class="indexterm"/>Swing offers sophisticated text components, from plain-text
entry boxes to HTML renderers. For full coverage of Swing’s text
capabilities, see O’Reilly’s <span class="emphasis"><em>Java Swing</em></span>. In that
encyclopedic book, several meaty chapters are devoted to text. It’s a huge
subject; we’ll just scratch the surface here.</p><p>Let’s begin by examining the simpler text components. <a id="I_indexterm18_id798841" class="indexterm"/><code class="literal">JTextField</code> is a
single-line text editor and <a id="I_indexterm18_id798852" class="indexterm"/><code class="literal">JTextArea</code> is a simple,
multiline text editor. Both <code class="literal">JTextField</code>
and <code class="literal">JTextArea</code> derive from the
<a id="I_indexterm18_id798874" class="indexterm"/><code class="literal">JTextComponent</code> class,
which provides the functionality they have in common. This includes
methods for setting and retrieving the displayed text, specifying whether
the text is “editable” or read-only, manipulating the cursor position
within the text, and manipulating text selections.</p><p>Observing changes in text components requires an understanding of
how the components implement the Model-View-Controller (MVC) architecture.
You may recall from the last chapter that Swing components implement a
true MVC architecture. It’s in the text components that you first get an
inkling of a clear separation between the M and VC parts of the MVC
architecture. The model for text components is an object called a <code class="literal">Document</code>. When you add or remove text from a
<code class="literal">JTextField</code> or a <code class="literal">JTextArea</code>, the corresponding <code class="literal">Document</code> is changed. It’s the document itself,
not the visual components, that generates text-related events when
something changes. To receive notification of <code class="literal">JTextArea</code> changes, therefore, you register with
the underlying <code class="literal">Document</code>, not with the
<code class="literal">JTextArea</code> component itself:</p><a id="I_18_tt1053"/><pre class="programlisting"> <code class="n">JTextArea</code> <code class="n">textArea</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code>
<code class="n">Document</code> <code class="n">doc</code> <code class="o">=</code> <code class="n">textArea</code><code class="o">.</code><code class="na">getDocument</code><code class="o">();</code>
<code class="n">doc</code><code class="o">.</code><code class="na">addDocumentListener</code><code class="o">(</code><code class="n">someListener</code><code class="o">);</code></pre><p>As you’ll see in an upcoming example, you can easily have more than
one visual text component use the same underlying <code class="literal">Document</code> data model.</p><p>In addition, <code class="literal">JTextField</code>
components generate <code class="literal">ActionEvent</code>s
whenever the user presses the Return key within the field. To get these
events, just implement the <code class="literal">ActionListener</code> interface and register your
listener using the <code class="literal">addActionListener()</code>
method.</p><p>The next sections contain a couple of simple applications that show
you how to work with text areas and fields.</p><div class="sect2" title="The TextEntryBox Application"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.1"/>The TextEntryBox Application</h2></div></div></div><p><a id="idx11038" class="indexterm"/> <a id="idx11040" class="indexterm"/>Our first example, <code class="literal">TextEntryBox</code>, creates a <a id="I_indexterm18_id799024" class="indexterm"/><code class="literal">JTextArea</code> and ties it
to a <a id="I_indexterm18_id799035" class="indexterm"/><code class="literal">JTextField</code>, as you can
see in <a class="xref" href="ch18s01.html#learnjava3-CHP-18-FIG-1" title="Figure 18-1. The TextEntryBox application">Figure 18-1</a>.</p><div class="figure"><a id="learnjava3-CHP-18-FIG-1"/><div class="figure-contents"><div class="mediaobject"><a id="I_18_tt1054"/><img src="httpatomoreillycomsourceoreillyimages1707667.png" alt="The TextEntryBox application"/></div></div><p class="title">Figure 18-1. The TextEntryBox application</p></div><p>When the user hits Return in the <code class="literal">JTextField</code>, we receive an <code class="literal">ActionEvent</code> and add the line to the <code class="literal">JTextArea</code>’s display. Try it out. You may have
to click your mouse in the <code class="literal">JTextField</code>
to give it focus before typing in it. If you fill up the display with
lines, you can test-drive the scroll bar:</p><a id="I_18_tt1055"/><pre class="programlisting"> <code class="c1">//file: TextEntryBox.java</code>
<code class="kn">import</code> <code class="nn">java.awt.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.awt.event.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">TextEntryBox</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code><code class="o">[]</code> <code class="n">args</code><code class="o">)</code> <code class="o">{</code>
<code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"Text Entry Box"</code><code class="o">);</code>
<code class="kd">final</code> <code class="n">JTextArea</code> <code class="n">area</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code>
<code class="n">area</code><code class="o">.</code><code class="na">setFont</code><code class="o">(</code><code class="k">new</code> <code class="n">Font</code><code class="o">(</code><code class="s">"Serif"</code><code class="o">,</code> <code class="n">Font</code><code class="o">.</code><code class="na">BOLD</code><code class="o">,</code> <code class="mi">18</code><code class="o">));</code>
<code class="n">area</code><code class="o">.</code><code class="na">setText</code><code class="o">(</code><code class="s">"Howdy!\n"</code><code class="o">);</code>
<code class="kd">final</code> <code class="n">JTextField</code> <code class="n">field</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">();</code>
<code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="k">new</code> <code class="n">JScrollPane</code><code class="o">(</code><code class="n">area</code><code class="o">),</code> <code class="n">BorderLayout</code><code class="o">.</code><code class="na">CENTER</code><code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="n">field</code><code class="o">,</code> <code class="n">BorderLayout</code><code class="o">.</code><code class="na">SOUTH</code><code class="o">);</code>
<code class="n">field</code><code class="o">.</code><code class="na">requestFocus</code><code class="o">();</code>
<code class="n">field</code><code class="o">.</code><code class="na">addActionListener</code><code class="o">(</code><code class="k">new</code> <code class="n">ActionListener</code><code class="o">()</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">actionPerformed</code><code class="o">(</code><code class="n">ActionEvent</code> <code class="n">ae</code><code class="o">)</code> <code class="o">{</code>
<code class="n">area</code><code class="o">.</code><code class="na">append</code><code class="o">(</code><code class="n">field</code><code class="o">.</code><code class="na">getText</code><code class="o">()</code> <code class="o">+</code> <code class="sc">'\n'</code><code class="o">);</code>
<code class="n">field</code><code class="o">.</code><code class="na">setText</code><code class="o">(</code><code class="s">""</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">});</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setSize</code><code class="o">(</code><code class="mi">200</code><code class="o">,</code> <code class="mi">300</code><code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code></pre><p><code class="literal">TextEntryBox</code> is exceedingly
simple; we’ve done a few things to make it more interesting. We give the
text area a bigger font using <code class="literal">Component</code>’s <a id="I_indexterm18_id799126" class="indexterm"/><code class="literal">setFont()</code> method; fonts
are discussed in <a class="xref" href="ch20.html" title="Chapter 20. Drawing with the 2D API">Chapter 20</a>. Finally, we want
to be notified whenever the user presses Return in the text field, so we
register an anonymous inner class as a listener for action
events.</p><p>Pressing Return in the <code class="literal">JTextField</code> generates an action event, and
that’s where the fun begins. We handle the event in the <code class="literal">actionPerformed()</code> method of our inner <code class="literal">ActionListener</code> implementation. Then, we use
the <a id="I_indexterm18_id799163" class="indexterm"/><code class="literal">getText()</code> and
<a id="I_indexterm18_id799174" class="indexterm"/><code class="literal">setText()</code> methods to
manipulate the text that the user has typed. These methods can be used
for <code class="literal">JTextField</code> and <code class="literal">JTextArea</code>, as these components are both
derived from the <code class="literal">JTextComponent</code> class
and, therefore, have some common functionality.</p><p>The event handler, <code class="literal">actionPerformed()</code>, calls <code class="literal">field.getText()</code> to read the text that the user
typed into our <code class="literal">JTextField</code>. It then
adds this text to the <code class="literal">JTextArea</code> by
calling <code class="literal">area.append()</code>. Finally, we
clear the text field by calling the method <code class="literal">field.setText("")</code>, preparing it for more
input.</p><p>Remember, the text components really are distinct from the text
data model, the <code class="literal">Document</code>. When you
call <code class="literal">setText()</code>, <code class="literal">getText()</code>, or <a id="I_indexterm18_id799258" class="indexterm"/><code class="literal">append()</code>, these methods
are shorthand for operations on an underlying <code class="literal">Document</code>.</p><p>By default, <code class="literal">JTextField</code> and
<code class="literal">JTextArea</code> are editable; you can type
and edit in both text components. They can be changed to output-only
areas by calling <a id="I_indexterm18_id799288" class="indexterm"/><code class="literal">setEditable(false)</code>.
Both text components also support <a id="I_indexterm18_id799299" class="indexterm"/><span class="emphasis"><em>selections</em></span>. A selection is a range of
text that is highlighted for copying, cutting, or pasting in your
windowing system. You select text by dragging the mouse over it; you can
then cut, copy, and paste it into other text windows using the default
keyboard gestures. On most systems, these are Ctrl-C for copy, Ctrl-V
for paste, and Ctrl-X for cut (on the Mac it’s Command-C, Command-V, and
Command-X). You can also programmatically manage these operations using
the <code class="literal">JTextComponent</code>’s <code class="literal">cut()</code> , <code class="literal">copy()</code>, <code class="literal">and
paste()</code> methods. You could, for example, create a pop-up menu
with the standard cut, copy, and paste options using these methods. The
current text selection is returned by <a id="I_indexterm18_id799339" class="indexterm"/><code class="literal">getSelectedText()</code>, and
you can set the selection using <a id="I_indexterm18_id799351" class="indexterm"/><code class="literal">selectText()</code>, which
takes an index range or <a id="I_indexterm18_id799362" class="indexterm"/><code class="literal">selectAll()</code>.</p><p>Notice how <code class="literal">JTextArea</code> fits
neatly inside a <code class="literal">JScrollPane</code>. The
scroll pane gives us the expected scrollbars and scrolling behavior if
the text in the <code class="literal">JTextArea</code> becomes too
large for the available space.<a id="I_indexterm18_id799392" class="indexterm"/><a id="I_indexterm18_id799399" class="indexterm"/></p></div><div class="sect2" title="Formatted Text"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.2"/>Formatted Text</h2></div></div></div><p><a id="idx11015" class="indexterm"/> <a id="idx11033" class="indexterm"/>The <a id="I_indexterm18_id799436" class="indexterm"/><code class="literal">JFormattedTextField</code>
component provides explicit support for editing complex formatted values
such as numbers and dates. <code class="literal">JFormattedTextField</code> acts somewhat like a
<code class="literal">JTextField</code>, except that it accepts a
format-specifying object in its constructor and manages a complex object
type (such as <code class="literal">Date</code> or <code class="literal">Integer</code>) through its <a id="I_indexterm18_id799471" class="indexterm"/><code class="literal">setValue()</code> and
<a id="I_indexterm18_id799481" class="indexterm"/><code class="literal">getValue()</code> methods. The
following example shows the construction of a simple form with different
types of formatted fields:</p><a id="I_18_tt1056"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">java.text.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.swing.text.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.util.Date</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code>
<code class="nc">FormattedFields</code>
<code class="o">{</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code><code class="o">[]</code> <code class="n">args</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">Exception</code> <code class="o">{</code>
<code class="n">Box</code> <code class="n">form</code> <code class="o">=</code> <code class="n">Box</code><code class="o">.</code><code class="na">createVerticalBox</code><code class="o">();</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Name:"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">(</code><code class="s">"Joe User"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Birthday:"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">JFormattedTextField</code> <code class="n">birthdayField</code> <code class="o">=</code>
<code class="k">new</code> <code class="nf">JFormattedTextField</code><code class="o">(</code><code class="k">new</code> <code class="n">SimpleDateFormat</code><code class="o">(</code><code class="s">"MM/dd/yy"</code><code class="o">));</code>
<code class="n">birthdayField</code><code class="o">.</code><code class="na">setValue</code><code class="o">(</code> <code class="k">new</code> <code class="n">Date</code><code class="o">()</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">birthdayField</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Age:"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="k">new</code> <code class="n">JFormattedTextField</code><code class="o">(</code><code class="k">new</code> <code class="n">Integer</code><code class="o">(</code><code class="mi">32</code><code class="o">)));</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Hairs on Body:"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">JFormattedTextField</code> <code class="n">hairsField</code>
<code class="o">=</code> <code class="k">new</code> <code class="n">JFormattedTextField</code><code class="o">(</code> <code class="k">new</code> <code class="n">DecimalFormat</code><code class="o">(</code><code class="s">"###,###"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">hairsField</code><code class="o">.</code><code class="na">setValue</code><code class="o">(</code><code class="k">new</code> <code class="n">Integer</code><code class="o">(</code><code class="mi">100000</code><code class="o">));</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">hairsField</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Phone Number:"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">JFormattedTextField</code> <code class="n">phoneField</code> <code class="o">=</code>
<code class="k">new</code> <code class="nf">JFormattedTextField</code><code class="o">(</code> <code class="k">new</code> <code class="n">MaskFormatter</code><code class="o">(</code><code class="s">"(###)###-####"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">phoneField</code><code class="o">.</code><code class="na">setValue</code><code class="o">(</code><code class="s">"(314)555-1212"</code><code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">phoneField</code> <code class="o">);</code>
<code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"User Information"</code><code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">getContentPane</code><code class="o">().</code><code class="na">add</code><code class="o">(</code><code class="n">form</code><code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">pack</code><code class="o">();</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code></pre><p><code class="literal">JFormattedTextField</code> can be
constructed in a variety of ways. You can use a plain instance of
<code class="literal">java.lang.Number</code> (e.g., <code class="literal">Integer</code> and <code class="literal">Float</code>) as a prototype or set the layout
explicitly using a formatting object from the <a id="I_indexterm18_id799538" class="indexterm"/><code class="literal">java.text</code> package:
<code class="literal">java.text.NumberFormat</code>, <code class="literal">java.text.DateFormat</code>, or the more arbitrary
<code class="literal">java.text.MaskFormatter</code>. The
<a id="I_indexterm18_id799566" class="indexterm"/><code class="literal">NumberFormat</code> and
<a id="I_indexterm18_id799577" class="indexterm"/><code class="literal">DateFormat</code> classes of
the <code class="literal">java.text</code> package are discussed
in Chapters <a class="xref" href="ch10.html" title="Chapter 10. Working with Text">10</a> and <a class="xref" href="ch11.html" title="Chapter 11. Core Utilities">11</a>. <a id="I_indexterm18_id799607" class="indexterm"/><code class="literal">MaskFormatter</code> allows
you to construct arbitrary physical layout conventions. In a moment,
we’ll discuss input filtering and component validation, which also allow
you to restrict the kinds of characters that could fill the fields or
perform arbitrary checks on the data. Finally, we should mention that in
this example, we’ve used a <a id="I_indexterm18_id799623" class="indexterm"/><code class="literal">Box</code> container. A
<code class="literal">Box</code> is just a Swing container that
uses a <code class="literal">BoxLayout</code>, which we’ll discuss
more in <a class="xref" href="ch19.html" title="Chapter 19. Layout Managers">Chapter 19</a>.</p><p>After construction, you can set a valid value using <code class="literal">setValue()</code> and retrieve the last valid value
with <code class="literal">getValue()</code>. To do this, you’ll
have to cast the value back to the correct type based on the format you
are using. For example, this statement retrieves the date from our
birthday field:</p><a id="I_18_tt1057"/><pre class="programlisting"> <code class="n">Date</code> <code class="n">bday</code> <code class="o">=</code> <code class="o">(</code><code class="n">Date</code><code class="o">)</code><code class="n">birthdayField</code><code class="o">.</code><code class="na">getValue</code><code class="o">();</code></pre><p><code class="literal">JFormattedTextField</code> validates
its text when the user attempts to shift focus to a new field (either by
clicking with the mouse outside of the field or using keyboard
navigation). By default, <code class="literal">JFormattedTextField</code> handles invalid input by
simply reverting to the last valid value. If you wish to allow invalid
input to remain in the field for further editing, you can set the
<code class="literal">setFocusLostBehavior()</code> method with
the value <code class="literal">JFormattedTextField.COMMIT</code>
(the default is <code class="literal">COMMIT_OR_REVERT</code>). In
any case, invalid input does not change the value property retrieved by
<code class="literal">getValue()</code>.<a id="I_indexterm18_id799714" class="indexterm"/><a id="I_indexterm18_id799722" class="indexterm"/></p></div><div class="sect2" title="Filtering Input"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.3"/>Filtering Input</h2></div></div></div><p><a id="idx11009" class="indexterm"/> <a id="idx11032" class="indexterm"/> <code class="literal">JFormattedTextField</code>
does not know about all format types itself; instead, it uses <a id="I_indexterm18_id799766" class="indexterm"/><code class="literal">AbstractFormatter</code> objects that know
about particular format types. The <code class="literal">AbstractFormatter</code>s, in turn, provide
implementations of two interfaces: <code class="literal">DocumentFilter</code> and <a id="I_indexterm18_id799793" class="indexterm"/><code class="literal">NavigationFilter</code>. A
<code class="literal">DocumentFilter</code> attaches to
implementations of <code class="literal">Document</code> and
allows you to intercept editing
commands, modifying them as you wish. A <code class="literal">NavigationFilter</code> can be attached to <code class="literal">JTextComponent</code>s to control the movement of the
cursor (as in a mask-formatted field). You can implement your own
<code class="literal">AbstractFormatter</code>s for use with
<code class="literal">JFormattedTextField</code>, and, more
generally, you can use the <code class="literal">DocumentFilter</code> interface to control how
documents are edited in any type of text component. For example, you
could create a <code class="literal">DocumentFilter</code> that
maps characters to uppercase or strange symbols. <code class="literal">DocumentFilter</code> provides a low-level,
edit-by-edit means of controlling or mapping user input. We will show an
example of this now. In the following section, we discuss how to
implement higher-level field <span class="emphasis"><em>validation</em></span> that
ensures the correctness of data after it is entered, in the same way
that the formatted text field did for us earlier.</p><div class="sect3" title="DocumentFilter"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-18-SECT-1.3.1"/>DocumentFilter</h3></div></div></div><p><a id="idx11008" class="indexterm"/>The following example, <code class="literal">DocFilter</code>, applies a document filter to a
<code class="literal">JTextField</code>. Our <code class="literal">DocumentFilter</code> simply maps any input to
uppercase. Here is the code:</p><a id="I_18_tt1058"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">java.text.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.swing.text.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">DocFilter</code>
<code class="o">{</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code><code class="o">[]</code> <code class="n">args</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">Exception</code>
<code class="o">{</code>
<code class="n">JTextField</code> <code class="n">field</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">(</code><code class="mi">30</code><code class="o">);</code>
<code class="o">((</code><code class="n">AbstractDocument</code><code class="o">)(</code><code class="n">field</code><code class="o">.</code><code class="na">getDocument</code><code class="o">())).</code><code class="na">setDocumentFilter</code><code class="o">(</code>
<code class="k">new</code> <code class="nf">DocumentFilter</code><code class="o">()</code>
<code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">insertString</code><code class="o">(</code>
<code class="n">FilterBypass</code> <code class="n">fb</code><code class="o">,</code> <code class="kt">int</code> <code class="n">offset</code><code class="o">,</code> <code class="n">String</code> <code class="n">string</code><code class="o">,</code> <code class="n">AttributeSet</code> <code class="n">attr</code><code class="o">)</code>
<code class="kd">throws</code> <code class="n">BadLocationException</code>
<code class="o">{</code>
<code class="n">fb</code><code class="o">.</code><code class="na">insertString</code><code class="o">(</code> <code class="n">offset</code><code class="o">,</code> <code class="n">string</code><code class="o">.</code><code class="na">toUpperCase</code><code class="o">(),</code> <code class="n">attr</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">replace</code><code class="o">(</code>
<code class="n">FilterBypass</code> <code class="n">fb</code><code class="o">,</code> <code class="kt">int</code> <code class="n">offset</code><code class="o">,</code> <code class="kt">int</code> <code class="n">length</code><code class="o">,</code> <code class="n">String</code> <code class="n">string</code><code class="o">,</code>
<code class="n">AttributeSet</code> <code class="n">attr</code><code class="o">)</code> <code class="kd">throws</code> <code class="n">BadLocationException</code>
<code class="o">{</code>
<code class="n">fb</code><code class="o">.</code><code class="na">replace</code><code class="o">(</code> <code class="n">offset</code><code class="o">,</code> <code class="n">length</code><code class="o">,</code> <code class="n">string</code><code class="o">.</code><code class="na">toUpperCase</code><code class="o">(),</code> <code class="n">attr</code> <code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code> <code class="o">);</code>
<code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"User Information"</code><code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">field</code> <code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">pack</code><code class="o">();</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>The methods <a id="I_indexterm18_id799923" class="indexterm"/><code class="literal">insertString()</code> and
<a id="I_indexterm18_id799934" class="indexterm"/><code class="literal">replace()</code> of the
<code class="literal">DocumentFilter</code> are called when text
is added to the document or modified. Within them, we have an
opportunity to filter the text before passing it on. When we are ready
to apply the text, we use the <a id="I_indexterm18_id799954" class="indexterm"/><code class="literal">FilterBypass</code>
reference to pass it along. <code class="literal">FilterBypass</code> has the same set of methods,
which apply the changes directly to the document. The <code class="literal">DocumentFilter remove()</code> method can also be
used to intercept edits to the document that remove characters. One
thing to note in our example is that not all <code class="literal">Document</code>s have a <a id="I_indexterm18_id799984" class="indexterm"/><code class="literal">setDocumentFilter()</code>
method. Instead, we have to cast our document to an <code class="literal">AbstractDocument</code>. Only document
implementations that extend <code class="literal">AbstractDocument</code> accept filters (unless you
implement your own). This sad state of affairs is because the Document
Filter API was added in Java 1.4, and it was decided that changes
could not be made to the original <code class="literal">Document</code> interface.<a id="I_indexterm18_id800014" class="indexterm"/><a id="I_indexterm18_id800021" class="indexterm"/><a id="I_indexterm18_id800028" class="indexterm"/></p></div></div><div class="sect2" title="Validating Data"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.4"/>Validating Data</h2></div></div></div><p><a id="idx11039" class="indexterm"/> <a id="idx11042" class="indexterm"/>Low-level input filtering prevents you from doing such
things as entering a number where a character should be. In this
section, we’re going to talk about high-level validation, which accounts
for things like February having only 28 days or a credit card number
being for a Visa or MasterCard. Whereas character filtering prevents you
from entering incorrect data, field validation happens after data has
been entered. Normally, validation occurs when the user tries to change
focus and leave the field, either by clicking the mouse or through
keyboard navigation. Java 1.4 added the <code class="literal">InputVerifier</code> API, which allows you to
validate the contents of a component before focus is transferred.
Although we are going to talk about this in the context of text fields,
an <a id="I_indexterm18_id800081" class="indexterm"/><code class="literal">InputVerifier</code> can
actually be attached to any <code class="literal">JComponent</code> to validate its state in this
way.</p><p>The following example creates a pair of text fields. The first
allows any value to be entered, while the second accepts only numbers
between 0 and 100. When both fields are happy, you can freely move
between them. However, when you enter an invalid value in the second
field and try to leave, the program just beeps and selects the text. The
focus remains trapped until you correct the problem.</p><a id="I_18_tt1059"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">Validator</code>
<code class="o">{</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code><code class="o">[]</code> <code class="n">args</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">Exception</code> <code class="o">{</code>
<code class="n">Box</code> <code class="n">form</code> <code class="o">=</code> <code class="n">Box</code><code class="o">.</code><code class="na">createVerticalBox</code><code class="o">();</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Any Value"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">(</code><code class="s">"5000"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Only 0-100"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">JTextField</code> <code class="n">rangeField</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">(</code><code class="s">"50"</code><code class="o">);</code>
<code class="n">rangeField</code><code class="o">.</code><code class="na">setInputVerifier</code><code class="o">(</code> <code class="k">new</code> <code class="n">InputVerifier</code><code class="o">()</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">boolean</code> <code class="nf">verify</code><code class="o">(</code> <code class="n">JComponent</code> <code class="n">comp</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">JTextField</code> <code class="n">field</code> <code class="o">=</code> <code class="o">(</code><code class="n">JTextField</code><code class="o">)</code><code class="n">comp</code><code class="o">;</code>
<code class="kt">boolean</code> <code class="n">passed</code> <code class="o">=</code> <code class="kc">false</code><code class="o">;</code>
<code class="k">try</code> <code class="o">{</code>
<code class="kt">int</code> <code class="n">n</code> <code class="o">=</code> <code class="n">Integer</code><code class="o">.</code><code class="na">parseInt</code><code class="o">(</code><code class="n">field</code><code class="o">.</code><code class="na">getText</code><code class="o">());</code>
<code class="n">passed</code> <code class="o">=</code> <code class="o">(</code> <code class="mi">0</code> <code class="o"><=</code> <code class="n">n</code> <code class="o">&&</code> <code class="n">n</code> <code class="o"><=</code> <code class="mi">100</code> <code class="o">);</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code><code class="n">NumberFormatException</code> <code class="n">e</code><code class="o">)</code> <code class="o">{</code> <code class="o">}</code>
<code class="k">if</code> <code class="o">(</code> <code class="o">!</code><code class="n">passed</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">comp</code><code class="o">.</code><code class="na">getToolkit</code><code class="o">().</code><code class="na">beep</code><code class="o">();</code>
<code class="n">field</code><code class="o">.</code><code class="na">selectAll</code><code class="o">();</code>
<code class="o">}</code>
<code class="k">return</code> <code class="n">passed</code><code class="o">;</code>
<code class="o">}</code>
<code class="o">}</code> <code class="o">);</code>
<code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">rangeField</code> <code class="o">);</code>
<code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"User Information"</code><code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="n">form</code><code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">pack</code><code class="o">();</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>We’ve created an anonymous inner class extending <code class="literal">InputVerifier</code> with this code. The API is very
simple; at validation time, our <a id="I_indexterm18_id800133" class="indexterm"/><code class="literal">verify()</code> method is
called, and we are passed a reference to the component needing checking.
Here we cast to the correct type (we know what we are verifying, of
course) and parse the number. If it is out of range, we beep and select
the text. We then return <code class="literal">true</code> or
<code class="literal">false</code> indicating whether the value
passes validation.</p><p>You can use an <code class="literal">InputVerifier</code> in
combination with a <code class="literal">JFormattedTextField</code> to both guide user input
into the correct format and validate the semantics of what the user
entered.<a id="I_indexterm18_id800171" class="indexterm"/><a id="I_indexterm18_id800178" class="indexterm"/></p></div><div class="sect2" title="Say the Magic Word"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.5"/>Say the Magic Word</h2></div></div></div><p><a id="idx11017" class="indexterm"/> <a id="idx11036" class="indexterm"/>Before we move on from our discussion of formatted text,
we should mention that Swing includes a class just for typing passwords,
called <a id="I_indexterm18_id800217" class="indexterm"/><code class="literal">JPasswordField</code>. A
<code class="literal">JPasswordField</code> behaves just like a
<code class="literal">JTextField</code> (it’s a subclass), except
every character typed is echoed as the same, obfuscating character,
typically an asterisk. <a class="xref" href="ch18s01.html#learnjava3-CHP-18-FIG-2" title="Figure 18-2. Using a JPasswordField in a dialog">Figure 18-2</a> shows
the option dialog example that was presented in <a class="xref" href="ch17.html" title="Chapter 17. Using Swing Components">Chapter 17</a>. The example includes a <code class="literal">JTextField</code> and a <code class="literal">JPasswordField</code>.</p><p>The creation and use of <code class="literal">JPasswordField</code> is basically the same as for
<code class="literal">JTextField</code>. If you find asterisks
distasteful, you can tell the <code class="literal">JPasswordField</code> to use a different character
using the <a id="I_indexterm18_id800281" class="indexterm"/><code class="literal">setEchoChar()</code>
method.</p><p>Normally, you would use <code class="literal">getText()</code> to retrieve the text typed into the
<code class="literal">JPasswordField</code>. This method, however,
is deprecated; you should use <a id="I_indexterm18_id800305" class="indexterm"/><code class="literal">getPassword()</code> instead.
The <code class="literal">getPassword()</code> method returns a
character array rather than a <code class="literal">String</code>
object. This is done because character arrays are a little less
vulnerable than <code class="literal">String</code>s to discover
by memory-snooping password sniffer programs and they can be erased
directly and easily. If you’re not that concerned, you can simply create
a new <code class="literal">String</code> from the character
array. Note that methods in the Java cryptographic classes accept
passwords as character arrays, not strings, so you can pass the results
of a <code class="literal">getPassword()</code> call directly to
methods in the cryptographic classes without ever creating a <code class="literal">String</code>.<a id="I_indexterm18_id800355" class="indexterm"/><a id="I_indexterm18_id800362" class="indexterm"/></p><div class="figure"><a id="learnjava3-CHP-18-FIG-2"/><div class="figure-contents"><div class="mediaobject"><a id="I_18_tt1060"/><img src="httpatomoreillycomsourceoreillyimages1707668.png" alt="Using a JPasswordField in a dialog"/></div></div><p class="title">Figure 18-2. Using a JPasswordField in a dialog</p></div></div><div class="sect2" title="Sharing a Data Model"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.6"/>Sharing a Data Model</h2></div></div></div><p><a id="idx11005" class="indexterm"/> <a id="idx11019" class="indexterm"/> <a id="idx11037" class="indexterm"/>Our next example shows how easy it is to make two or more
text components share the same <code class="literal">Document</code>; <a class="xref" href="ch18s01.html#learnjava3-CHP-18-FIG-3" title="Figure 18-3. Three views of the same data model">Figure 18-3</a> shows what the application looks
like.</p><div class="figure"><a id="learnjava3-CHP-18-FIG-3"/><div class="figure-contents"><div class="mediaobject"><a id="I_18_tt1061"/><img src="httpatomoreillycomsourceoreillyimages1707669.png" alt="Three views of the same data model"/></div></div><p class="title">Figure 18-3. Three views of the same data model</p></div><p>Anything the user types into any text area is reflected in all of
them. All we had to do is make all the text areas use the same data
model, like this:</p><a id="I_18_tt1062"/><pre class="programlisting"> <code class="n">JTextArea</code> <code class="n">areaFiftyOne</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code>
<code class="n">JTextArea</code> <code class="n">areaFiftyTwo</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code>
<code class="n">areaFiftyTwo</code><code class="o">.</code><code class="na">setDocument</code><code class="o">(</code><code class="n">areaFiftyOne</code><code class="o">.</code><code class="na">getDocument</code><code class="o">());</code>
<code class="n">JTextArea</code> <code class="n">areaFiftyThree</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code>
<code class="n">areaFiftyThree</code><code class="o">.</code><code class="na">setDocument</code><code class="o">(</code><code class="n">areaFiftyOne</code><code class="o">.</code><code class="na">getDocument</code><code class="o">());</code></pre><p>We could just as easily make seven text areas sharing the same
document—or seventy. While this example may not look very useful, keep
in mind that you can scroll different text areas to different places in
the same document. That’s one of the beauties of putting multiple views
on the same data; you get to examine different parts of it. Another
useful technique is viewing the same data in different ways. You could,
for example, view some tabular numerical data as both a spreadsheet and
a pie chart. The MVC architecture that Swing uses means that it’s
possible to do this in an intelligent way so that if numbers in a
spreadsheet are updated, a pie chart that uses the same data is
automatically updated, too.</p><p>This example works because, behind the scenes, there are a lot of
events flying around. When you type in one of the text areas, the text
area receives the keyboard events. It calls methods in the document to
update its data. In turn, the document sends events to the other text
areas telling them about the updates so that they can correctly display
the document’s new data. But don’t worry about any of this; you just
tell the text areas to use the same data, and Swing takes care of the
rest:</p><a id="I_18_tt1063"/><pre class="programlisting"> <code class="c1">//file: SharedModel.java<