Java/Swing JFC/Formatted TextField
Содержание
- 1 A BigDecimal object custom formatter
- 2 Accepting Formatted Input
- 3 A decimal number with one digit following the decimal point;
- 4 A formatter for regular expressions to be used with JFormattedTextField
- 5 A quick demonstration of JFormattedTextField
- 6 Creating a Text Field to Display and Edit a Date
- 7 Creating a Text Field to Display and Edit a Number
- 8 Creating a Text Field to Display and Edit a Phone Number
- 9 Creating a Text Field to Display and Edit a social security number
- 10 Different configurations of JFormattedTextField: Date
- 11 different configurations of JFormattedTextField: Number
- 12 Dynamically change the format
- 13 Field with different formats with focus and without
- 14 Format and validate input field in Java Swing
- 15 Formatted TextField Demo
- 16 Formatted TextField Example
- 17 Formatter Factory Demo
- 18 Input: any number of hyphen-delimeted numbers. Output: int array
- 19 Input Verification Demo
- 20 Input Verification Dialog Demo
- 21 JFormattedTextField: an input mask (###) ###-#### for a telephone number
- 22 Make custom Input Text Formatter in Java
- 23 Support a date with the custom format: 2009-1-1
- 24 Using an InputVerifier with a formatted textfield
A BigDecimal object custom formatter
<source lang="java">
import java.math.BigDecimal; import java.text.DecimalFormat; import javax.swing.JFormattedTextField; import javax.swing.text.DefaultFormatter; import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.NumberFormatter; public class Main {
public static void main(String[] argv) { JFormattedTextField f = new JFormattedTextField(new BigDecimal("123.4567")); DefaultFormatter fmt = new NumberFormatter(new DecimalFormat("#.0###############")); fmt.setValueClass(f.getValue().getClass()); DefaultFormatterFactory fmtFactory = new DefaultFormatterFactory(fmt, fmt, fmt); f.setFormatterFactory(fmtFactory); BigDecimal bigValue = (BigDecimal) f.getValue(); }
}
</source>
Accepting Formatted Input
<source lang="java">
import java.awt.BorderLayout; import java.awt.Container; import java.util.Date; import javax.swing.JFormattedTextField; import javax.swing.JFrame; public class FormattedTest {
public static void main(String args[]) { JFrame frame = new JFrame("Formatted"); Container contentPane = frame.getContentPane(); JFormattedTextField ftf1 = new JFormattedTextField(new Integer(0)); contentPane.add(ftf1, BorderLayout.NORTH); JFormattedTextField ftf2 = new JFormattedTextField(new Date()); contentPane.add(ftf2, BorderLayout.SOUTH); frame.setSize(200, 100); frame.show(); }
}
</source>
A decimal number with one digit following the decimal point;
<source lang="java">
import java.text.DecimalFormat; import javax.swing.JFormattedTextField; public class Main {
public static void main(String[] argv) throws Exception { JFormattedTextField tft2 = new JFormattedTextField(new DecimalFormat("#.0")); tft2.setValue(new Float(123.4F)); // Retrieve the value from the text field Float floatValue = (Float) tft2.getValue(); }
}
</source>
A formatter for regular expressions to be used with JFormattedTextField
<source lang="java">
/* Java Swing, 2nd Edition By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole ISBN: 0-596-00408-7 Publisher: O"Reilly
- /
// RegexPatternFormatter.java //A formatter for regular expressions to be used with JFormattedTextField. // import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.text.DefaultFormatter; public class RegexPatternFormatter extends DefaultFormatter {
protected java.util.regex.Matcher matcher; public RegexPatternFormatter(java.util.regex.Pattern regex) { setOverwriteMode(false); matcher = regex.matcher(""); // create a Matcher for the regular // expression } public Object stringToValue(String string) throws java.text.ParseException { if (string == null) return null; matcher.reset(string); // set "string" as the matcher"s input if (!matcher.matches()) // Does "string" match the regular expression? throw new java.text.ParseException("does not match regex", 0); // If we get this far, then it did match. return super.stringToValue(string); // will honor the "valueClass" // property } public static void main(String argv[]) { // a demo main() to show how RegexPatternFormatter could be used JLabel lab1 = new JLabel("even length strings:"); java.util.regex.Pattern evenLength = java.util.regex.Pattern .rupile("(..)*"); JFormattedTextField ftf1 = new JFormattedTextField( new RegexPatternFormatter(evenLength)); JLabel lab2 = new JLabel("no vowels:"); java.util.regex.Pattern noVowels = java.util.regex.Pattern.rupile( "[^aeiou]*", java.util.regex.Pattern.CASE_INSENSITIVE); RegexPatternFormatter noVowelFormatter = new RegexPatternFormatter( noVowels); noVowelFormatter.setAllowsInvalid(false); // don"t allow user to type // vowels JFormattedTextField ftf2 = new JFormattedTextField(noVowelFormatter); JFrame f = new JFrame("RegexPatternFormatter Demo"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel pan1 = new JPanel(new java.awt.BorderLayout()); pan1.add(lab1, java.awt.BorderLayout.WEST); pan1.add(ftf1, java.awt.BorderLayout.CENTER); lab1.setLabelFor(ftf1); f.getContentPane().add(pan1, java.awt.BorderLayout.NORTH); JPanel pan2 = new JPanel(new java.awt.BorderLayout()); pan2.add(lab2, java.awt.BorderLayout.WEST); pan2.add(ftf2, java.awt.BorderLayout.CENTER); lab2.setLabelFor(ftf2); f.getContentPane().add(pan2, java.awt.BorderLayout.SOUTH); f.setSize(300, 80); f.setVisible(true); }
}
</source>
A quick demonstration of JFormattedTextField
<source lang="java">
/* Java Swing, 2nd Edition By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole ISBN: 0-596-00408-7 Publisher: O"Reilly
- /
// SimpleFTF.java //A quick demonstration of JFormattedTextField. // import javax.swing.BoxLayout; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JPanel; public class SimpleFTF extends JPanel {
public SimpleFTF() { JFormattedTextField ftf[] = new JFormattedTextField[7]; String des[] = new String[ftf.length]; // description of each field des[0] = "Date"; ftf[0] = new JFormattedTextField(new java.util.Date()); des[1] = "Integer"; ftf[1] = new JFormattedTextField(new Integer(90032221)); des[2] = "Float"; ftf[2] = new JFormattedTextField(new Float(3.14)); des[3] = "Float work-around"; // manually specify a NumberFormat ftf[3] = new JFormattedTextField(java.text.NumberFormat.getInstance()); ftf[3].setValue(new Float(3.14)); des[4] = "currency"; ftf[4] = new JFormattedTextField(java.text.NumberFormat .getCurrencyInstance()); ftf[4].setValue(new Float(5.99)); des[5] = "percent"; ftf[5] = new JFormattedTextField(java.text.NumberFormat .getPercentInstance()); ftf[5].setValue(new Float(0.33)); des[6] = "java.net.URL"; // works via 1-arg String constructor and // toString() java.net.URL u = null; try { u = new java.net.URL("http://www.ora.ru/"); } catch (java.net.MalformedURLException ignored) { } ftf[6] = new JFormattedTextField(u); ftf[6].setColumns(24); // add each ftf[] to a BoxLayout setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); for (int j = 0; j < ftf.length; j += 1) { JPanel borderPanel = new JPanel(new java.awt.BorderLayout()); borderPanel.setBorder(new javax.swing.border.TitledBorder(des[j])); borderPanel.add(ftf[j], java.awt.BorderLayout.CENTER); add(borderPanel); } } public static void main(String argv[]) { if (argv.length > 0) { // change to command-line locale if (argv[0].equalsIgnoreCase("canada")) java.util.Locale.setDefault(java.util.Locale.CANADA); if (argv[0].equalsIgnoreCase("canada_french")) java.util.Locale.setDefault(java.util.Locale.CANADA_FRENCH); if (argv[0].equalsIgnoreCase("china")) java.util.Locale.setDefault(java.util.Locale.CHINA); if (argv[0].equalsIgnoreCase("france")) java.util.Locale.setDefault(java.util.Locale.FRANCE); if (argv[0].equalsIgnoreCase("germany")) java.util.Locale.setDefault(java.util.Locale.GERMANY); if (argv[0].equalsIgnoreCase("italy")) java.util.Locale.setDefault(java.util.Locale.ITALY); if (argv[0].equalsIgnoreCase("japan")) java.util.Locale.setDefault(java.util.Locale.JAPAN); if (argv[0].equalsIgnoreCase("korea")) java.util.Locale.setDefault(java.util.Locale.KOREA); if (argv[0].equalsIgnoreCase("prc")) java.util.Locale.setDefault(java.util.Locale.PRC); if (argv[0].equalsIgnoreCase("taiwan")) java.util.Locale.setDefault(java.util.Locale.TAIWAN); if (argv[0].equalsIgnoreCase("uk")) java.util.Locale.setDefault(java.util.Locale.UK); if (argv[0].equalsIgnoreCase("us")) java.util.Locale.setDefault(java.util.Locale.US); } String localeString = java.util.Locale.getDefault().getDisplayName(); JFrame f = new JFrame("SimpleFTF " + localeString); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setContentPane(new SimpleFTF()); f.pack(); f.setVisible(true); }
}
</source>
Creating a Text Field to Display and Edit a Date
<source lang="java">
import java.text.DateFormat; import java.util.Date; import javax.swing.JFormattedTextField; public class Main {
public static void main(String[] argv) { JFormattedTextField tft2 = new JFormattedTextField(DateFormat.getDateInstance(DateFormat.SHORT)); tft2.setValue(new Date()); }
}
</source>
Creating a Text Field to Display and Edit a Number
<source lang="java">
import java.text.NumberFormat; import javax.swing.JFormattedTextField; public class Main {
public static void main(String[] argv) { JFormattedTextField tft1 = new JFormattedTextField(NumberFormat.getIntegerInstance()); tft1.setValue(new Integer(123)); Integer intValue = (Integer) tft1.getValue(); }
}
</source>
Creating a Text Field to Display and Edit a Phone Number
<source lang="java">
import javax.swing.JFormattedTextField; import javax.swing.text.MaskFormatter; public class Main {
public static void main(String[] argv) throws Exception { MaskFormatter fmt = new MaskFormatter("###-###-####"); JFormattedTextField tft1 = new JFormattedTextField(fmt); }
}
</source>
Creating a Text Field to Display and Edit a social security number
<source lang="java">
import javax.swing.JFormattedTextField; import javax.swing.text.MaskFormatter; public class Main {
public static void main(String[] argv) throws Exception{ MaskFormatter fmt = new MaskFormatter("###-###-####"); JFormattedTextField tft1 = new JFormattedTextField(fmt); fmt = new MaskFormatter("###-##-####"); JFormattedTextField tft2 = new JFormattedTextField(fmt); }
}
</source>
Different configurations of JFormattedTextField: Date
<source lang="java">
/*
* Copyright (c) 2003-2005 JGoodies Karsten Lentzsch. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of JGoodies Karsten Lentzsch nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
package com.jgoodies.validation.tutorial.formatted; import java.text.DateFormat; import java.text.Format; import java.util.Date; import java.util.LinkedList; import java.util.List; import javax.swing.*; import javax.swing.text.DateFormatter; import javax.swing.text.DefaultFormatter; import javax.swing.text.DefaultFormatterFactory; import com.jgoodies.binding.value.ValueHolder; import com.jgoodies.binding.value.ValueModel; import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.validation.formatter.EmptyDateFormatter; import com.jgoodies.validation.formatter.RelativeDateFormatter; import com.jgoodies.validation.tutorial.formatted.format.DisplayFormat; import com.jgoodies.validation.tutorial.formatted.format.RelativeDateFormat; import com.jgoodies.validation.tutorial.util.ExampleComponentFactory; import com.jgoodies.validation.tutorial.util.MyFocusTraversalPolicy; import com.jgoodies.validation.tutorial.util.TutorialUtils; /**
* Demonstrates different configurations of JFormattedTextField
* to display and edit numbers. Shows -
*
- how to use a custom DateFormat *
- how to use a custom DateFormatter *
- how to use a custom FormatterFactory *
- how to reset a date to
null
* - how to map the empty string to a special date *
- how to commit values on valid texts *
* * To look under the hood of this component, this class exposes * the bound properties text, value and editValid. * * @author Karsten Lentzsch * @version $Revision: 1.8 $ * * @see JFormattedTextField * @see JFormattedTextField.AbstractFormatter */
public final class DateExample {
public static void main(String[] args) { try { UIManager.setLookAndFeel("com.jgoodies.looks.plastic.PlasticXPLookAndFeel"); } catch (Exception e) { // Likely Plastic is not in the classpath; ignore it. } JFrame frame = new JFrame(); frame.setTitle("Formatted :: Dates"); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); JComponent panel = new DateExample() .buildPanel(); frame.getContentPane().add(panel); frame.pack(); TutorialUtils.locateOnScreenCenter(frame); frame.setVisible(true); } /** * Builds and returns a panel with a header row and the sample rows. * * @return the example panel with a header and the sample rows */ public JComponent buildPanel() { FormLayout layout = new FormLayout( "r:max(80dlu;pref), 4dlu, 45dlu, 1dlu, pref, 4dlu, pref, 0:grow"); DefaultFormBuilder builder = new DefaultFormBuilder(layout); builder.setDefaultDialogBorder(); builder.getPanel().setFocusTraversalPolicy( MyFocusTraversalPolicy.INSTANCE); Utils.appendTitleRow(builder, "Description"); List fields = appendDemoRows(builder); String validText = DateFormat.getDateInstance().format(new Date(67, 11, 5)); String invalidText = "aa" + validText; Utils.appendButtonBar(builder, fields, validText, invalidText); return builder.getPanel(); } /** * Appends the demo rows to the given builder and returns the List of * formatted text fields. * * @param builder the builder used to add components to * @return the List of formatted text fields */ private List appendDemoRows(DefaultFormBuilder builder) { // The Formatter is choosen by the initial value. JFormattedTextField defaultDateField = new JFormattedTextField(new Date()); // The Formatter is choosen by the given Format. JFormattedTextField noInitialValueField = new JFormattedTextField(DateFormat.getDateInstance()); // Uses a custom DateFormat. DateFormat customFormat = DateFormat.getDateInstance(DateFormat.SHORT); JFormattedTextField customFormatField = new JFormattedTextField(new DateFormatter(customFormat)); // Uses a RelativeDateFormat. DateFormat relativeFormat = new RelativeDateFormat(); JFormattedTextField relativeFormatField = new JFormattedTextField(new DateFormatter(relativeFormat)); // Uses a custom DateFormatter that allows relative input and // prints natural language strings. JFormattedTextField relativeFormatterField = new JFormattedTextField(new RelativeDateFormatter()); // Uses a custom FormatterFactory that used different formatters // for the display and while editing. DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory(new RelativeDateFormatter(false, true), new RelativeDateFormatter(true, true)); JFormattedTextField relativeFactoryField = new JFormattedTextField(formatterFactory); // Wraps a DateFormatter to map empty strings to null and vice versa. JFormattedTextField numberOrNullField = new JFormattedTextField(new EmptyDateFormatter()); // Wraps a DateFormatter to map empty strings to -1 and vice versa. Date epoch = new Date(0); // January 1, 1970 JFormattedTextField numberOrEmptyValueField = new JFormattedTextField(new EmptyDateFormatter(epoch)); numberOrEmptyValueField.setValue(epoch); // Commits value on valid edit text DefaultFormatter formatter = new RelativeDateFormatter(); formatter.setCommitsOnValidEdit(true); JFormattedTextField commitOnValidEditField = new JFormattedTextField(formatter); // A date field as created by the BasicComponentFactory: // Uses relative date input, and maps empty strings to null. ValueModel dateHolder = new ValueHolder(); JFormattedTextField componentFactoryField = ExampleComponentFactory.createDateField(dateHolder); Format displayFormat = new DisplayFormat(DateFormat.getDateInstance()); List fields = new LinkedList(); fields.add(Utils.appendRow(builder, "Default", defaultDateField, displayFormat)); fields.add(Utils.appendRow(builder, "No initial value", noInitialValueField, displayFormat)); fields.add(Utils.appendRow(builder, "Empty <-> null", numberOrNullField, displayFormat)); fields.add(Utils.appendRow(builder, "Empty <-> epoch", numberOrEmptyValueField, displayFormat)); fields.add(Utils.appendRow(builder, "Short format", customFormatField, displayFormat)); fields.add(Utils.appendRow(builder, "Relative format", relativeFormatField, displayFormat)); fields.add(Utils.appendRow(builder, "Relative formatter", relativeFormatterField, displayFormat)); fields.add(Utils.appendRow(builder, "Relative factory", relativeFactoryField, displayFormat)); fields.add(Utils.appendRow(builder, "Commits on valid edit", commitOnValidEditField, displayFormat)); fields.add(Utils.appendRow(builder, "Relative, maps null", componentFactoryField, displayFormat)); return fields; }
}
</source>
different configurations of JFormattedTextField: Number
<source lang="java">
/*
* Copyright (c) 2003-2005 JGoodies Karsten Lentzsch. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of JGoodies Karsten Lentzsch nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
package com.jgoodies.validation.tutorial.formatted; import java.text.Format; import java.text.NumberFormat; import java.util.LinkedList; import java.util.List; import javax.swing.*; import javax.swing.text.DefaultFormatter; import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.NumberFormatter; import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.validation.formatter.EmptyNumberFormatter; import com.jgoodies.validation.tutorial.formatted.format.DisplayFormat; import com.jgoodies.validation.tutorial.formatted.formatter.CustomNumberFormatter; import com.jgoodies.validation.tutorial.util.MyFocusTraversalPolicy; import com.jgoodies.validation.tutorial.util.TutorialUtils; /**
* Demonstrates different configurations of JFormattedTextField
* to display and edit numbers. Shows-
*
- how to use a custom NumberFormat *
- how to use a custom NumberFormatter *
- how to use a custom FormatterFactory *
- how to reset a number to
null
* - how to map the empty string to a special number *
- how to commit values on valid texts *
- how to set the class of the result *
* * To look under the hood of this component, this class exposes * the bound properties text, value and editValid. * * @author Karsten Lentzsch * @version $Revision: 1.9 $ * * @see JFormattedTextField * @see JFormattedTextField.AbstractFormatter */
public final class NumberExample {
public static void main(String[] args) { try { UIManager.setLookAndFeel("com.jgoodies.looks.plastic.PlasticXPLookAndFeel"); } catch (Exception e) { // Likely Plastic is not in the classpath; ignore it. } JFrame frame = new JFrame(); frame.setTitle("Formatted :: Numbers"); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); JComponent panel = new NumberExample() .buildPanel(); frame.getContentPane().add(panel); frame.pack(); TutorialUtils.locateOnScreenCenter(frame); frame.setVisible(true); } /** * Builds and returns a panel with a header row and the sample rows. * * @return the example panel with a header and the sample rows */ public JComponent buildPanel() { FormLayout layout = new FormLayout( "r:max(80dlu;pref), 4dlu, 45dlu, 1dlu, pref, 4dlu, pref, 0:grow"); DefaultFormBuilder builder = new DefaultFormBuilder(layout); builder.setDefaultDialogBorder(); builder.getPanel().setFocusTraversalPolicy( MyFocusTraversalPolicy.INSTANCE); Utils.appendTitleRow(builder, "Description"); List fields = appendDemoRows(builder); Utils.appendButtonBar(builder, fields, "42", "aa42"); return builder.getPanel(); } /** * Appends the demo rows to the given builder and returns the List of * formatted text fields. * * @param builder the builder used to add components to * @return the List of formatted text fields */ private List appendDemoRows(DefaultFormBuilder builder) { // The Formatter is choosen by the initial value. JFormattedTextField defaultNumberField = new JFormattedTextField(new Long(42)); // The Formatter is choosen by the given Format. JFormattedTextField noInitialValueField = new JFormattedTextField(NumberFormat.getIntegerInstance()); // Uses a custom NumberFormat. NumberFormat customFormat = NumberFormat.getIntegerInstance(); customFormat.setMinimumIntegerDigits(3); JFormattedTextField customFormatField = new JFormattedTextField(new NumberFormatter(customFormat)); // Uses a custom NumberFormatter that prints natural language strings. JFormattedTextField customFormatterField = new JFormattedTextField(new CustomNumberFormatter()); // Uses a custom FormatterFactory that used different formatters // for the display and while editing. DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory(new NumberFormatter(), new CustomNumberFormatter()); JFormattedTextField formatterFactoryField = new JFormattedTextField(formatterFactory); // Wraps a NumberFormatter to map empty strings to null and vice versa. JFormattedTextField numberOrNullField = new JFormattedTextField(new EmptyNumberFormatter()); // Wraps a NumberFormatter to map empty strings to -1 and vice versa. Integer emptyValue = new Integer(-1); JFormattedTextField numberOrEmptyValueField = new JFormattedTextField(new EmptyNumberFormatter(emptyValue)); numberOrEmptyValueField.setValue(emptyValue); // Commits values on valid edit texts. DefaultFormatter formatter = new NumberFormatter(); formatter.setCommitsOnValidEdit(true); JFormattedTextField commitOnValidEditField = new JFormattedTextField(formatter); // Returns number values of type Integer NumberFormatter numberFormatter = new NumberFormatter(); numberFormatter.setValueClass(Integer.class); JFormattedTextField integerField = new JFormattedTextField(numberFormatter); Format displayFormat = new DisplayFormat(NumberFormat.getIntegerInstance()); Format typedDisplayFormat = new DisplayFormat(NumberFormat.getIntegerInstance(), true); List fields = new LinkedList(); fields.add(Utils.appendRow(builder, "Default", defaultNumberField, typedDisplayFormat)); fields.add(Utils.appendRow(builder, "No initial value", noInitialValueField, displayFormat)); fields.add(Utils.appendRow(builder, "Empty <-> null", numberOrNullField, displayFormat)); fields.add(Utils.appendRow(builder, "Empty <-> -1", numberOrEmptyValueField, displayFormat)); fields.add(Utils.appendRow(builder, "Custom format", customFormatField, displayFormat)); fields.add(Utils.appendRow(builder, "Custom formatter", customFormatterField, displayFormat)); fields.add(Utils.appendRow(builder, "Formatter factory", formatterFactoryField, displayFormat)); fields.add(Utils.appendRow(builder, "Commits on valid edit", commitOnValidEditField, displayFormat)); fields.add(Utils.appendRow(builder, "Integer Result", integerField, typedDisplayFormat)); return fields; }
}
</source>
Dynamically change the format
<source lang="java">
import java.text.SimpleDateFormat; import java.util.Date; import javax.swing.JFormattedTextField; import javax.swing.text.DateFormatter; public class Main {
public static void main(String[] argv) { JFormattedTextField f = new JFormattedTextField(new SimpleDateFormat("yyyy-M-d")); f.setValue(new Date()); DateFormatter fmt = (DateFormatter) f.getFormatter(); fmt.setFormat(new SimpleDateFormat("d/M/yyyy")); f.setValue(f.getValue()); }
}
</source>
Field with different formats with focus and without
<source lang="java">
/* Java Swing, 2nd Edition By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole ISBN: 0-596-00408-7 Publisher: O"Reilly
- /
// FactoryDemo.java
//Demo 1: field with different formats with focus and without.
//Demo 2: Change the format of a field when the user presses a button.
//
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.MaskFormatter;
public class FactoryDemo {
public static JPanel demo1() { // Demo 1: field with different formats with focus and without JPanel pan = new JPanel(new BorderLayout()); pan.setBorder(new TitledBorder("Demo 1: format toggles with focus")); MaskFormatter withFocus = null, withoutFocus = null; try { withFocus = new MaskFormatter("LLLL"); withoutFocus = new MaskFormatter("UUUU"); } catch (ParseException pe) { } DefaultFormatterFactory factory = new DefaultFormatterFactory( withoutFocus, null, withFocus); JFormattedTextField field = new JFormattedTextField(factory); field.setValue("Four"); pan.add(field, BorderLayout.CENTER); return pan; } public static JPanel demo2() { // Demo 2: Change the format of a field when the user presses a button. // We can"t call field.setFormatter() because that"s a protected method. // (Plus it wouldn"t work anyway. The old factory would replace our new // formatter with an "old" one next time the field gains or loses // focus.) // The thing to do is send a new factory to field.setFormatterFactory(). JPanel pan = new JPanel(new BorderLayout()); pan.setBorder(new TitledBorder("Demo 2: change format midstream")); MaskFormatter lowercase = null; try { lowercase = new MaskFormatter("LLLL"); } catch (ParseException pe) { } final JFormattedTextField field = new JFormattedTextField(lowercase); field.setValue("Fore"); pan.add(field, BorderLayout.CENTER); final JButton change = new JButton("change format"); JPanel changePanel = new JPanel(); changePanel.add(change); pan.add(changePanel, BorderLayout.SOUTH); change.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { try { field.rumitEdit(); // commit current edit (if any) MaskFormatter uppercase = new MaskFormatter("UUUU"); DefaultFormatterFactory factory = new DefaultFormatterFactory( uppercase); field.setFormatterFactory(factory); change.setEnabled(false); } catch (ParseException pe) { } } }); return pan; } public static void main(String argv[]) { JFrame f = new JFrame("FactoryDemo"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().add(demo1(), BorderLayout.NORTH); f.getContentPane().add(demo2(), BorderLayout.SOUTH); f.setSize(240, 160); f.setVisible(true); }
}
</source>
Format and validate input field in Java Swing
<source lang="java">
import java.awt.BorderLayout; import java.text.NumberFormat; import javax.swing.JButton; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class Main extends JFrame {
public Main() { JPanel panel = new JPanel(); JLabel label = new JLabel("Number :"); JFormattedTextField tf = new JFormattedTextField(NumberFormat.getIntegerInstance()); tf.setColumns(10); panel.add(label); panel.add(tf); JButton button = new JButton("Click Me"); panel.add(button); getContentPane().add(panel, BorderLayout.SOUTH); pack(); } public static void main(String[] args) { Main tfe = new Main(); tfe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); tfe.setVisible(true); }
}
</source>
Formatted TextField Demo
<source lang="java">
/* From http://java.sun.ru/docs/books/tutorial/index.html */ /*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */
import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.NumberFormat; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; /**
* FormattedTextFieldDemo.java is a 1.4 example that * requires no other files. * * It implements a mortgage calculator that uses four * JFormattedTextFields. */
public class FormattedTextFieldDemo extends JPanel
implements PropertyChangeListener { //Values for the fields private double amount = 100000; private double rate = 7.5; //7.5% private int numPeriods = 30; //Labels to identify the fields private JLabel amountLabel; private JLabel rateLabel; private JLabel numPeriodsLabel; private JLabel paymentLabel; //Strings for the labels private static String amountString = "Loan Amount: "; private static String rateString = "APR (%): "; private static String numPeriodsString = "Years: "; private static String paymentString = "Monthly Payment: "; //Fields for data entry private JFormattedTextField amountField; private JFormattedTextField rateField; private JFormattedTextField numPeriodsField; private JFormattedTextField paymentField; //Formats to format and parse numbers private NumberFormat amountFormat; private NumberFormat percentFormat; private NumberFormat paymentFormat; public FormattedTextFieldDemo() { super(new BorderLayout()); setUpFormats(); double payment = computePayment(amount, rate, numPeriods); //Create the labels. amountLabel = new JLabel(amountString); rateLabel = new JLabel(rateString); numPeriodsLabel = new JLabel(numPeriodsString); paymentLabel = new JLabel(paymentString); //Create the text fields and set them up. amountField = new JFormattedTextField(amountFormat); amountField.setValue(new Double(amount)); amountField.setColumns(10); amountField.addPropertyChangeListener("value", this); rateField = new JFormattedTextField(percentFormat); rateField.setValue(new Double(rate)); rateField.setColumns(10); rateField.addPropertyChangeListener("value", this); numPeriodsField = new JFormattedTextField(); numPeriodsField.setValue(new Integer(numPeriods)); numPeriodsField.setColumns(10); numPeriodsField.addPropertyChangeListener("value", this); paymentField = new JFormattedTextField(paymentFormat); paymentField.setValue(new Double(payment)); paymentField.setColumns(10); paymentField.setEditable(false); paymentField.setForeground(Color.red); //Tell accessibility tools about label/textfield pairs. amountLabel.setLabelFor(amountField); rateLabel.setLabelFor(rateField); numPeriodsLabel.setLabelFor(numPeriodsField); paymentLabel.setLabelFor(paymentField); //Lay out the labels in a panel. JPanel labelPane = new JPanel(new GridLayout(0,1)); labelPane.add(amountLabel); labelPane.add(rateLabel); labelPane.add(numPeriodsLabel); labelPane.add(paymentLabel); //Layout the text fields in a panel. JPanel fieldPane = new JPanel(new GridLayout(0,1)); fieldPane.add(amountField); fieldPane.add(rateField); fieldPane.add(numPeriodsField); fieldPane.add(paymentField); //Put the panels in this panel, labels on left, //text fields on right. setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); add(labelPane, BorderLayout.CENTER); add(fieldPane, BorderLayout.LINE_END); } /** Called when a field"s "value" property changes. */ public void propertyChange(PropertyChangeEvent e) { Object source = e.getSource(); if (source == amountField) { amount = ((Number)amountField.getValue()).doubleValue(); } else if (source == rateField) { rate = ((Number)rateField.getValue()).doubleValue(); } else if (source == numPeriodsField) { numPeriods = ((Number)numPeriodsField.getValue()).intValue(); } double payment = computePayment(amount, rate, numPeriods); paymentField.setValue(new Double(payment)); } /** * Create the GUI and show it. For thread safety, * this method should be invoked from the * event-dispatching thread. */ private static void createAndShowGUI() { //Make sure we have nice window decorations. JFrame.setDefaultLookAndFeelDecorated(true); //Create and set up the window. JFrame frame = new JFrame("FormattedTextFieldDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the content pane. JComponent newContentPane = new FormattedTextFieldDemo(); newContentPane.setOpaque(true); //content panes must be opaque frame.setContentPane(newContentPane); //Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application"s GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } //Compute the monthly payment based on the loan amount, //APR, and length of loan. double computePayment(double loanAmt, double rate, int numPeriods) { double I, partial1, denominator, answer; numPeriods *= 12; //get number of months if (rate > 0.01) { I = rate / 100.0 / 12.0; //get monthly rate from annual partial1 = Math.pow((1 + I), (0.0 - numPeriods)); denominator = (1 - partial1) / I; } else { //rate ~= 0 denominator = numPeriods; } answer = (-1 * loanAmt) / denominator; return answer; } //Create and set up number formats. These objects also //parse numbers input by user. private void setUpFormats() { amountFormat = NumberFormat.getNumberInstance(); percentFormat = NumberFormat.getNumberInstance(); percentFormat.setMinimumFractionDigits(3); paymentFormat = NumberFormat.getCurrencyInstance(); }
}
</source>
Formatted TextField Example
<source lang="java">
/* Core SWING Advanced Programming By Kim Topley ISBN: 0 13 083292 8 Publisher: Prentice Hall
- /
import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; import javax.swing.UIManager; import javax.swing.event.DocumentEvent; import javax.swing.plaf.ruponentUI; import javax.swing.plaf.metal.MetalTextFieldUI; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.FieldView; import javax.swing.text.JTextComponent; import javax.swing.text.PlainDocument; import javax.swing.text.Position; import javax.swing.text.Segment; import javax.swing.text.Utilities; import javax.swing.text.View; import javax.swing.text.ViewFactory; public class FormattedTextFieldExample {
public static void main(String[] args) { try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (Exception evt) {} JFrame f = new JFrame("Formatted Text Field"); final FormattedTextField tf = new FormattedTextField(); FormattedTextField.FormatSpec formatSpec = new FormattedTextField.FormatSpec( "(...)-...-....", "(***)-***-****"); tf.setFormatSpec(formatSpec); f.getContentPane().add(tf, "Center"); f.getContentPane().add(new JLabel("Phone Number: ", JLabel.RIGHT), "West"); tf.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { System.out.println("Field content is <" + tf.getText() + ">"); } }); f.pack(); f.setVisible(true); }
} class FormattedTextField extends JTextField {
public FormattedTextField() { this(null, null, 0, null); } public FormattedTextField(String text, FormatSpec spec) { this(null, text, 0, spec); } public FormattedTextField(int columns, FormatSpec spec) { this(null, null, columns, spec); } public FormattedTextField(String text, int columns, FormatSpec spec) { this(null, text, columns, spec); } public FormattedTextField(Document doc, String text, int columns, FormatSpec spec) { super(doc, text, columns); setFont(new Font("monospaced", Font.PLAIN, 14)); if (spec != null) { setFormatSpec(spec); } } public void updateUI() { setUI(new FormattedTextFieldUI()); } public FormatSpec getFormatSpec() { return formatSpec; } public void setFormatSpec(FormattedTextField.FormatSpec formatSpec) { FormatSpec oldFormatSpec = this.formatSpec; // Do nothing if no change to the format specification if (formatSpec.equals(oldFormatSpec) == false) { this.formatSpec = formatSpec; // Limit the input to the number of markers. Document doc = getDocument(); if (doc instanceof BoundedPlainDocument) { ((BoundedPlainDocument) doc).setMaxLength(formatSpec .getMarkerCount()); } // Notify a change in the format spec firePropertyChange(FORMAT_PROPERTY, oldFormatSpec, formatSpec); } } // Use a model that bounds the input length protected Document createDefaultModel() { BoundedPlainDocument doc = new BoundedPlainDocument(); doc .addInsertErrorListener(new BoundedPlainDocument.InsertErrorListener() { public void insertFailed(BoundedPlainDocument doc, int offset, String str, AttributeSet a) { // Beep when the field is full Toolkit.getDefaultToolkit().beep(); } }); return doc; } public static class FormatSpec { public FormatSpec(String format, String mask) { this.format = format; this.mask = mask; this.formatSize = format.length(); if (formatSize != mask.length()) { throw new IllegalArgumentException( "Format and mask must be the same size"); } for (int i = 0; i < formatSize; i++) { if (mask.charAt(i) == MARKER_CHAR) { markerCount++; } } } public String getFormat() { return format; } public String getMask() { return mask; } public int getFormatSize() { return formatSize; } public int getMarkerCount() { return markerCount; } public boolean equals(Object fmt) { return fmt != null && (fmt instanceof FormatSpec) && ((FormatSpec) fmt).getFormat().equals(format) && ((FormatSpec) fmt).getMask().equals(mask); } public String toString() { return "FormatSpec with format <" + format + ">, mask <" + mask + ">"; } private String format; private String mask; private int formatSize; private int markerCount; public static final char MARKER_CHAR = "*"; } protected FormatSpec formatSpec; public static final String FORMAT_PROPERTY = "format";
} class BoundedPlainDocument extends PlainDocument {
public BoundedPlainDocument() { // Default constructor - must use setMaxLength later this.maxLength = 0; } public BoundedPlainDocument(int maxLength) { this.maxLength = maxLength; } public BoundedPlainDocument(AbstractDocument.Content content, int maxLength) { super(content); if (content.length() > maxLength) { throw new IllegalArgumentException( "Initial content larger than maximum size"); } this.maxLength = maxLength; } public void setMaxLength(int maxLength) { if (getLength() > maxLength) { throw new IllegalArgumentException( "Current content larger than new maximum size"); } this.maxLength = maxLength; } public int getMaxLength() { return maxLength; } public void insertString(int offset, String str, AttributeSet a) throws BadLocationException { if (str == null) { return; } // Note: be careful here - the content always has a // trailing newline, which should not be counted! int capacity = maxLength + 1 - getContent().length(); if (capacity >= str.length()) { // It all fits super.insertString(offset, str, a); } else { // It doesn"t all fit. Add as much as we can. if (capacity > 0) { super.insertString(offset, str.substring(0, capacity), a); } // Finally, signal an error. if (errorListener != null) { errorListener.insertFailed(this, offset, str, a); } } } public void addInsertErrorListener(InsertErrorListener l) { if (errorListener == null) { errorListener = l; return; } throw new IllegalArgumentException( "InsertErrorListener already registered"); } public void removeInsertErrorListener(InsertErrorListener l) { if (errorListener == l) { errorListener = null; } } public interface InsertErrorListener { public abstract void insertFailed(BoundedPlainDocument doc, int offset, String str, AttributeSet a); } protected InsertErrorListener errorListener; // Unicast listener protected int maxLength;
} class FormattedTextFieldUI extends MetalTextFieldUI implements
PropertyChangeListener { public static ComponentUI createUI(JComponent c) { return new FormattedTextFieldUI(); } public FormattedTextFieldUI() { super(); } public void installUI(JComponent c) { super.installUI(c); if (c instanceof FormattedTextField) { c.addPropertyChangeListener(this); editor = (FormattedTextField) c; formatSpec = editor.getFormatSpec(); } } public void uninstallUI(JComponent c) { super.uninstallUI(c); c.removePropertyChangeListener(this); } public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(FormattedTextField.FORMAT_PROPERTY)) { // Install the new format specification formatSpec = editor.getFormatSpec(); // Recreate the View hierarchy modelChanged(); } } // ViewFactory method - creates a view public View create(Element elem) { return new FormattedFieldView(elem, formatSpec); } protected FormattedTextField.FormatSpec formatSpec; protected FormattedTextField editor;
} class FormattedFieldView extends FieldView {
public FormattedFieldView(Element elem, FormattedTextField.FormatSpec formatSpec) { super(elem); this.contentBuff = new Segment(); this.measureBuff = new Segment(); this.workBuff = new Segment(); this.element = elem; buildMapping(formatSpec); // Build the model -> view map createContent(); // Update content string } // View methods start here public float getPreferredSpan(int axis) { int widthFormat; int widthContent; if (formatSize == 0 || axis == View.Y_AXIS) { return super.getPreferredSpan(axis); } widthFormat = Utilities.getTabbedTextWidth(measureBuff, getFontMetrics(), 0, this, 0); widthContent = Utilities.getTabbedTextWidth(contentBuff, getFontMetrics(), 0, this, 0); return Math.max(widthFormat, widthContent); } public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { a = adjustAllocation(a); Rectangle r = new Rectangle(a.getBounds()); FontMetrics fm = getFontMetrics(); r.height = fm.getHeight(); int oldCount = contentBuff.count; if (pos < offsets.length) { contentBuff.count = offsets[pos]; } else { // Beyond the end: point to the location // after the last model position. contentBuff.count = offsets[offsets.length - 1] + 1; } int offset = Utilities.getTabbedTextWidth(contentBuff, metrics, 0, this, element.getStartOffset()); contentBuff.count = oldCount; r.x += offset; r.width = 1; return r; } public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) { a = adjustAllocation(a); bias[0] = Position.Bias.Forward; int x = (int) fx; int y = (int) fy; Rectangle r = a.getBounds(); int startOffset = element.getStartOffset(); int endOffset = element.getEndOffset(); if (y < r.y || x < r.x) { return startOffset; } else if (y > r.y + r.height || x > r.x + r.width) { return endOffset - 1; } // The given position is within the bounds of the view. int offset = Utilities.getTabbedTextOffset(contentBuff, getFontMetrics(), r.x, x, this, startOffset); // The offset includes characters not in the model, // so get rid of them to return a true model offset. for (int i = 0; i < offsets.length; i++) { if (offset <= offsets[i]) { offset = i; break; } } // Don"t return an offset beyond the data // actually in the model. if (offset > endOffset - 1) { offset = endOffset - 1; } return offset; } public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) { super.insertUpdate(changes, adjustAllocation(a), f); createContent(); // Update content string } public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) { super.removeUpdate(changes, adjustAllocation(a), f); createContent(); // Update content string } // End of View methods // View drawing methods: overridden from PlainView protected void drawLine(int line, Graphics g, int x, int y) { // Set the colors JTextComponent host = (JTextComponent) getContainer(); unselected = (host.isEnabled()) ? host.getForeground() : host .getDisabledTextColor(); Caret c = host.getCaret(); selected = c.isSelectionVisible() ? host.getSelectedTextColor() : unselected; int p0 = element.getStartOffset(); int p1 = element.getEndOffset() - 1; int sel0 = ((JTextComponent) getContainer()).getSelectionStart(); int sel1 = ((JTextComponent) getContainer()).getSelectionEnd(); try { // If the element is empty or there is no selection // in this view, just draw the whole thing in one go. if (p0 == p1 || sel0 == sel1 || inView(p0, p1, sel0, sel1) == false) { drawUnselectedText(g, x, y, 0, contentBuff.count); return; } // There is a selection in this view. Draw up to three regions: // (a) The unselected region before the selection. // (b) The selected region. // (c) The unselected region after the selection. // First, map the selected region offsets to be relative // to the start of the region and then map them to view // offsets so that they take into account characters not // present in the model. int mappedSel0 = mapOffset(Math.max(sel0 - p0, 0)); int mappedSel1 = mapOffset(Math.min(sel1 - p0, p1 - p0)); if (mappedSel0 > 0) { // Draw an initial unselected region x = drawUnselectedText(g, x, y, 0, mappedSel0); } x = drawSelectedText(g, x, y, mappedSel0, mappedSel1); if (mappedSel1 < contentBuff.count) { drawUnselectedText(g, x, y, mappedSel1, contentBuff.count); } } catch (BadLocationException e) { // Should not happen! } } protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { g.setColor(unselected); workBuff.array = contentBuff.array; workBuff.offset = p0; workBuff.count = p1 - p0; return Utilities.drawTabbedText(workBuff, x, y, g, this, p0); } protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { workBuff.array = contentBuff.array; workBuff.offset = p0; workBuff.count = p1 - p0; g.setColor(selected); return Utilities.drawTabbedText(workBuff, x, y, g, this, p0); } // End of View drawing methods // Build the model-to-view mapping protected void buildMapping(FormattedTextField.FormatSpec formatSpec) { formatSize = formatSpec != null ? formatSpec.getFormatSize() : 0; if (formatSize != 0) { // Save the format string as a character array formatChars = formatSpec.getFormat().toCharArray(); // Allocate a buffer to store the formatted string formattedContent = new char[formatSize]; contentBuff.offset = 0; contentBuff.count = formatSize; contentBuff.array = formattedContent; // Keep the mask for computing // the preferred horizontal span, but use // a wide character for measurement char[] maskChars = formatSpec.getMask().toCharArray(); measureBuff.offset = 0; measureBuff.array = maskChars; measureBuff.count = formatSize; // Get the number of markers markerCount = formatSpec.getMarkerCount(); // Allocate an array to hold the offsets offsets = new int[markerCount]; // Create the offset array markerCount = 0; for (int i = 0; i < formatSize; i++) { if (maskChars[i] == FormattedTextField.FormatSpec.MARKER_CHAR) { offsets[markerCount++] = i; // Replace marker with a wide character // in the array used for measurement. maskChars[i] = WIDE_CHARACTER; } } } } // Use the document content and the format // string to build the display content protected void createContent() { try { Document doc = getDocument(); int startOffset = element.getStartOffset(); int endOffset = element.getEndOffset(); int length = endOffset - startOffset - 1; // If there is no format, use the raw data. if (formatSize != 0) { // Get the document content doc.getText(startOffset, length, workBuff); // Initialize the output buffer with the // format string. System.arraycopy(formatChars, 0, formattedContent, 0, formatSize); // Insert the model content into // the target string. int count = Math.min(length, markerCount); int firstOffset = workBuff.offset; // Place the model data into the output array for (int i = 0; i < count; i++) { formattedContent[offsets[i]] = workBuff.array[i + firstOffset]; } } else { doc.getText(startOffset, length, contentBuff); } } catch (BadLocationException bl) { contentBuff.count = 0; } } // Map a document offset to a view offset. protected int mapOffset(int pos) { pos -= element.getStartOffset(); if (pos >= offsets.length) { return contentBuff.count; } else { return offsets[pos]; } } // Determines whether the selection intersects // a given range of model offsets. protected boolean inView(int p0, int p1, int sel0, int sel1) { if (sel0 >= p0 && sel0 < p1) { return true; } if (sel0 < p0 && sel1 >= p0) { return true; } return false; } protected char[] formattedContent; // The formatted content for display protected char[] formatChars; // The format string as characters protected Segment contentBuff; // Segment pointing to formatted content protected Segment measureBuff; // Segment pointing to mask string protected Segment workBuff; // Segment used for scratch purposes protected Element element; // The mapped element protected int[] offsets; // Model-to-view offsets protected Color selected; // Selected text color protected Color unselected; // Unselected text color protected int formatSize; // Length of the formatting string protected int markerCount; // Number of markers in the format protected static final char WIDE_CHARACTER = "m";
}
</source>
Formatter Factory Demo
<source lang="java">
/* From http://java.sun.ru/docs/books/tutorial/index.html */ /*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */
import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.NumberFormat; import java.text.ParseException; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.NumberFormatter; /**
* FormatterFactoryDemo.java is a 1.4 example that * requires no other files. */
public class FormatterFactoryDemo extends JPanel
implements PropertyChangeListener { //Values for the text fields private double amount = 100000; private double rate = .075; //7.5 % private int numPeriods = 30; //Labels to identify the fields private JLabel amountLabel; private JLabel rateLabel; private JLabel numPeriodsLabel; private JLabel paymentLabel; //Strings for the labels private static String amountString = "Loan Amount: "; private static String rateString = "APR (%): "; private static String numPeriodsString = "Years: "; private static String paymentString = "Monthly Payment: "; //Fields for data entry private JFormattedTextField amountField; private JFormattedTextField rateField; private JFormattedTextField numPeriodsField; private JFormattedTextField paymentField; //Formats to format and parse numbers private NumberFormat amountDisplayFormat; private NumberFormat amountEditFormat; private NumberFormat percentDisplayFormat; private NumberFormat percentEditFormat; private NumberFormat paymentFormat; public FormatterFactoryDemo() { super(new BorderLayout()); setUpFormats(); double payment = computePayment(amount, rate, numPeriods); //Create the labels. amountLabel = new JLabel(amountString); rateLabel = new JLabel(rateString); numPeriodsLabel = new JLabel(numPeriodsString); paymentLabel = new JLabel(paymentString); //Create the text fields and set them up. amountField = new JFormattedTextField( new DefaultFormatterFactory( new NumberFormatter(amountDisplayFormat), new NumberFormatter(amountDisplayFormat), new NumberFormatter(amountEditFormat))); amountField.setValue(new Double(amount)); amountField.setColumns(10); amountField.addPropertyChangeListener("value", this); NumberFormatter percentEditFormatter = new NumberFormatter(percentEditFormat) { public String valueToString(Object o) throws ParseException { Number number = (Number)o; if (number != null) { double d = number.doubleValue() * 100.0; number = new Double(d); } return super.valueToString(number); } public Object stringToValue(String s) throws ParseException { Number number = (Number)super.stringToValue(s); if (number != null) { double d = number.doubleValue() / 100.0; number = new Double(d); } return number; } }; rateField = new JFormattedTextField( new DefaultFormatterFactory( new NumberFormatter(percentDisplayFormat), new NumberFormatter(percentDisplayFormat), percentEditFormatter)); rateField.setValue(new Double(rate)); rateField.setColumns(10); rateField.addPropertyChangeListener("value", this); numPeriodsField = new JFormattedTextField(); numPeriodsField.setValue(new Integer(numPeriods)); numPeriodsField.setColumns(10); numPeriodsField.addPropertyChangeListener("value", this); paymentField = new JFormattedTextField(paymentFormat); paymentField.setValue(new Double(payment)); paymentField.setColumns(10); paymentField.setEditable(false); paymentField.setForeground(Color.red); //Tell accessibility tools about label/textfield pairs. amountLabel.setLabelFor(amountField); rateLabel.setLabelFor(rateField); numPeriodsLabel.setLabelFor(numPeriodsField); paymentLabel.setLabelFor(paymentField); //Lay out the labels in a panel. JPanel labelPane = new JPanel(new GridLayout(0,1)); labelPane.add(amountLabel); labelPane.add(rateLabel); labelPane.add(numPeriodsLabel); labelPane.add(paymentLabel); //Layout the text fields in a panel. JPanel fieldPane = new JPanel(new GridLayout(0,1)); fieldPane.add(amountField); fieldPane.add(rateField); fieldPane.add(numPeriodsField); fieldPane.add(paymentField); //Put the panels in this panel, labels on left, //text fields on right. setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); add(labelPane, BorderLayout.CENTER); add(fieldPane, BorderLayout.LINE_END); } /** Called when a field"s "value" property changes. */ public void propertyChange(PropertyChangeEvent e) { Object source = e.getSource(); if (source == amountField) { amount = ((Number)amountField.getValue()).doubleValue(); } else if (source == rateField) { rate = ((Number)rateField.getValue()).doubleValue(); } else if (source == numPeriodsField) { numPeriods = ((Number)numPeriodsField.getValue()).intValue(); } double payment = computePayment(amount, rate, numPeriods); paymentField.setValue(new Double(payment)); } /** * Create the GUI and show it. For thread safety, * this method should be invoked from the * event-dispatching thread. */ private static void createAndShowGUI() { //Make sure we have nice window decorations. JFrame.setDefaultLookAndFeelDecorated(true); //Create and set up the window. JFrame frame = new JFrame("FormatterFactoryDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the content pane. JComponent newContentPane = new FormatterFactoryDemo(); newContentPane.setOpaque(true); //content panes must be opaque frame.setContentPane(newContentPane); //Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application"s GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } //Compute the monthly payment based on the loan amount, //APR, and length of loan. double computePayment(double loanAmt, double rate, int numPeriods) { double I, partial1, denominator, answer; numPeriods *= 12; //get number of months if (rate > 0.001) { I = rate / 12.0; //get monthly rate from annual partial1 = Math.pow((1 + I), (0.0 - numPeriods)); denominator = (1 - partial1) / I; } else { //rate ~= 0 denominator = numPeriods; } answer = (-1 * loanAmt) / denominator; return answer; } //Create and set up number formats. These objects also //parse numbers input by user. private void setUpFormats() { amountDisplayFormat = NumberFormat.getCurrencyInstance(); amountDisplayFormat.setMinimumFractionDigits(0); amountEditFormat = NumberFormat.getNumberInstance(); percentDisplayFormat = NumberFormat.getPercentInstance(); percentDisplayFormat.setMinimumFractionDigits(2); percentEditFormat = NumberFormat.getNumberInstance(); percentEditFormat.setMinimumFractionDigits(2); paymentFormat = NumberFormat.getCurrencyInstance(); }
}
</source>
Input: any number of hyphen-delimeted numbers. Output: int array
<source lang="java">
/* Java Swing, 2nd Edition By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole ISBN: 0-596-00408-7 Publisher: O"Reilly
- /
// CombinationFormatter.java
//Input: string of form "15-45-22" (any number of hyphen-delimeted numbers)
//
Output: int array
//
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.DefaultFormatter;
public class CombinationFormatter extends DefaultFormatter {
public CombinationFormatter() { setOverwriteMode(false); } public Object stringToValue(String string) throws java.text.ParseException { // input: string of form "15-45-22" (any number of hyphen-delimeted // numbers) // output: int array String s[] = string.split("-"); int a[] = new int[s.length]; for (int j = 0; j < a.length; j += 1) try { a[j] = Integer.parseInt(s[j]); } catch (NumberFormatException nfe) { throw new java.text.ParseException(s[j] + " is not an int", 0); } return a; } public String valueToString(Object value) throws java.text.ParseException { // input: int array // output: string of numerals separated by hyphens if (value == null) return null; if (!(value instanceof int[])) throw new java.text.ParseException("expected int[]", 0); int a[] = (int[]) value; StringBuffer sb = new StringBuffer(); for (int j = 0; j < a.length; j += 1) { if (j > 0) sb.append("-"); sb.append(a[j]); } return sb.toString(); } protected Action[] getActions() { Action[] actions = { new CombinationIncrementer("increment", 1), new CombinationIncrementer("decrement", -1) }; return actions; } // begin inner class ---------------------------------------- public class CombinationIncrementer extends AbstractAction { protected int delta; public CombinationIncrementer(String name, int delta) { // constructor super(name); // "name" must match something in the component"s // InputMap // or else this Action will not get invoked automatically. // Valid names include: "reset-field-edit", "increment", // "decrement", and "unselect" (see appendix B) this.delta = delta; } public void actionPerformed(java.awt.event.ActionEvent ae) { JFormattedTextField ftf = getFormattedTextField(); // from // AbstractFormtter if (ftf == null) return; String text = ftf.getText(); if (text == null) return; int pos = ftf.getCaretPosition(); int hyphenCount = 0; for (int j = 0; j < pos; j += 1) // how many hyphens precede the caret? if (text.charAt(j) == "-") hyphenCount += 1; try { int a[] = (int[]) stringToValue(text); a[hyphenCount] += delta; // change the number at caret position if (a[hyphenCount] < 0) a[hyphenCount] = 0; String newText = valueToString(a); ftf.setText(newText); // does not retain caret position if ((text.charAt(pos) == "-") && (newText.length() < text.length())) pos -= 1; // don"t let caret move past "-" when "10" changes // to "9" ftf.setCaretPosition(pos); } catch (Exception e) { return; } } } // end inner class ---------------------------------------- public static void main(String argv[]) { // a demo main() to show how CombinationFormatter could be used int comb1[] = { 35, 11, 19 }; int comb2[] = { 10, 20, 30 }; final JFormattedTextField field1 = new JFormattedTextField( new CombinationFormatter()); field1.setValue(comb1); final JFormattedTextField field2 = new JFormattedTextField( new CombinationFormatter()); field2.setValue(comb2); JPanel pan = new JPanel(); pan.add(new JLabel("Change the combination from")); pan.add(field1); pan.add(new JLabel("to")); pan.add(field2); JButton b = new JButton("Submit"); b.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent ae) { try { field1.rumitEdit(); // make sure current edit (if any) gets // committed field2.rumitEdit(); } catch (java.text.ParseException pe) { } int oldc[] = (int[]) field1.getValue(); int newc[] = (int[]) field2.getValue(); // // code to validate oldc[] and change to newc[] goes here // } }); pan.add(b); JFrame f = new JFrame("CombinationFormatter Demo"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setContentPane(pan); f.setSize(360, 100); f.setVisible(true); }
}
</source>
Input Verification Demo
<source lang="java">
/* From http://java.sun.ru/docs/books/tutorial/index.html */ /*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */
/*
* InputVerificationDemo.java is a 1.4 example that * requires no other files. */
import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.ParseException; import javax.swing.BorderFactory; import javax.swing.InputVerifier; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; /**
* InputVerificationDemo.java is a 1.4 example that requires no other files. * * Yet another mortgage calculator. However, instead of using a formatted text * field, as shown in FormattedTextFieldDemo, this example uses input * verification to validate user input. */
public class InputVerificationDemo extends JPanel {
//Default values private static double DEFAULT_AMOUNT = 100000; private static double DEFAULT_RATE = 7.5; //7.5% private static int DEFAULT_PERIOD = 30; //Labels to identify the text fields private JLabel amountLabel; private JLabel rateLabel; private JLabel numPeriodsLabel; private JLabel paymentLabel; //Strings for the labels private static String amountString = "Loan Amount (10,000-10,000,000): "; private static String rateString = "APR (>= 0%): "; private static String numPeriodsString = "Years (1-40): "; private static String paymentString = "Monthly Payment: "; //Text fields for data entry private JTextField amountField; private JTextField rateField; private JTextField numPeriodsField; private JTextField paymentField; //Formats to format and parse numbers private NumberFormat moneyFormat; private NumberFormat percentFormat; private DecimalFormat decimalFormat; private DecimalFormat paymentFormat; private MyVerifier verifier = new MyVerifier(); public InputVerificationDemo() { super(new BorderLayout()); setUpFormats(); double payment = computePayment(DEFAULT_AMOUNT, DEFAULT_RATE, DEFAULT_PERIOD); //Create the labels. amountLabel = new JLabel(amountString); rateLabel = new JLabel(rateString); numPeriodsLabel = new JLabel(numPeriodsString); paymentLabel = new JLabel(paymentString); //Create the text fields and set them up. amountField = new JTextField(moneyFormat.format(DEFAULT_AMOUNT), 10); amountField.setInputVerifier(verifier); rateField = new JTextField(percentFormat.format(DEFAULT_RATE), 10); rateField.setInputVerifier(verifier); numPeriodsField = new JTextField(decimalFormat.format(DEFAULT_PERIOD), 10); numPeriodsField.setInputVerifier(verifier); paymentField = new JTextField(paymentFormat.format(payment), 10); paymentField.setInputVerifier(verifier); paymentField.setEditable(false); //Remove this component from the focus cycle. paymentField.setFocusable(false); paymentField.setForeground(Color.red); //Register an action listener to handle Return. amountField.addActionListener(verifier); rateField.addActionListener(verifier); numPeriodsField.addActionListener(verifier); //Tell accessibility tools about label/textfield pairs. amountLabel.setLabelFor(amountField); rateLabel.setLabelFor(rateField); numPeriodsLabel.setLabelFor(numPeriodsField); paymentLabel.setLabelFor(paymentField); //Lay out the labels in a panel. JPanel labelPane = new JPanel(new GridLayout(0, 1)); labelPane.add(amountLabel); labelPane.add(rateLabel); labelPane.add(numPeriodsLabel); labelPane.add(paymentLabel); //Layout the text fields in a panel. JPanel fieldPane = new JPanel(new GridLayout(0, 1)); fieldPane.add(amountField); fieldPane.add(rateField); fieldPane.add(numPeriodsField); fieldPane.add(paymentField); //Put the panels in this panel, labels on left, //text fields on right. setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); add(labelPane, BorderLayout.CENTER); add(fieldPane, BorderLayout.LINE_END); } class MyVerifier extends InputVerifier implements ActionListener { double MIN_AMOUNT = 10000.0; double MAX_AMOUNT = 10000000.0; double MIN_RATE = 0.0; int MIN_PERIOD = 1; int MAX_PERIOD = 40; public boolean shouldYieldFocus(JComponent input) { boolean inputOK = verify(input); makeItPretty(input); updatePayment(); if (inputOK) { return true; } else { Toolkit.getDefaultToolkit().beep(); return false; } } protected void updatePayment() { double amount = DEFAULT_AMOUNT; double rate = DEFAULT_RATE; int numPeriods = DEFAULT_PERIOD; double payment = 0.0; //Parse the values. try { amount = moneyFormat.parse(amountField.getText()).doubleValue(); } catch (ParseException pe) { } try { rate = percentFormat.parse(rateField.getText()).doubleValue(); } catch (ParseException pe) { } try { numPeriods = decimalFormat.parse(numPeriodsField.getText()) .intValue(); } catch (ParseException pe) { } //Calculate the result and update the GUI. payment = computePayment(amount, rate, numPeriods); paymentField.setText(paymentFormat.format(payment)); } //This method checks input, but should cause no side effects. public boolean verify(JComponent input) { return checkField(input, false); } protected void makeItPretty(JComponent input) { checkField(input, true); } protected boolean checkField(JComponent input, boolean changeIt) { if (input == amountField) { return checkAmountField(changeIt); } else if (input == rateField) { return checkRateField(changeIt); } else if (input == numPeriodsField) { return checkNumPeriodsField(changeIt); } else { return true; //shouldn"t happen } } //Checks that the amount field is valid. If it is valid, //it returns true; otherwise, returns false. If the //change argument is true, this method reigns in the //value if necessary and (even if not) sets it to the //parsed number so that it looks good -- no letters, //for example. protected boolean checkAmountField(boolean change) { boolean wasValid = true; double amount = DEFAULT_AMOUNT; //Parse the value. try { amount = moneyFormat.parse(amountField.getText()).doubleValue(); } catch (ParseException pe) { wasValid = false; } //Value was invalid. if ((amount < MIN_AMOUNT) || (amount > MAX_AMOUNT)) { wasValid = false; if (change) { if (amount < MIN_AMOUNT) { amount = MIN_AMOUNT; } else { // amount is greater than MAX_AMOUNT amount = MAX_AMOUNT; } } } //Whether value was valid or not, format it nicely. if (change) { amountField.setText(moneyFormat.format(amount)); amountField.selectAll(); } return wasValid; } //Checks that the rate field is valid. If it is valid, //it returns true; otherwise, returns false. If the //change argument is true, this method reigns in the //value if necessary and (even if not) sets it to the //parsed number so that it looks good -- no letters, //for example. protected boolean checkRateField(boolean change) { boolean wasValid = true; double rate = DEFAULT_RATE; //Parse the value. try { rate = percentFormat.parse(rateField.getText()).doubleValue(); } catch (ParseException pe) { wasValid = false; } //Value was invalid. if (rate < MIN_RATE) { wasValid = false; if (change) { rate = MIN_RATE; } } //Whether value was valid or not, format it nicely. if (change) { rateField.setText(percentFormat.format(rate)); rateField.selectAll(); } return wasValid; } //Checks that the numPeriods field is valid. If it is valid, //it returns true; otherwise, returns false. If the //change argument is true, this method reigns in the //value if necessary and (even if not) sets it to the //parsed number so that it looks good -- no letters, //for example. protected boolean checkNumPeriodsField(boolean change) { boolean wasValid = true; int numPeriods = DEFAULT_PERIOD; //Parse the value. try { numPeriods = decimalFormat.parse(numPeriodsField.getText()) .intValue(); } catch (ParseException pe) { wasValid = false; } //Value was invalid. if ((numPeriods < MIN_PERIOD) || (numPeriods > MAX_PERIOD)) { wasValid = false; if (change) { if (numPeriods < MIN_PERIOD) { numPeriods = MIN_PERIOD; } else { // numPeriods is greater than MAX_PERIOD numPeriods = MAX_PERIOD; } } } //Whether value was valid or not, format it nicely. if (change) { numPeriodsField.setText(decimalFormat.format(numPeriods)); numPeriodsField.selectAll(); } return wasValid; } public void actionPerformed(ActionEvent e) { JTextField source = (JTextField) e.getSource(); shouldYieldFocus(source); //ignore return value source.selectAll(); } } /** * Create the GUI and show it. For thread safety, this method should be * invoked from the event-dispatching thread. */ private static void createAndShowGUI() { //Make sure we have nice window decorations. JFrame.setDefaultLookAndFeelDecorated(true); //Create and set up the window. JFrame frame = new JFrame("InputVerificationDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the content pane. JComponent newContentPane = new InputVerificationDemo(); newContentPane.setOpaque(true); //content panes must be opaque frame.setContentPane(newContentPane); //Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application"s GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } //Compute the monthly payment based on the loan amount, //APR, and length of loan. double computePayment(double loanAmt, double rate, int numPeriods) { double I, partial1, denominator, answer; numPeriods *= 12; //get number of months if (rate > 0.01) { I = rate / 100.0 / 12.0; //get monthly rate from annual partial1 = Math.pow((1 + I), (0.0 - numPeriods)); denominator = (1 - partial1) / I; } else { //rate ~= 0 denominator = numPeriods; } answer = (-1 * loanAmt) / denominator; return answer; } //Create and set up number formats. These objects are used //for both parsing input and formatting output. private void setUpFormats() { moneyFormat = (NumberFormat) NumberFormat.getNumberInstance(); percentFormat = NumberFormat.getNumberInstance(); percentFormat.setMinimumFractionDigits(3); decimalFormat = (DecimalFormat) NumberFormat.getNumberInstance(); decimalFormat.setParseIntegerOnly(true); paymentFormat = (DecimalFormat) NumberFormat.getNumberInstance(); paymentFormat.setMaximumFractionDigits(2); paymentFormat.setNegativePrefix("("); paymentFormat.setNegativeSuffix(")"); }
}
</source>
Input Verification Dialog Demo
<source lang="java">
/* From http://java.sun.ru/docs/books/tutorial/index.html */ /*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */
/*
* InputVerificationDialogDemo.java is a 1.4 example that * requires no other files. */
import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.ParseException; import javax.swing.BorderFactory; import javax.swing.InputVerifier; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; /**
* InputVerificationDialogDemo.java is a 1.4 example that requires no other * files. * * Yet another mortgage calculator. However, instead of using a formatted text * field, as shown in FormattedTextFieldDemo, this example uses input * verification to validate user input. This one uses a dialog to warn people * when their input is bad. */
public class InputVerificationDialogDemo extends JPanel {
//Default values private static double DEFAULT_AMOUNT = 100000; private static double DEFAULT_RATE = 7.5; //7.5 % private static int DEFAULT_PERIOD = 30; //Labels to identify the text fields private JLabel amountLabel; private JLabel rateLabel; private JLabel numPeriodsLabel; private JLabel paymentLabel; //Strings for the labels private static String amountString = "Loan Amount (10,000 - 10,000,000): "; private static String rateString = "APR (>= 0%): "; private static String numPeriodsString = "Years (1-40): "; private static String paymentString = "Monthly Payment: "; //Text fields for data entry private JTextField amountField; private JTextField rateField; private JTextField numPeriodsField; private JTextField paymentField; //Formats to format and parse numbers private NumberFormat moneyFormat; private NumberFormat percentFormat; private DecimalFormat decimalFormat; private DecimalFormat paymentFormat; private NumberFormat integerFormat; private MyVerifier verifier = new MyVerifier(); public InputVerificationDialogDemo() { super(new BorderLayout()); setUpFormats(); double payment = computePayment(DEFAULT_AMOUNT, DEFAULT_RATE, DEFAULT_PERIOD); //Create the labels. amountLabel = new JLabel(amountString); rateLabel = new JLabel(rateString); numPeriodsLabel = new JLabel(numPeriodsString); paymentLabel = new JLabel(paymentString); //Create the text fields and set them up. amountField = new JTextField(moneyFormat.format(DEFAULT_AMOUNT), 10); amountField.setInputVerifier(verifier); rateField = new JTextField(percentFormat.format(DEFAULT_RATE), 10); rateField.setInputVerifier(verifier); numPeriodsField = new JTextField(decimalFormat.format(DEFAULT_PERIOD), 10); numPeriodsField.setInputVerifier(verifier); paymentField = new JTextField(paymentFormat.format(payment), 10); paymentField.setInputVerifier(verifier); paymentField.setEditable(false); //Remove this component from the focus cycle. paymentField.setFocusable(false); paymentField.setForeground(Color.red); //Register an action listener to handle Return. amountField.addActionListener(verifier); rateField.addActionListener(verifier); numPeriodsField.addActionListener(verifier); //Tell accessibility tools about label/textfield pairs. amountLabel.setLabelFor(amountField); rateLabel.setLabelFor(rateField); numPeriodsLabel.setLabelFor(numPeriodsField); paymentLabel.setLabelFor(paymentField); //Lay out the labels in a panel. JPanel labelPane = new JPanel(new GridLayout(0, 1)); labelPane.add(amountLabel); labelPane.add(rateLabel); labelPane.add(numPeriodsLabel); labelPane.add(paymentLabel); //Layout the text fields in a panel. JPanel fieldPane = new JPanel(new GridLayout(0, 1)); fieldPane.add(amountField); fieldPane.add(rateField); fieldPane.add(numPeriodsField); fieldPane.add(paymentField); //Put the panels in this panel, labels on left, //text fields on right. setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); add(labelPane, BorderLayout.CENTER); add(fieldPane, BorderLayout.LINE_END); } class MyVerifier extends InputVerifier implements ActionListener { double MIN_AMOUNT = 10000.0; double MAX_AMOUNT = 10000000.0; double MIN_RATE = 0.0; int MIN_PERIOD = 1; int MAX_PERIOD = 40; String message = null; public boolean shouldYieldFocus(JComponent input) { boolean inputOK = verify(input); makeItPretty(input); updatePayment(); if (inputOK) { return true; } //Avoid possible focus-transfer problems when bringing up //the dialog by temporarily removing the input verifier. //This is a workaround for bug #4532517. input.setInputVerifier(null); //Pop up the message dialog. message += ".\nPlease try again."; JOptionPane.showMessageDialog(null, //no owner frame message, //text to display "Invalid Value", //title JOptionPane.WARNING_MESSAGE); //Reinstall the input verifier. input.setInputVerifier(this); //Beep and then tell whoever called us that we don"t //want to yield focus. Toolkit.getDefaultToolkit().beep(); return false; } protected void updatePayment() { double amount = DEFAULT_AMOUNT; double rate = DEFAULT_RATE; int numPeriods = DEFAULT_PERIOD; double payment = 0.0; //Parse the values. try { amount = moneyFormat.parse(amountField.getText()).doubleValue(); } catch (ParseException pe) { } try { rate = percentFormat.parse(rateField.getText()).doubleValue(); } catch (ParseException pe) { } try { numPeriods = decimalFormat.parse(numPeriodsField.getText()) .intValue(); } catch (ParseException pe) { } //Calculate the result and update the GUI. payment = computePayment(amount, rate, numPeriods); paymentField.setText(paymentFormat.format(payment)); } //This method checks input, but should cause no side effects. public boolean verify(JComponent input) { return checkField(input, false); } protected void makeItPretty(JComponent input) { checkField(input, true); } protected boolean checkField(JComponent input, boolean changeIt) { if (input == amountField) { return checkAmountField(changeIt); } else if (input == rateField) { return checkRateField(changeIt); } else if (input == numPeriodsField) { return checkNumPeriodsField(changeIt); } else { return true; //shouldn"t happen } } //Checks that the amount field is valid. If it is valid, //it returns true, otherwise it sets the message field and //returns false. If the change argument is true, set //the textfield to the parsed number so that it looks //good -- no letters, for example. public boolean checkAmountField(boolean change) { boolean wasValid = true; double amount = DEFAULT_AMOUNT; //Parse the value. try { amount = moneyFormat.parse(amountField.getText()).doubleValue(); } catch (ParseException pe) { message = "Invalid money format in Loan Amount field"; return false; } //Value was invalid. if ((amount < MIN_AMOUNT) || (amount > MAX_AMOUNT)) { wasValid = false; if (amount < MIN_AMOUNT) { message = "Loan Amount was < " + integerFormat.format(MIN_AMOUNT); } else { //amount is greater than MAX_AMOUNT message = "Loan Amount was > " + integerFormat.format(MAX_AMOUNT); } } //Whether value was valid or not, format it nicely. if (change) { amountField.setText(moneyFormat.format(amount)); amountField.selectAll(); } return wasValid; } //Checks that the rate field is valid. If it is valid, //it returns true, otherwise it sets the message field and //returns false. If the change argument is true, set the //textfield to the parsed number so that it looks good -- no //letters, for example. public boolean checkRateField(boolean change) { boolean wasValid = true; double rate = DEFAULT_RATE; //Parse the value. try { rate = percentFormat.parse(rateField.getText()).doubleValue(); } catch (ParseException pe) { message = "Invalid percent format in APR field"; return false; } //Value was invalid. if (rate < MIN_RATE) { wasValid = false; message = "Bad value: APR was < " + MIN_RATE; } //Whether value was valid or not, format it nicely. if (change) { rateField.setText(percentFormat.format(rate)); rateField.selectAll(); } return wasValid; } //Checks that the numPeriods field is valid. If it is valid, //it returns true, otherwise it sets the message field and //returns false. If the change argument is true, set the //textfield to the parsed number so that it looks good -- no //letters, for example. public boolean checkNumPeriodsField(boolean change) { boolean wasValid = true; int numPeriods = DEFAULT_PERIOD; //Parse the value. try { numPeriods = decimalFormat.parse(numPeriodsField.getText()) .intValue(); } catch (ParseException pe) { message = "Invalid decimal format in Years field"; return false; } //Value was invalid. if (numPeriods < MIN_PERIOD) { wasValid = false; message = "Bad value: Number of years was < " + integerFormat.format(MIN_PERIOD); } else if (numPeriods > MAX_PERIOD) { wasValid = false; message = "Bad value: Number of years was > " + integerFormat.format(MAX_PERIOD); } //Whether value was valid or not, format it nicely. if (change) { numPeriodsField.setText(decimalFormat.format(numPeriods)); numPeriodsField.selectAll(); } return wasValid; } public void actionPerformed(ActionEvent e) { JTextField source = (JTextField) e.getSource(); shouldYieldFocus(source); //ignore return value source.selectAll(); } } /** * Create the GUI and show it. For thread safety, this method should be * invoked from the event-dispatching thread. */ private static void createAndShowGUI() { //We can"t set this due to bug #4819813. //This bug causes the Java look and feel window //decorations to be focusable. //JFrame.setDefaultLookAndFeelDecorated(true); //Create and set up the window. JFrame frame = new JFrame("InputVerificationDialogDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JComponent newContentPane = new InputVerificationDialogDemo(); newContentPane.setOpaque(true); //content panes must be opaque frame.setContentPane(newContentPane); //Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application"s GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } //Compute the monthly payment based on the loan amount, //APR, and length of loan. double computePayment(double loanAmt, double rate, int numPeriods) { double I, partial1, denominator, answer; numPeriods *= 12; //get number of months if (rate > 0.01) { I = rate / 100.0 / 12.0; //get monthly rate from annual partial1 = Math.pow((1 + I), (0.0 - numPeriods)); denominator = (1 - partial1) / I; } else { //rate ~= 0 denominator = numPeriods; } answer = (-1 * loanAmt) / denominator; return answer; } //Create and set up number formats. These objects also //parse numbers input by user. private void setUpFormats() { moneyFormat = (NumberFormat) NumberFormat.getNumberInstance(); percentFormat = NumberFormat.getNumberInstance(); percentFormat.setMinimumFractionDigits(3); decimalFormat = (DecimalFormat) NumberFormat.getNumberInstance(); decimalFormat.setParseIntegerOnly(true); paymentFormat = (DecimalFormat) NumberFormat.getNumberInstance(); paymentFormat.setMaximumFractionDigits(2); paymentFormat.setNegativePrefix("("); paymentFormat.setNegativeSuffix(")"); integerFormat = NumberFormat.getIntegerInstance(); }
}
</source>
JFormattedTextField: an input mask (###) ###-#### for a telephone number
<source lang="java">
import java.awt.Container; import java.text.ParseException; import javax.swing.BoxLayout; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.text.MaskFormatter; public class Main {
public static void main(String args[]) throws ParseException { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container content = f.getContentPane(); content.setLayout(new BoxLayout(content, BoxLayout.PAGE_AXIS)); MaskFormatter mf1 = new MaskFormatter("###-###-###"); mf1.setPlaceholderCharacter("_"); JFormattedTextField ftf1 = new JFormattedTextField(mf1); content.add(ftf1); MaskFormatter mf2 = new MaskFormatter("(###) ###-####"); JFormattedTextField ftf2 = new JFormattedTextField(mf2); content.add(ftf2); f.setSize(300, 100); f.setVisible(true); }
}
</source>
Make custom Input Text Formatter in Java
<source lang="java">
import java.awt.BorderLayout; import java.text.ParseException; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.text.DefaultFormatter; class MyFormatter extends DefaultFormatter {
public MyFormatter() { super(); } public String valueToString(Object arg0) throws ParseException { return super.valueToString(arg0); } public Object stringToValue(String arg0) throws ParseException { try { int value = Integer.parseInt(arg0); if (value >= 1 && value <= 10) { return "" + value; } else { return "Invalid"; } } catch (Exception e) { return "Invalid"; } }
} public class Main extends JFrame {
public Main() { JPanel panel = new JPanel(); JLabel label = new JLabel("Number :"); JFormattedTextField tf = new JFormattedTextField(new MyFormatter()); tf.setColumns(10); panel.add(label); panel.add(tf); getContentPane().add(panel, BorderLayout.SOUTH); pack(); } public static void main(String[] args) { Main mfe = new Main(); mfe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mfe.setVisible(true); }
}
</source>
Support a date with the custom format: 2009-1-1
<source lang="java">
import java.text.SimpleDateFormat; import java.util.Date; import javax.swing.JFormattedTextField; public class Main {
public static void main(String[] argv) throws Exception { JFormattedTextField tft3 = new JFormattedTextField(new SimpleDateFormat("yyyy-M-d")); tft3.setValue(new Date()); Date date = (Date) tft3.getValue(); }
}
</source>
Using an InputVerifier with a formatted textfield
<source lang="java">
/* Java Swing, 2nd Edition By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole ISBN: 0-596-00408-7 Publisher: O"Reilly
- /
// FtfInputVerifier.java //An example of using an InputVerifier with a formatted textfield. // import javax.swing.BorderFactory; import javax.swing.InputVerifier; import javax.swing.JComponent; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; public class FtfInputVerifier {
public static void main(String argv[]) { java.net.URL u = null; try { u = new java.net.URL("http://www.ora.ru/"); } catch (java.net.MalformedURLException ignored) { } // create two identical JFormattedTextFields JFormattedTextField ftf1 = new JFormattedTextField(u); JFormattedTextField ftf2 = new JFormattedTextField(u); // and set an InputVerifier on one of them ftf2.setInputVerifier(new InputVerifier() { public boolean verify(JComponent input) { if (!(input instanceof JFormattedTextField)) return true; // give up focus return ((JFormattedTextField) input).isEditValid(); } }); JPanel p = new JPanel(new java.awt.GridLayout(0, 2, 3, 8)); p.add(new JLabel("plain JFormattedTextField:", JLabel.RIGHT)); p.add(ftf1); p.add(new JLabel("FTF with InputVerifier:", JLabel.RIGHT)); p.add(ftf2); p.add(new JLabel("plain JTextField:", JLabel.RIGHT)); p.add(new JTextField(u.toString())); p.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); JFrame f = new JFrame("FtfInputVerifier"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().add( new JLabel("Try to delete the colon in each field.", JLabel.CENTER), java.awt.BorderLayout.NORTH); f.getContentPane().add(p, java.awt.BorderLayout.CENTER); f.pack(); f.setVisible(true); }
}
</source>