web-dev-qa-db-fra.com

Champ de texte numérique pour les entiers dans JavaFX 8 avec TextFormatter et/ou UnaryOperator

J'essaye de créer un TextField numérique pour les entiers en utilisant le TextFormatter de JavaFX 8.

Solution avec UnaryOperator:

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

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

Solution avec IntegerStringConverter:

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

Les deux solutions ont leurs propres problèmes. Avec UnaryOperator, je ne peux saisir que les chiffres compris entre 0 et 9, mais je dois également entrer des valeurs négatives, telles que "-512", où le signe n'est autorisé qu'à la première position. Aussi, je ne veux pas de chiffres comme "00016", ce qui est encore possible.

La méthode IntegerStringConverter fonctionne mieux: chaque nombre non valide tel que "-16-123" n'est pas accepté et les nombres tels que "0123" sont convertis en "123". Mais la conversion ne se produit que lorsque le texte est validé (en appuyant sur Entrée) ou lorsque TextField perd son focus.

Existe-t-il un moyen d'imposer la conversion de la deuxième méthode avec IntegerStringConverter chaque fois que la valeur de TextField est mise à jour?

8
ShadowEagle

Le convertisseur est différent du filtre: le convertisseur spécifie comment convertir le texte en une valeur et les filtres de filtres que l'utilisateur peut modifier. Cela ressemble ici à ce que vous souhaitiez les deux, mais vous souhaitez que le filtre filtre plus précisément les modifications autorisées.

Je trouve généralement plus facile de vérifier la nouvelle valeur du texte si le changement a été accepté. Vous souhaitez éventuellement avoir un -, suivi de 1-9 avec un nombre quelconque de chiffres après celui-ci. Il est important d'autoriser une chaîne vide, sinon l'utilisateur ne pourra pas tout supprimer.

Donc vous avez probablement besoin de quelque chose comme

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

Vous pouvez même ajouter plus de fonctionnalités au filtre pour lui permettre de traiter - de manière plus intelligente, par exemple.

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

Cela permettra à l'utilisateur d'appuyer sur - à tout moment et changera le signe de l'entier. 

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);
    }
}
14
James_D
 TextField txtpoint = new TextField();
    txtpoint.textProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
            if (!newValue.isEmpty()) {
                try {
                    long pointI = Integer.parseInt(newValue);
                    txtpoint.setText(String.valueOf(pointI));
                } catch (Exception e) {
                    txtpoint.clear();
                    txtpoint.setText(getNumber(oldValue));
                }
            }
        }
    });


private String getNumber(String value) {
    String n = "";
    try {
        return String.valueOf(Integer.parseInt(value));
    } catch (Exception e) {
        String[] array = value.split("");
        for (String tab : array) {
            try {
                System.out.println(tab);
                n = n.concat(String.valueOf(Integer.parseInt(String.valueOf(tab))));
            } catch (Exception ex) {
                System.out.println("not nomber");
            }
        }
        return n;
    }
}
0
user8498734