Каков рекомендуемый способ сделать числовое TextField в JavaFX? - PullRequest
74 голосов
/ 26 сентября 2011

Мне нужно ограничить ввод в TextField целыми числами. Любой совет?

Ответы [ 22 ]

96 голосов
/ 12 июня 2015

Очень старая тема, но она выглядит аккуратнее и удаляет нечисловые символы при вставке.

// force the field to be numeric only
textField.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue, 
        String newValue) {
        if (!newValue.matches("\\d*")) {
            textField.setText(newValue.replaceAll("[^\\d]", ""));
        }
    }
});
38 голосов
/ 23 сентября 2013

Я знаю, что это довольно старая тема, но для будущих читателей здесь есть другое решение, которое я нашел довольно интуитивно понятным:

public class NumberTextField extends TextField
{

    @Override
    public void replaceText(int start, int end, String text)
    {
        if (validate(text))
        {
            super.replaceText(start, end, text);
        }
    }

    @Override
    public void replaceSelection(String text)
    {
        if (validate(text))
        {
            super.replaceSelection(text);
        }
    }

    private boolean validate(String text)
    {
        return text.matches("[0-9]*");
    }
}

Редактировать: Спасибо none_ и SCBoy за предложенные улучшения.

33 голосов
/ 12 октября 2012

Обновление апрель 2016

Этот ответ был создан несколько лет назад, и в настоящее время первоначальный ответ в значительной степени устарел.

Начиная с Java 8u40, Java имеет TextFormatter , который обычно лучше всего подходит для принудительного ввода определенных форматов, таких как числа, в JavaFX TextFields:

См. Также другие ответы на этот вопрос, в которых конкретно упоминается TextFormatter.


Оригинальный ответ

Есть несколько примеров этого в этой сущности , я продублировал один из примеров ниже:

// helper text field subclass which restricts text input to a given range of natural int numbers
// and exposes the current numeric int value of the edit box as a value property.
class IntField extends TextField {
  final private IntegerProperty value;
  final private int minValue;
  final private int maxValue;

  // expose an integer value property for the text field.
  public int  getValue()                 { return value.getValue(); }
  public void setValue(int newValue)     { value.setValue(newValue); }
  public IntegerProperty valueProperty() { return value; }

  IntField(int minValue, int maxValue, int initialValue) {
    if (minValue > maxValue) 
      throw new IllegalArgumentException(
        "IntField min value " + minValue + " greater than max value " + maxValue
      );
    if (maxValue < minValue) 
      throw new IllegalArgumentException(
        "IntField max value " + minValue + " less than min value " + maxValue
      );
    if (!((minValue <= initialValue) && (initialValue <= maxValue))) 
      throw new IllegalArgumentException(
        "IntField initialValue " + initialValue + " not between " + minValue + " and " + maxValue
      );

    // initialize the field values.
    this.minValue = minValue;
    this.maxValue = maxValue;
    value = new SimpleIntegerProperty(initialValue);
    setText(initialValue + "");

    final IntField intField = this;

    // make sure the value property is clamped to the required range
    // and update the field's text to be in sync with the value.
    value.addListener(new ChangeListener<Number>() {
      @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
        if (newValue == null) {
          intField.setText("");
        } else {
          if (newValue.intValue() < intField.minValue) {
            value.setValue(intField.minValue);
            return;
          }

          if (newValue.intValue() > intField.maxValue) {
            value.setValue(intField.maxValue);
            return;
          }

          if (newValue.intValue() == 0 && (textProperty().get() == null || "".equals(textProperty().get()))) {
            // no action required, text property is already blank, we don't need to set it to 0.
          } else {
            intField.setText(newValue.toString());
          }
        }
      }
    });

    // restrict key input to numerals.
    this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
      @Override public void handle(KeyEvent keyEvent) {
        if (!"0123456789".contains(keyEvent.getCharacter())) {
          keyEvent.consume();
        }
      }
    });

    // ensure any entered values lie inside the required range.
    this.textProperty().addListener(new ChangeListener<String>() {
      @Override public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
        if (newValue == null || "".equals(newValue)) {
          value.setValue(0);
          return;
        }

        final int intValue = Integer.parseInt(newValue);

        if (intField.minValue > intValue || intValue > intField.maxValue) {
          textProperty().setValue(oldValue);
        }

        value.set(Integer.parseInt(textProperty().get()));
      }
    });
  }
}
31 голосов
/ 05 апреля 2016

Начиная с JavaFX 8u40, вы можете установить объект TextFormatter в текстовое поле:

UnaryOperator<Change> filter = change -> {
    String text = change.getText();

    if (text.matches("[0-9]*")) {
        return change;
    }

    return null;
};
TextFormatter<String> textFormatter = new TextFormatter<>(filter);
fieldNport = new TextField();
fieldNport.setTextFormatter(textFormatter);

Это позволяет избежать как наследования, так и дублирования событий изменения, которые вы получите, когда добавите прослушиватель изменений в свойство text и измените текст в этом прослушивателе.

26 голосов
/ 20 апреля 2016

TextInput имеет TextFormatter, который можно использовать для форматирования, преобразования и ограничения типов текста, который можно вводить.

