javafx:文本控件如何控制用户输入文本的格式
TextField里面有一个setTextFormatter()
方法,需要传入javafx.scene.control.TextFormatter<V>
类型
TextFormatter
这个类通过使用两种不同的机制来描述文本框中文本的格式。对这两个机制学习之后我们可以做到控制、转化用户输入的文本。
-
过滤器机制:可以拦截、修改用户的文本输入,不符合预设要求则可以拦截字符不予返回。
由
UnaryOperator<TextFormatter.Change>
函数式接口提供支持
-
值转换器机制:由
StringConverter<V>
接口提供支持。该接口有两个方法:public String toString(V value) public T fromString(String s)
转换器定义字符串和对象之间的转换行为。
接下来是对这两个机制的详细介绍
UnaryOperator接口与TextFormatter过滤器
该接口只有一个方法,用户每次在文本框输入一个字符,TextFormatter都会调用该方法并将TextFormatter.Change对象作为参数传入
TextField textField = new Textfield();
textField.setTextFormatter(new TextFormatter<String>(new UnaryOperator<TextFormatter.Change>() {
@Override
public TextFormatter.Change apply(TextFormatter.Change change) {
System.out.println(change.getText());
return null;
}
}));
输入12345,控制台中,你看到的会类似于下面这样的输出。每输入一个字符都会产生一个Change对象
1
2
3
4
5
这里apply
方法返回了null,实际运行之后会发现,输入任意字符,控制台能看见输入的字符,但是文本框没有任何显示。如果我们将null改为传入的change
变量,那么文本就能正常显示了。这再次启示我们:我们完全可以借此过滤非法字符,只要在这个方法里进行if判断,非法字符则返回null就可以了。
使用lambda表达式重写会变得比较简洁
textField.setTextFormatter(new TextFormatter<String>(change -> {
System.out.println(change.getText());
return change;
}));
Change还有许多比较有意思的方法。除了返回每次文本框输入的新值getText()外,还有getControlNewText()方法,返回的是当前时刻控件里面所有的文本。有兴趣可以看文档
另外要注意的是,仍依次输入12345,我们有可能在控制台看见这样的输出
1
2
3
4
5
每一次输入新的字符,UnaryOperator接口竟然被调用了三次?!这个锅由中文输入法背。在中文输入法下,尤其是企图往框里输入中文,过滤器将会被很奇怪地调用。
StringConverter <T>值转换器与TextFormatter
先来看看TextFormatter的四个构造方法:
TextFormatter(StringConverter<V> valueConverter)
TextFormatter(StringConverter<V> valueConverter, V defaultValue)
TextFormatter(StringConverter<V> valueConverter, V defaultValue, UnaryOperator<TextFormatter.Change> filter)
TextFormatter(UnaryOperator<TextFormatter.Change> filter)
第四个很熟悉:其实就是使用指定的过滤器创建新的TextFormatter对象。
另外三个都与StringConverter 值转换器有关。
public StringConverter<V>() {
public String toString(V value)
public T fromString(String s)
}
在toString()
和fromString
方法中,我们需要自定义一种类型的对象和文本框的文本(String)的对应关系。toString
方法将对象以某种法则变成文本框里面的字符串(如果第一次启动程序,这将成为预设文本),fromString
方法则根据用户的输入进行某些操作,并返回该类型的对象。
有几种时刻会调用这两个方法:
- 窗口打开时,这个时候无论焦点在哪个控件,
toString
方法都会被调用。 - 点击文本框(焦点移至文本框)时,
toString
方法会被调用。然后再点击其他控件(焦点从文本框离开时),fromString
方法将会被调用。 - 在选中文本框的情况下按下Enter键会调用
fromString
方法,这并不影响焦点离开文本框后调用fromString
方法
当焦点再次移向这个文本框时,会发现,toString
方法会再次被调用,传入的对象则是上一次fromString
返回的对象。这是因为TextFormatter在私有域中保存了这个对象,以ObjectProperty<V>
的形式。事实上,toString
方法传入的也是这个对象。第二个第三个构造方法的V defaultValue
,实际上是给这个域设置了一个初始值。
如果我们写一个从String到String的映射呢?
TextField textField = new Textfield();
textField.setTextFormatter(new TextFormatter<>(new StringConverter<String>() {
@Override
public String toString(String s) {
System.out.println("start:" + s);
if (s == null) {
return "Sorry, I am Null.";
}
return s;
}
@Override
public String fromString(String s) {
System.out.println("finish:" +s);
return "I will come back.";
}
}));
来回移动焦点,控制台输出为
start:null
start:null
finish:Sorry, I am Null.
start:I will come back.
start:I will come back.
finish:I will come back.
start:I will come back.
finish:I will come back.
而且,无论用户输入了什么,焦点离开这个文本框的时候,都会重置回这句话“I will come back.”