带有 TextFormatter 和/或 UnaryOperator 的 JavaFX 8 中整数的数字文本字段 [英] Numeric TextField for Integers in JavaFX 8 with TextFormatter and/or UnaryOperator

查看:28
本文介绍了带有 TextFormatter 和/或 UnaryOperator 的 JavaFX 8 中整数的数字文本字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 JavaFX 8 的 TextFormatter 为整数创建数字文本字段.

I am trying to create a numeric TextField for Integers by using the TextFormatter of JavaFX 8.

使用 UnaryOperator 的解决方案:

UnaryOperator<Change> integerFilter = change -> {
    String input = change.getText();
    if (input.matches("[0-9]*")) { 
        return change;
    }
    return null;
};

myNumericField.setTextFormatter(new TextFormatter<String>(integerFilter));

IntegerStringConverter 的解决方案:

myNumericField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter()));  

两种解决方案都有自己的问题.使用 UnaryOperator,我只能像预期的那样输入从 0 到 9 的数字,但我还需要输入负值,如-512",其中符号只允许在第一个位置.另外我不想要像00016"这样的数字,这仍然是可能的.

Both solutions have their own problems. With the UnaryOperator, I can only enter digits from 0 to 9 like intended, but I also need to enter negative values like "-512", where the sign is only allowed at the first position. Also I don't want numbers like "00016" which is still possible.

IntegerStringConverter 方法效果更好:不接受像-16-123"这样的每个无效数字,并且像0123"这样的数字被转换为123".但转换仅在提交文本(通过按 Enter 键)或 TextField 失去焦点时发生.

The IntegerStringConverter method works way better: Every invalid number like "-16-123" is not accepted and numbers like "0123" get converted to "123". But the conversion only happens when the text is commited (via pressing enter) or when the TextField loses its focus.

有没有办法在每次更新 TextField 的值时使用 IntegerStringConverter 强制执行第二种方法的转换?

Is there a way to enforce the conversion of the second method with the IntegerStringConverter every time the value of the TextField is updated?

推荐答案

转换器与过滤器不同:转换器指定如何将文本转换为值,而过滤器过滤器用户可能做出的更改.在这里听起来您想要两者兼而有之,但您希望过滤器更准确地过滤允许的更改.

The converter is different to the filter: the converter specifies how to convert the text to a value, and the filter filters changes the user may make. It sounds like here you want both, but you want the filter to more accurately filter the changes that are allowed.

如果更改被接受,我通常发现检查文本的新值最容易.您可以选择有一个 -,后跟 1-9 和任意数量的数字.允许空字符串很重要,否则用户将无法删除所有内容.

I usually find it easiest to check the new value of the text if the change were accepted. You want to optionally have a -, followed by 1-9 with any number of digits after it. It's important to allow an empty string, else the user won't be able to delete everything.

所以你可能需要类似的东西

So you probably need something like

UnaryOperator<Change> integerFilter = change -> {
    String newText = change.getControlNewText();
    if (newText.matches("-?([1-9][0-9]*)?")) { 
        return change;
    }
    return null;
};

myNumericField.setTextFormatter(
    new TextFormatter<Integer>(new IntegerStringConverter(), 0, integerFilter));

您甚至可以向过滤器添加更多功能,让它以更智能的方式处理 -,例如

You can even add more functionality to the filter to let it process - in a smarter way, e.g.

UnaryOperator<Change> integerFilter = change -> {
    String newText = change.getControlNewText();
    // if proposed change results in a valid value, return change as-is:
    if (newText.matches("-?([1-9][0-9]*)?")) { 
        return change;
    } else if ("-".equals(change.getText()) ) {

        // if user types or pastes a "-" in middle of current text,
        // toggle sign of value:

        if (change.getControlText().startsWith("-")) {
            // if we currently start with a "-", remove first character:
            change.setText("");
            change.setRange(0, 1);
            // since we're deleting a character instead of adding one,
            // the caret position needs to move back one, instead of 
            // moving forward one, so we modify the proposed change to
            // move the caret two places earlier than the proposed change:
            change.setCaretPosition(change.getCaretPosition()-2);
            change.setAnchor(change.getAnchor()-2);
        } else {
            // otherwise just insert at the beginning of the text:
            change.setRange(0, 0);
        }
        return change ;
    }
    // invalid change, veto it by returning null:
    return null;
};

这将让用户在任何时候按下 - 并切换整数的符号.

This will let the user press - at any point and it will toggle the sign of the integer.

SSCCE:

import java.util.function.UnaryOperator;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.IntegerStringConverter;

public class IntegerFieldExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TextField integerField = new TextField();
        UnaryOperator<Change> integerFilter = change -> {
            String newText = change.getControlNewText();
            if (newText.matches("-?([1-9][0-9]*)?")) { 
                return change;
            } else if ("-".equals(change.getText()) ) {
                if (change.getControlText().startsWith("-")) {
                    change.setText("");
                    change.setRange(0, 1);
                    change.setCaretPosition(change.getCaretPosition()-2);
                    change.setAnchor(change.getAnchor()-2);
                    return change ;
                } else {
                    change.setRange(0, 0);
                    return change ;
                }
            }
            return null;
        };

        // modified version of standard converter that evaluates an empty string 
        // as zero instead of null:
        StringConverter<Integer> converter = new IntegerStringConverter() {
            @Override
            public Integer fromString(String s) {
                if (s.isEmpty()) return 0 ;
                return super.fromString(s);
            }
        };

        TextFormatter<Integer> textFormatter = 
                new TextFormatter<Integer>(converter, 0, integerFilter);
        integerField.setTextFormatter(textFormatter);

        // demo listener:
        textFormatter.valueProperty().addListener((obs, oldValue, newValue) -> System.out.println(newValue));

        VBox root = new VBox(5, integerField, new Button("Click Me"));
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root, 300, 120);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

这篇关于带有 TextFormatter 和/或 UnaryOperator 的 JavaFX 8 中整数的数字文本字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