2016-11-07 1 views
7

Я пытаюсь создать цифровую TextField для Целые с помощью TextFormatter из JavaFX 8.Числовой TextField для Целые в JavaFX 8 с TextFormatter и/или UnaryOperator

Решение с 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», все еще возможны.

Метод IntegerStringConverter работает лучше: каждое недопустимое число, например «-16-123», не принимается, а цифры типа «0123» преобразуются в «123». Но преобразование происходит только тогда, когда текст выполняется (через нажатие enter) или когда TextField теряет фокус.

Есть ли способ принудительного преобразования второго метода с IntegerStringConverter каждый раз, когда значение TextField обновляется?

ответ

13

Конвертер отличается от фильтра: преобразователь определяет, как преобразовать текст в значение, и фильтры фильтров, которые пользователь может внести. Похоже, вы здесь хотите, но вы хотите, чтобы фильтр более точно отфильтровал разрешенные изменения.

Обычно мне легче проверить новое значение текста, если изменение было принято. Вы хотите дополнительно иметь -, а затем 1-9 с любым количеством цифр после него. Важно разрешить пустую строку, иначе пользователь не сможет удалить все.

Таким образом, вы, вероятно, нужно что-то вроде

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

Вы даже можете добавить больше функциональных возможностей фильтра, чтобы она обрабатывать - более разумным способом, например,

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

Это позволит пользователю нажать - в любой момент, и это будет переключать знак целого числа.

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); 
    } 
} 
+1

Разумное использование '' на группе номер?! Думал о '-? ([1-9]? | [1-9] [0-9] *)', но это намного чище! – flakes

+1

Я очень ценю ваши усилия, ваше решение отлично работает! – ShadowEagle

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

В случае переполнения стека некоторые слова объяснения/инструкции об использовании обычно приветствуются. – mkl

Смежные вопросы