TextFormatter имеет фильтр, который можно использовать для отклонения ввода. Нам нужно установить это, чтобы отклонить все, что не является допустимым целым числом. Он также имеет конвертер, который нам нужно установить для преобразования строкового значения в целочисленное значение, которое мы можем связать позже.

Позволяет создать фильтр многократного использования:

public class IntegerFilter implements UnaryOperator<TextFormatter.Change> {
    private final static Pattern DIGIT_PATTERN = Pattern.compile("\\d*");

    @Override
    public Change apply(TextFormatter.Change aT) {
        return DIGIT_PATTERN.matcher(aT.getText()).matches() ? aT : null;
    }
}

Фильтр может выполнять одно из трех действий: он может вернуть изменение без изменений, чтобы принять его таким, какой он есть, он может изменить изменение некоторым способом, который он сочтет нужным, или он может вернуть null, чтобы полностью отклонить изменение.

Мы будем использовать стандарт IntegerStringConverter в качестве конвертера.

Собрав все вместе, мы имеем:

TextField textField = ...;

TextFormatter<Integer> formatter = new TextFormatter<>(
    new IntegerStringConverter(), // Standard converter form JavaFX
    defaultValue, 
    new IntegerFilter());
formatter.valueProperty().bindBidirectional(myIntegerProperty);

textField.setTextFormatter(formatter);

Если вы не нуждаетесь в многоразовом фильтре, вы можете вместо этого сделать этот модный однострочный:

TextFormatter<Integer> formatter = new TextFormatter<>(
    new IntegerStringConverter(), 
    defaultValue,  
    c -> Pattern.matches("\\d*", c.getText()) ? c : null );
21 голосов
/ 30 сентября 2013

Мне не нравятся исключения, поэтому я использовал функцию matches из String-Class

text.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue, 
        String newValue) {
        if (newValue.matches("\\d*")) {
            int value = Integer.parseInt(newValue);
        } else {
            text.setText(oldValue);
        }
    }
});
7 голосов
/ 24 марта 2017

Начиная с Java SE 8u40 , для такой необходимости вы можете использовать " integer " Spinner, позволяющий безопасно выбрать действительное целое число с помощьюклавиши со стрелками вверх / вниз или со стрелками вверх / вниз.

Вы также можете определить мин , макс и начальное значение, чтобы ограничить допустимые значения и величину для увеличения или уменьшения на, за шаг.

Например,

// Creates an integer spinner with 1 as min, 10 as max and 2 as initial value
Spinner<Integer> spinner1 = new Spinner<>(1, 10, 2);
// Creates an integer spinner with 0 as min, 100 as max and 10 as initial 
// value and 10 as amount to increment or decrement by, per step
Spinner<Integer> spinner2 = new Spinner<>(0, 100, 10, 10);

Пример результата со счетчиком " integer " и счетчиком " double "

enter image description here

A spinner - это однострочный элемент управления текстовым полем, который позволяет пользователю выбрать число или значение объекта изупорядоченная последовательность таких значений.Вращатели обычно предоставляют пару крошечных кнопок со стрелками для перехода по элементам последовательности.Клавиши со стрелками вверх / вниз на клавиатуре также переключают элементы.Пользователю также может быть разрешено вводить (легальное) значение непосредственно в счетчик.Хотя комбинированные блоки предоставляют аналогичную функциональность, блесны иногда предпочтительнее, поскольку они не требуют раскрывающегося списка, который может скрыть важные данные, а также потому, что они позволяют такие функции, как перенос из максимального значения обратно в минимальное значение (например,от наибольшего положительного целого до 0).

Подробнее о элемент управления Spinner

6 голосов
/ 14 марта 2017

Предпочитаемый ответ может быть еще меньше, если вы используете Java 1.8 Lambdas

textfield.textProperty().addListener((observable, oldValue, newValue) -> {
    if (newValue.matches("\\d*")) return;
    textfield.setText(newValue.replaceAll("[^\\d]", ""));
});
6 голосов
/ 14 июня 2012
TextField text = new TextField();

text.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable,
            String oldValue, String newValue) {
        try {
            Integer.parseInt(newValue);
            if (newValue.endsWith("f") || newValue.endsWith("d")) {
                manualPriceInput.setText(newValue.substring(0, newValue.length()-1));
            }
        } catch (ParseException e) {
            text.setText(oldValue);
        }
    }
});

Предложение if важно для обработки входных данных, таких как 0.5d или 0.7f, которые правильно анализируются Int.parseInt (), но не должны появляться в текстовом поле.

4 голосов
/ 26 ноября 2016

Попробуйте этот простой код, он сделает работу.

DecimalFormat format = new DecimalFormat( "#.0" );
TextField field = new TextField();
field.setTextFormatter( new TextFormatter<>(c ->
{
    if ( c.getControlNewText().isEmpty() )
    {
        return c;
    }

    ParsePosition parsePosition = new ParsePosition( 0 );
    Object object = format.parse( c.getControlNewText(), parsePosition );

    if ( object == null || parsePosition.getIndex() <          c.getControlNewText().length() )
    {
        return null;
    }
    else
    {
        return c;
    }
}));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...