文档

Java™教程
隐藏目录
如何使用格式化文本字段
路径: 使用Swing创建GUI
课程: 使用Swing组件
章节: 如何使用各种组件

如何使用格式化文本字段

格式化文本字段提供了一种开发者可以指定可在文本字段中输入的有效字符集的方法。具体来说,JFormattedTextField 类从 JTextField 类继承了一些特性,并添加了一个格式化器和一个值对象。格式化器将字段的值转换为显示的文本,以及将文本转换为字段的值。

使用Swing提供的格式化器,可以设置格式化文本字段以便以本地化格式输入日期和数字。另一种格式化器可以使用字符掩码来指定字段中每个位置可以输入的字符集。例如,可以为特定格式(如 (XX) X-XX-XX-XX-XX)的电话号码输入指定一个掩码。

如果格式化文本字段的可能值具有明显的顺序,则应使用 微调器。微调器默认使用格式化文本字段,但添加了两个按钮,用户可以使用这些按钮在序列中选择一个值。

使用格式化文本字段的另一种选择或补充是在字段上安装一个输入验证器。组件的输入验证器在组件即将失去键盘焦点时被调用。输入验证器使您能够检查组件的值是否有效,并可选择更改它或停止焦点的转移。

此GUI使用格式化文本字段以四种不同的格式显示数字。

FormattedTextFieldDemo的快照

试一试: 
  1. 点击启动按钮以使用Java™ Web Start 运行FormattedTextFieldDemo(下载JDK 7或更高版本)。或者,如果要自己编译和运行示例,请参考示例索引启动FormattedTextFieldDemo应用程序
  2. 尝试不同的贷款金额、年利率(APR)和贷款期限。
    注意,只要您输入的文本有效,当您按下Enter键或将焦点移出正在编辑的字段时,月还款字段就会更新。
  3. 在贷款金额字段中输入无效文本,如 "abcd",然后按Enter键。
    月还款字段保持不变。当您将焦点从贷款金额字段移开时,文本会恢复到字段的上一个有效值。
  4. 在贷款金额字段中输入边缘有效的文本,如 "2000abcd",然后按Enter键。
    月还款字段会更新,尽管贷款金额字段仍然显示 2000abcd。当您将焦点从贷款金额字段移开时,它显示的文本会更新为其值的格式化版本,例如 "2,000"。

您可以在FormattedTextFieldDemo.java中找到此程序的全部代码。这段代码创建了第一个字段。

amountField = new JFormattedTextField(amountFormat);
amountField.setValue(new Double(amount));
amountField.setColumns(10);
amountField.addPropertyChangeListener("value", this);
...
amountFormat = NumberFormat.getNumberInstance();

用于创建amountField对象的构造函数接受一个java.text.Format参数。该Format对象由字段的格式化程序用于将字段的值转换为文本以及将文本转换为字段的值。

剩余的代码设置了amountField对象。 setValue方法将字段的值属性设置为以Double对象表示的浮点数。 setColumns方法继承自JTextField类,用于指定字段的首选大小。调用addPropertyChangeListener方法为字段的值属性注册监听器,以便在用户更改贷款金额时更新月付款字段。

本节的其余部分涵盖了以下主题:

本节不介绍从JTextField类继承的API。有关该API的说明,请参阅如何使用文本字段

创建和初始化格式化文本字段

以下代码创建并初始化了FormattedTextFieldDemo示例中的剩余三个字段。

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);

...
percentFormat = NumberFormat.getNumberInstance();
percentFormat.setMinimumFractionDigits(2);

paymentFormat = NumberFormat.getCurrencyInstance();

设置rateField对象的代码几乎与之前列出的其他字段的代码完全相同。唯一的区别是格式略有不同,这要归功于代码percentFormat.setMinimumFractionDigits(2)

创建numPeriodsField对象的代码没有显式设置格式或格式化程序。相反,它将值设置为一个Integer并启用字段使用Integer对象的默认格式化程序。之前的两个字段没有这样做,因为默认的格式化程序不适用于Double对象。结果不是所需的。如何指定格式和格式化程序将在本节后面讨论。

