【问题标题】:How to implement a NumberField in javaFX 2.0?如何在 javaFX 2.0 中实现 NumberField?
【发布时间】:2012-01-12 23:03:35
【问题描述】:

我需要在我的 UI 中插入一个数字字段。所以我需要检查文本字段上的关键事件,以检查输入字符是否为数字。我通过扩展 TextField 创建了一个类。如果 TextField 类中有处理 keyEvents 的方法,我可以简单地用适合数字字段的方法覆盖该方法。有什么想法吗?

谢谢

【问题讨论】:

  • 请发布您的解决方案作为您问题的答案并将其标记为已接受!

标签: user-interface input javafx-2


【解决方案1】:

FXExperience 上有一个提示可以处理类似的问题。 换句话说,您扩展 TextField 并覆盖 replaceText()replaceSelection() 方法,过滤所有不是数字的输入。

实施后,这两种方法都应遵循此模板:

if (!newText.matches("[0-9]")) {
    super.call(allParams)
}

【讨论】:

  • 但是我也确认TextField 无法避免将受限值粘贴到其中,如该教程中所述。
【解决方案2】:

2016 年 5 月 27 日更新

Java 8u40 引入了 TextFormatter 类,这是完成此功能的推荐方法(尽管此答案中提供的解决方案仍然有效)。有关详细信息,请参阅Uwe's answerHassan's answer 和其他在以下问题中提到 TextFormatter 的答案:

这个问题的另一个答案也有这个解决方案,我没有尝试过,但看起来不错,并且删除了 StackOverflow 版主:

TextField numberField = new TextField();
numberField.setTextFormatter(new TextFormatter<>(new NumberStringConverter()));

上面的代码错过了 TextFormatter 的 UnaryOperator 过滤器,您通常也需要该过滤器(否则,该字段将不会显示,将用户输入限制为仅格式化的值,它只允许您通过文本监视未格式化的值格式化程序值属性)。要将解决方案扩展为使用过滤器,可以使用如下代码:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;

import java.text.ParsePosition;
import java.util.function.UnaryOperator;

public class NumberConverterFieldTest extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        TextField numberField = new TextField();
        NumberStringFilteredConverter converter = new NumberStringFilteredConverter();
        final TextFormatter<Number> formatter = new TextFormatter<>(
                converter,
                0,
                converter.getFilter()
        );

        numberField.setTextFormatter(formatter);

        formatter.valueProperty().addListener((observable, oldValue, newValue) ->
                System.out.println(newValue)
        );

        stage.setScene(new Scene(numberField));
        stage.show();
    }

    class NumberStringFilteredConverter extends NumberStringConverter {
        // Note, if needed you can add in appropriate constructors 
        // here to set locale, pattern matching or an explicit
        // type of NumberFormat.
        // 
        // For more information on format control, see 
        //    the NumberStringConverter constructors
        //    DecimalFormat class 
        //    NumberFormat static methods for examples.
        // This solution can instead extend other NumberStringConverters if needed
        //    e.g. CurrencyStringConverter or PercentageStringConverter.

        public UnaryOperator<TextFormatter.Change> getFilter() {
            return change -> {
                String newText = change.getControlNewText();
                if (newText.isEmpty()) {
                    return change;
                }

                ParsePosition parsePosition = new ParsePosition( 0 );
                Object object = getNumberFormat().parse( newText, parsePosition );
                if ( object == null || parsePosition.getIndex() < newText.length()) {
                    return null;
                } else {
                    return change;
                }
            };
        }
    }
}

运行上述示例时,编辑输入字段并按回车键查看更新的值(更新后的值在更改时输出到System.out)。

有关教程,请参阅:


这与 Urs 引用的解决方案相同,但是我只是将它放在一个完全可执行的程序中以提供上下文中的示例并修改正则表达式(通过在末尾添加 *)以便复制和粘贴工作和它没有 Uluk 提到的问题。该解决方案似乎很简单,可能足以满足大多数目的:

import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

public class NumericTextFieldTest extends Application {
  public static void main(String[] args) { launch(args); }

  @Override public void start(Stage stage) {
    TextField numberField = new TextField() {
      @Override public void replaceText(int start, int end, String text) {
        if (text.matches("[0-9]*")) {
          super.replaceText(start, end, text);
        }
      }

      @Override public void replaceSelection(String text) {
        if (text.matches("[0-9]*")) {
          super.replaceSelection(text);
        }
      }
    };

    stage.setScene(new Scene(numberField));
    stage.show();
  }
}

替代解决方案

