限制JavaFX TextField的字符数会导致撤消时出现IndexOutOfBounds
我需要限制用户可以输入到限制JavaFX TextField的字符数会导致撤消时出现IndexOutOfBounds,java,text,javafx-2,javafx-8,indexoutofboundsexception,Java,Text,Javafx 2,Javafx 8,Indexoutofboundsexception,我需要限制用户可以输入到TextFieldJavaFX控件中的字符数。我像这样扩展了TextField public class LengthLimitedTextField extends TextField { /** * @param maxCharacters The max allowed characters that can be entered into this {@link TextField}. */ public LengthLimit
TextField
JavaFX控件中的字符数。我像这样扩展了TextField
public class LengthLimitedTextField extends TextField {
/**
* @param maxCharacters The max allowed characters that can be entered into this {@link TextField}.
*/
public LengthLimitedTextField(final int maxCharacters) {
final TextField thisField = this;
this.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
// Force correct length by deleting the last entered character if text is longer than maxCharacters
if (newValue.length() > maxCharacters) {
thisField.deleteNextChar();
}
}
});
}
}
我不知道如何解决这个问题。一个可能的(但不是理想的)解决方案是完全禁用撤消/重做,但如果不完全覆盖上下文菜单(根据这一点,这并不容易)和默认的键盘快捷键,这似乎是不可能的
因此,归根结底,我的问题有两个:
是否有其他方法可以限制TextField
的字符数,而不在撤消时引发异常?还是有一种干净的方法可以完全禁用应用程序中的撤消
编辑:我做了一些进一步的研究,根据研究结果,这似乎是一个bug。看见那么也许这是不可能实现的呢?我将把这个问题留待讨论,希望有人能找到解决办法 以下是我的做法:
我将使用一个普通的文本字段,并添加一个事件过滤器
设置:
事件处理程序:
public EventHandler maxLength(最终整数i){
返回新的EventHandler(){
@凌驾
公共无效句柄(KeyEvent arg0){
TextField tx=(TextField)arg0.getSource();
if(tx.getText().length()>=i){
arg0.consume();
}
}
};
}
在Magcus代码中添加一点香料
@FXML
private TextField txt_Numeric;
@FXML
private TextField txt_Letters;
@Override
public void initialize(URL url, ResourceBundle rb) {
/* add Event Filter to your TextFields **************************************************/
txt_Numeric.addEventFilter(KeyEvent.KEY_TYPED , numeric_Validation(10));
txt_Letters.addEventFilter(KeyEvent.KEY_TYPED , letter_Validation(10));
}
/* Numeric Validation Limit the characters to maxLengh AND to ONLY DigitS *************************************/
public EventHandler<KeyEvent> numeric_Validation(final Integer max_Lengh) {
return new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {
TextField txt_TextField = (TextField) e.getSource();
if (txt_TextField.getText().length() >= max_Lengh) {
e.consume();
}
if(e.getCharacter().matches("[0-9.]")){
if(txt_TextField.getText().contains(".") && e.getCharacter().matches("[.]")){
e.consume();
}else if(txt_TextField.getText().length() == 0 && e.getCharacter().matches("[.]")){
e.consume();
}
}else{
e.consume();
}
}
};
}
/*****************************************************************************************/
/* Letters Validation Limit the characters to maxLengh AND to ONLY Letters *************************************/
public EventHandler<KeyEvent> letter_Validation(final Integer max_Lengh) {
return new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {
TextField txt_TextField = (TextField) e.getSource();
if (txt_TextField.getText().length() >= max_Lengh) {
e.consume();
}
if(e.getCharacter().matches("[A-Za-z]")){
}else{
e.consume();
}
}
};
}
/*****************************************************************************************/
@FXML
私有文本字段txt_数字;
@FXML
私有文本字段txt_字母;
@凌驾
公共void初始化(URL、ResourceBundle rb){
/*将事件筛选器添加到文本字段**************************************************/
txt_Numeric.addEventFilter(KeyEvent.KEY_类型,数字_验证(10));
txt_Letters.addEventFilter(键入KeyEvent.KEY,字母验证(10));
}
/*数字验证将字符限制为最大长度,且仅限数字*************************************/
公共事件处理程序数字验证(最终整数最大长度){
返回新的EventHandler(){
@凌驾
公共无效句柄(KeyEvent e){
TextField txt_TextField=(TextField)e.getSource();
如果(txt_TextField.getText().length()>=max_Lengh){
e、 消费();
}
如果(e.getCharacter()匹配(“[0-9.]”){
如果(txt_TextField.getText()包含(“.”)和&e.getCharacter()匹配(“[.””){
e、 消费();
}如果(txt_TextField.getText().length()==0&&e.getCharacter().matches(“[.]”),则为else{
e、 消费();
}
}否则{
e、 消费();
}
}
};
}
/*****************************************************************************************/
/*字母验证将字符限制为maxLengh且仅限于字母*************************************/
公共事件处理程序字母验证(最终整数最大长度){
返回新的EventHandler(){
@凌驾
公共无效句柄(KeyEvent e){
TextField txt_TextField=(TextField)e.getSource();
如果(txt_TextField.getText().length()>=max_Lengh){
e、 消费();
}
如果(e.getCharacter()匹配(“[A-Za-z]”){
}否则{
e、 消费();
}
}
};
}
/*****************************************************************************************/
祝你好运^^ 此方法允许TextField完成所有处理(复制/粘贴/撤消安全)。 不要求进行扩展类。 并允许您在每次更改后决定如何处理新文本 (将其推送到逻辑,或返回到上一个值,甚至修改它) 对于您的情况,只需在内部添加此逻辑即可。工作完美
// For example 10 characters
if (newValue.length() >= 10) ((StringProperty)observable).setValue(oldValue);
下面是另一个在JavaFX8上使用Lambda表达式的解决方案
textField.textProperty().addListener(
(observable,oldValue,newValue)-> {
if(newValue.length() > 5) cp.setText(oldValue);
}
);
如果textField长度大于5,则不会插入更多文本。当我键入第一个字符时,我获得NullPointerException'因为tx.getText()为null。可能是tx.getText()!=如果有必要,则第一次签入时为null。此解决方案不会阻止复制和粘贴比我不适用的长度更长的字符串。仍然获得IndexOutOfBoundsException。@MichaelHaefele,从8u40开始,您可能会尝试。是否想出解决方案?通常,在听取属性更改的同时修改属性的状态是个坏主意-您可能会侥幸逃脱(fx属性会处理循环),但仍然可能会有令人讨厌的副作用(撤销错误可能是或不是这样的副作用,没有挖掘)如果您转到jdk8u40及更高版本,则
@FXML
private TextField txt_Numeric;
@FXML
private TextField txt_Letters;
@Override
public void initialize(URL url, ResourceBundle rb) {
/* add Event Filter to your TextFields **************************************************/
txt_Numeric.addEventFilter(KeyEvent.KEY_TYPED , numeric_Validation(10));
txt_Letters.addEventFilter(KeyEvent.KEY_TYPED , letter_Validation(10));
}
/* Numeric Validation Limit the characters to maxLengh AND to ONLY DigitS *************************************/
public EventHandler<KeyEvent> numeric_Validation(final Integer max_Lengh) {
return new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {
TextField txt_TextField = (TextField) e.getSource();
if (txt_TextField.getText().length() >= max_Lengh) {
e.consume();
}
if(e.getCharacter().matches("[0-9.]")){
if(txt_TextField.getText().contains(".") && e.getCharacter().matches("[.]")){
e.consume();
}else if(txt_TextField.getText().length() == 0 && e.getCharacter().matches("[.]")){
e.consume();
}
}else{
e.consume();
}
}
};
}
/*****************************************************************************************/
/* Letters Validation Limit the characters to maxLengh AND to ONLY Letters *************************************/
public EventHandler<KeyEvent> letter_Validation(final Integer max_Lengh) {
return new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {
TextField txt_TextField = (TextField) e.getSource();
if (txt_TextField.getText().length() >= max_Lengh) {
e.consume();
}
if(e.getCharacter().matches("[A-Za-z]")){
}else{
e.consume();
}
}
};
}
/*****************************************************************************************/
// fired by every text property change
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
// Your validation rules, anything you like
// (! note 1 !) make sure that empty string (newValue.equals(""))
// or initial text is always valid
// to prevent inifinity cycle
// do whatever you want with newValue
// If newValue is not valid for your rules
((StringProperty)observable).setValue(oldValue);
// (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
// to anything in your code. TextProperty implementation
// of StringProperty in TextFieldControl
// will throw RuntimeException in this case on setValue(string) call.
// Or catch and handle this exception.
// If you want to change something in text
// When it is valid for you with some changes that can be automated.
// For example change it to upper case
((StringProperty)observable).setValue(newValue.toUpperCase());
}
);
// For example 10 characters
if (newValue.length() >= 10) ((StringProperty)observable).setValue(oldValue);
textField.textProperty().addListener(
(observable,oldValue,newValue)-> {
if(newValue.length() > 5) cp.setText(oldValue);
}
);