付款字段与其他字段不同,因为它是不可编辑的,其文本使用不同的颜色,并且没有属性更改监听器。否则,它与其他字段完全相同。我们可以选择使用文本字段标签。无论使用哪个组件,我们仍然可以使用paymentFormat方法将付款金额解析为要显示的文本。

设置和获取字段的值

在使用格式化文本字段时,请记住以下几点:


格式化文本字段的文本和其是两个不同的属性,而值通常滞后于文本。


文本属性由JTextField类定义。该属性始终反映字段显示的内容。值属性由JFormattedTextField类定义,可能不会反映字段中显示的最新文本。当用户输入时,文本属性会更改,但是值属性直到更改被提交之后才会更改。

更准确地说,可以使用setValue方法或commitEdit方法设置格式化文本字段的值。 setValue方法将值设置为指定的参数。该参数可以是任何Object,但是格式化程序需要能够将其转换为字符串。否则,文本字段不会显示任何实质性信息。

commitEdit方法将值设置为格式化程序确定为字段文本所表示的任何对象。当满足以下任一条件时,将自动调用commitEdit方法:


注意: 

某些格式化程序可能会不断更新值,使得失去焦点变得无意义,因为值始终与文本指定的值相同。


当您设置格式化文本字段的值时,该字段的文本将更新以反映该值。值如何以文本形式表示取决于字段的格式化程序。

请注意,尽管JFormattedTextField类从JTextField类继承了setText方法,但通常不会在格式化文本字段上调用setText方法。如果这样做,字段的显示将相应更改,但值不会更新(除非字段的格式化程序不断更新它)。

要获取格式化文本字段的当前值,请使用getValue方法。如果需要,您可以在getValue之前调用commitEdit方法以确保值反映文本。因为getValue方法返回一个Object,所以您需要将其强制转换为用于字段值的类型。例如:

Date enteredDate = (Date)dateField.getValue();

要检测格式化文本字段值的更改,可以在格式化文本字段上注册一个属性更改监听器,以监听"value"属性的更改。属性更改监听器来自于FormattedTextFieldDemo示例:

//使用以下代码在每个字段上注册属性更改监听器:
//    someField.addPropertyChangeListener("value", this);

/** 当字段的"value"属性更改时调用。 */
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));
}

指定格式

Format类提供了一种格式化与语言环境相关的信息,如日期和数字。从InternationalFormatter类派生的格式化程序,如DateFormatterNumberFormatter类,使用Format对象在字段的文本和值之间进行转换。您可以通过调用DateFormatNumberFormat类中的工厂方法之一来获取Format对象,或者使用SimpleDateFormat构造函数之一来获取。


注意: 

第三个常用的格式化类MaskFormatter不继承InternationalFormatter类,也不使用格式。关于MaskFormatter的讨论可以参考使用MaskFormatter


创建Format对象时,可以自定义某些格式方面的内容,也可以通过特定于格式的API自定义其他内容。例如,继承自NumberFormat并且通常由其工厂方法返回的DecimalFormat对象,可以使用setMaximumFractionDigitssetNegativePrefix方法进行自定义。有关使用Format对象的信息,请参阅国际化教程的格式化部分。

将自定义格式与格式化文本字段关联的最简单方法是使用带有Format参数的JFormattedTextField构造函数创建字段。您可以在前面创建amountFieldrateField对象的代码示例中看到这种关联。

使用MaskFormatter

MaskFormatter类实现了一个格式化程序,用于指定字段文本中每个位置上哪些字符是有效的。例如,下面的代码创建了一个允许用户输入五位数邮政编码的MaskFormatter

zipField = new JFormattedTextField(
                    createFormatter("#####"));
...
protected MaskFormatter createFormatter(String s) {
    MaskFormatter formatter = null;
    try {
        formatter = new MaskFormatter(s);
    } catch (java.text.ParseException exc) {
        System.err.println("formatter is bad: " + exc.getMessage());
        System.exit(-1);
    }
    return formatter;
}