您可能还对我在JavaFX example of binding a slider value to a editable TextField 中的替代解决方案感兴趣。在该解决方案中,我扩展 TextField 以在字段上公开一个 IntegerProperty 以用于简单的绑定目的。替代解决方案类似于原始发布者在他们更新的问题中概述的解决方案(即添加了一个事件过滤器以限制来自关键事件的输入数据),但另外在 TextField 的文本属性上添加了一个 ChangeListener 以确保复制和粘贴的值只有当它们是数字时才被接受。

JavaFX 论坛线程Numeric Textfield in JavaFX 2.0? 中还有一些其他解决方案,其中包括对number fields from FXExperience controls 的引用。

【讨论】:

  • 这应该作为组件在 javafx 中提供
  • @pdem Java 8u40 引入了 TextFormatter,它提供了一种获得这种功能的通用方法,尽管它的文档并没有那么好,而且它也不仅仅针对数字字段。但是,是的,如果在 JavaFX 库核心(现在不太可能已经有 TextFormatter)或第三方扩展库(如 ControlsFX)中有一个开箱即用的组件来实现此类功能,那就太好了。
  • @jewelsea NumberStringConverter 对我不起作用。我建议尝试一下。
  • @Kachna 感谢您提供信息,这有点棘手。最初呈现的 NumberStringConverter 仅用作格式化程序而不是过滤器,因此它可以正确提取输出值,但不会改变输入值的显示或限制用户可以键入的内容(这是大多数人想要的),所以我更新了答案以包含一个带有 TextFormatter 和 NumberStringConverter 的过滤器示例。
【解决方案3】:

这是我编写的 CustomText 字段。 它处理仅数字输入和最大大小。 它是一个可以在 FXML 中使用的自定义控件,并且可以在 FXMl 本身中设置属性。

package fxml;

import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.IntegerPropertyBase;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.TextField;

public class CustomTextField extends TextField {

/**
 * numericOnly property if set, will allow accept only numeric input.
 */
private BooleanProperty numericOnly = new SimpleBooleanProperty(this,
        "numericOnly", false);

public final boolean isNumericOnly() {
    return numericOnly.getValue();
}

public final void setNumericOnly(boolean value) {
    numericOnly.setValue(value);
}

public final BooleanProperty numericOnlyProperty() {
    return numericOnly;
}

/**
 * maxSize property , determines the maximum size of the text that can be
 * input.
 */
public IntegerProperty maxSize = new IntegerPropertyBase(1000) {

    @Override
    public String getName() {
        return "maxSize";
    }

    @Override
    public Object getBean() {
        return CustomTextField.this;
    }
};

public final IntegerProperty maxSizeProperty() {
    return maxSize;
};

public final int getMaxSize() {
    return maxSize.getValue();
}

public final void setMaxSize(int value) {
    maxSize.setValue(value);
}

/**
 * this method is called when user inputs text into the textField
 */
@Override
public void replaceText(int start, int end, String text) {
    if (numericOnly.getValue() && !text.equals("")) {
        if (!text.matches("[0-9]")) {
            return;
        }
    }
    if (getText().length() < getMaxSize() || text.equals("")) {
        super.replaceText(start, end, text);
    }
}

/**
 * this method is called when user pastes text into the textField
 */
@Override
public void replaceSelection(String text) {
    if (numericOnly.getValue() && !text.equals("")) {
        if (!text.matches("[0-9]+")) {
            return;
        }
    }
    super.replaceSelection(text);
    if (getText().length() > getMaxSize()) {
        String maxSubString = getText().substring(0, getMaxSize());
        setText(maxSubString);
        positionCaret(getMaxSize());
    }
}

}