您可以通过运行TextInputDemo来尝试上述代码的结果。点击“启动”按钮以使用Java™ Web Start 运行TextInputDemo(下载JDK 7或更高版本)。或者,要自行编译和运行示例,请参考示例索引

启动TextInputDemo应用程序

程序的GUI界面显示。

TextInputDemo的快照

下表显示了可以在格式化掩码中使用的字符:

字符  描述
# 任何有效数字(Character.isDigit)。
'
(单引号)
转义字符,用于转义任何特殊的格式化字符。
U 任何字符(Character.isLetter)。所有小写字母都被映射为大写。
L 任何字符(Character.isLetter)。所有大写字母都被映射为小写。
A 任何字符或数字(Character.isLetterCharacter.isDigit)。
? 任何字符(Character.isLetter)。
* 任何内容。
H 任何十六进制字符(0-9、a-f或A-F)。

指定格式化程序和使用格式化程序工厂

在指定格式化程序时,请记住每个格式化程序对象一次只能被一个格式化文本字段使用。每个字段应至少有一个与之关联的格式化程序,其中恰好有一个在任何时间内被使用。

可以通过以下几种方式指定要用于格式化文本字段的格式化程序:

您可以通过使用带有格式化工厂参数的构造函数创建字段,或通过在字段上调用setFormatterFactory方法来设置字段的格式化工厂。要创建格式化工厂,通常可以使用DefaultFormatterFactory类的实例。 DefaultFormatterFactory对象使您可以指定在正在编辑值时返回的格式化程序、未在编辑的值或具有空值时返回的格式化程序。

下面的图像显示了基于FormattedTextFieldDemo示例的应用程序,该应用程序使用格式化工厂为贷款金额和年利率字段设置多个编辑器。在用户编辑贷款金额时,不使用美元符号,因此用户不被强制输入它。类似地,当用户编辑年利率字段时,不需要百分号。

单击“启动”按钮以使用Java™ Web Start 运行FormatterFactoryDemo(下载JDK 7或更高版本)。或者,要自行编译和运行示例,请参阅示例索引

启动FormatterFactoryDemo应用程序

FormatterFactoryDemo,正在编辑的金额字段 FormatterFactoryDemo,未安装自定义编辑器

以下代码使用DefaultFormatterFactory类的实例创建格式化程序并设置它们:

private double rate = .075;  //7.5 %
...
amountField = new JFormattedTextField(
                    new DefaultFormatterFactory(
                        new NumberFormatter(amountDisplayFormat),
                        new NumberFormatter(amountDisplayFormat),
                        new NumberFormatter(amountEditFormat)));
...
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));
...
amountDisplayFormat = NumberFormat.getCurrencyInstance();
amountDisplayFormat.setMinimumFractionDigits(0);
amountEditFormat = NumberFormat.getNumberInstance();

percentDisplayFormat = NumberFormat.getPercentInstance();
percentDisplayFormat.setMinimumFractionDigits(2);
percentEditFormat = NumberFormat.getNumberInstance();
percentEditFormat.setMinimumFractionDigits(2);

加粗的代码突出显示了对DefaultFormatterFactory构造函数的调用。构造函数的第一个参数指定了用于格式化文本字段的默认格式器。第二个参数指定了显示格式器,在字段没有焦点时使用。第三个参数指定了编辑格式器,在字段有焦点时使用。代码没有使用第四个参数,但如果使用了第四个参数,它将指定空格式器,在字段的值为空时使用。由于没有指定空格式器,当值为空时将使用默认格式器。

代码自定义了使用percentEditFormat的格式器,通过创建NumberFormatter类的子类来实现。这个子类重写了NumberFormattervalueToStringstringToValue方法,以便将显示的数字转换为实际用于计算的值,并将值转换为数字。具体来说,显示的数字是实际值的100倍。原因是显示格式器使用的百分比格式自动将文本显示为值的100倍,因此对应的编辑格式器必须以相同的值显示文本。因为这个示例只使用了一个格式来进行显示和编辑,所以FormattedTextFieldDemo示例不需要关心这个转换。

您可以在FormatterFactoryDemo.java中找到整个程序的代码。

格式化文本字段 API

以下表格列出了一些常用的用于使用格式化文本字段的 API。

与格式化文本字段相关的类
类或接口 目的
JFormattedTextField JTextField的子类,支持格式化任意值。
JFormattedTextField.AbstractFormatter 所有JFormattedTextField格式化器的超类。格式化器强制执行编辑策略和导航策略,处理字符串到对象的转换,并根据需要操作JFormattedTextField以实现所需的策略。
JFormattedTextField.AbstractFormatterFactory 所有格式化器工厂的超类。每个JFormattedTextField都使用格式化器工厂来获取与文本字段状态最匹配的格式化器。
DefaultFormatterFactory 通常使用的格式化器工厂。根据传入的参数和焦点状态等详细信息提供格式化器。
DefaultFormatter JFormattedTextField.AbstractFormatter的子类,通过使用toString方法格式化任意对象。
MaskFormatter DefaultFormatter的子类,使用指定的字符掩码格式化和编辑字符串。(例如,可以使用"###-####"来指定七位数的电话号码。)
InternationalFormatter DefaultFormatter的子类,使用java.text.Format的实例来处理String的转换。
NumberFormatter InternationalFormatter的子类,通过使用NumberFormat的实例来支持数字格式化。
DateFormatter InternationalFormatter的子类,通过使用DateFormat的实例来支持日期格式化。
JFormattedTextField方法JFormattedTextField()
JFormattedTextField(Object)
JFormattedTextField(Format)
JFormattedTextField(AbstractFormatter)
JFormattedTextField(AbstractFormatterFactory)
JFormattedTextField(AbstractFormatterFactory, Object)ObjectFormatAbstractFormatterAbstractFormatterFactoryvoid setValue(Object)
Object getValue()JFormattedTextFieldsetValuevoid setFormatterFactory(AbstractFormatterFactory)DefaultFormatterFactoryAbstractFormatter getFormatter()DefaultFormattervoid setFocusLostBehavior(int)JFormattedTextFieldCOMMIT_OR_REVERTCOMMITPERSISTREVERTvoid commitEdit()ParseExceptionboolean isEditValid()
DefaultFormatter选项
方法 用途
void setCommitsOnValidEdit(boolean)
boolean getCommitsOnValidEdit()
设置或获取在编辑被推送回JFormattedTextField时的值。如果为true,每次有效编辑后都会调用commitEdit方法。默认情况下,此属性为false
void setOverwriteMode(boolean)
boolean getOverwriteMode()
设置或获取在插入字符时的行为。如果为true,新字符在插入时将覆盖模型中的现有字符。此属性的默认值在DefaultFormatter(以及MaskFormatter)中为true,在InternationalFormatter(以及DateFormatterNumberFormatter)中为false
void setAllowsInvalid(boolean)
boolean getAllowsInvalid()
设置或解释正在编辑的值是否允许在一段时间内无效。通常允许用户输入无效值,直到尝试调用commitEdit方法为止。默认情况下,DefaultFormatter将此属性初始化为true。在标准的Swing格式化程序中,只有MaskFormatter将此属性设置为false

使用格式化文本字段的示例

此表列出了使用格式化文本字段的示例,并指向这些示例的描述。

示例 所在位置 备注
FormattedTextFieldDemo 本节 使用了四个格式化文本字段。
SpinnerDemo 如何使用微调器 自定义了两个微调器使用的格式化文本字段的外观。
Converter 使用模型 每个ConversionPanel将一个格式化文本字段与一个滑块配对。
TextInputDemo 本节 展示了如何同时使用文本字段、微调器和格式化文本字段,并演示了如何使用MaskFormatter。包括了在焦点刚刚接收到的字段上选择文本的代码。
FormatterFactoryDemo 本节 FormattedTextFieldDemo的一个变体,使用格式化器工厂为两个格式化文本字段指定了多个格式化器。

上一页: 如何使用文件选择器
下一页: 如何创建框架(主窗口)