【讨论】:

    【解决方案4】:

    继承自TextField 并照此覆盖replaceText,以获取仅TextField 的Double 值:

    @Override
    public void replaceText(int start, int end, String text) {
        String preText = getText(0, start);
        String afterText = getText(end, getLength());
        String toBeEnteredText = preText + text + afterText;
    
        // Treat the case where the user inputs proper text and is not inputting backspaces or other control characters
        // which would be represented by an empty text argument:
        if (!text.isEmpty() && text.matches("\\d|\\.")) {
            Logger.getAnonymousLogger().info("Paring non-empty.");
            try {
                Logger.getAnonymousLogger().info("Parsing " + toBeEnteredText);
                Double.parseDouble(toBeEnteredText);
                super.replaceText(start, end, text);
            } catch (Exception ignored) {
            }
        }
    
        // If the user used backspace or del, the result text is impossible to not parse as a Double/Integer so just
        // enter that text right ahead:
        if (text.isEmpty()) {
            super.replaceText(start, end, text);
        }
    }
    

    【讨论】:

      【解决方案5】:

      找到了解决办法。 :)

      public class NumFieldFX extends TextField {
         public NumFieldFX() {
            this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
               public void handle( KeyEvent t ) {
                  char ar[] = t.getCharacter().toCharArray();
                  char ch = ar[t.getCharacter().toCharArray().length - 1];
                  if (!(ch >= '0' && ch <= '9')) {
                     System.out.println("The char you entered is not a number");
                     t.consume();
                  }
               }
            });
         }
      }
      

      【讨论】:

        【解决方案6】:

        对于多个TextFiled(包括小数点)

        Arrays.asList(txtLongitude, txtLatitude, txtAltitude, txtSpeed, txtOrientation).forEach(textField ->
                    textField.textProperty().addListener((observable, oldValue, newValue) ->
                            textField.setText(newValue.matches("^[0-9]*\\.?[0-9]*$") ? newValue : oldValue)
                    ));
        

        【讨论】:

          【解决方案7】:

          结合百机飞龙和AJAY PRAKASH解决方案,支持十进制输入

          package com.mazeworks.cloudhms.view.components;
          
          import javafx.beans.property.BooleanProperty;
          import javafx.beans.property.IntegerProperty;
          import javafx.beans.property.IntegerPropertyBase;
          import javafx.beans.property.SimpleBooleanProperty;
          import javafx.scene.control.TextField;
          
          public class NumericTextField extends TextField {
          
              /**
               * numericOnly property if set, will allow accept only numeric input.
               */
              private BooleanProperty numericOnly = new SimpleBooleanProperty(this,
                      "numericOnly", false);
          
              public final boolean isNumericOnly() {
                  return numericOnly.getValue();
              }
          
              public final void setNumericOnly(boolean value) {
                  numericOnly.setValue(value);
              }
          
              public final BooleanProperty numericOnlyProperty() {
                  return numericOnly;
              }
          
              /**
               * maxSize property, determines the maximum size of the text that 
               can be
               * input.
               */
              public IntegerProperty maxSize = new IntegerPropertyBase(1000) {
          
                  @Override
                  public String getName() {
                      return "maxSize";
                  }
          
                  @Override
                  public Object getBean() {
                      return NumericTextField.this;
                  }
              };
          
              public final IntegerProperty maxSizeProperty() {
                  return maxSize;
              }
          
              ;
          
              public final int getMaxSize() {
                  return maxSize.getValue();
              }
          
              public final void setMaxSize(int value) {
                  maxSize.setValue(value);
              }
          
              /**
               * this method is called when user inputs text into the textField
               */
              @Override
              public void replaceText(int start, int end, String text) {
                  if (numericOnly.getValue() && !text.equals("")) {
                      if (!text.matches("^[0-9]*\\.?[0-9]*$")) {
                          return;
                      }
                  }
                  if (getText().length() < getMaxSize() || text.equals("")) {
                      super.replaceText(start, end, text);
                  }
              }
          
              /**
               * this method is called when user pastes text into the textField
               */
              @Override
              public void replaceSelection(String text) {
                  if (numericOnly.getValue() && !text.equals("")) {
                      if (!text.matches("^[0-9]*\\.?[0-9]*$")) {
                          return;
                      }
                  }
                  super.replaceSelection(text);
                  if (getText().length() > getMaxSize()) {
                      String maxSubString = getText().substring(0, getMaxSize());
                      setText(maxSubString);
                      positionCaret(getMaxSize());
                  }
              }
          }
          

          【讨论】:

            【解决方案8】:

            应用以下方式可以使 TextField 变为 NumberField 但这样它不会立即过滤键盘输入...但是如果您将注意力集中在该字段上,那么您将看到结果。您可以将其用于基本的数字输入字段验证。我在这里举一些例子......它可能会帮助某人:)

            // It will show the number with comma ie. 64,568,455
            TextField numberField = new TextField();
            numberField.setTextFormatter(new TextFormatter<>(new NumberStringConverter()));
            
            // It will show the integer number ie. 64568455
            TextField integerField = new TextField();
            integerField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter()));
            
            // It will ensure the float format ie. 2635.0
            TextField floatField = new TextField();
            floatField.setTextFormatter(new TextFormatter<>(new FloatStringConverter()));
            

            【讨论】:

              猜你喜欢
              • 2014-01-06
              • 1970-01-01
              • 2013-03-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-05-28
              • 2018-03-21
              相关资源
              最近更新 更多