[input] JavaFX에서 숫자 TextField를 만드는 데 권장되는 방법은 무엇입니까?

TextField에 대한 입력을 정수로 제한해야합니다. 어떤 충고?



답변

아주 오래된 스레드이지만 붙여 넣으면 더 깔끔해 보이고 숫자가 아닌 문자를 제거합니다.

// 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]", ""));
        }
    }
});


답변

2016 년 4 월 업데이트

이 답변은 몇 년 전에 생성되었으며 원래 답변은 현재 거의 쓸모가 없습니다.

Java 8u40부터 Java에는 일반적으로 JavaFX TextFields에 숫자와 같은 특정 형식의 입력을 적용하는 데 가장 적합한 TextFormatter 가 있습니다.

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(intField.minValue<0) {
                if (!"-0123456789".contains(keyEvent.getCharacter())) {
                    keyEvent.consume();
                }
            }
            else {
                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) || (intField.minValue<0 && "-".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()));
      }
    });
  }
}


답변

나는 이것이 다소 오래된 스레드라는 것을 알고 있지만 미래의 독자를 위해 여기에 매우 직관적 인 또 다른 솔루션이 있습니다.

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 에게 감사드립니다 .


답변

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 속성에 변경 리스너를 추가하고 해당 리스너에서 텍스트를 수정할 때 발생하는 하위 클래스 지정 및 중복 변경 이벤트를 모두 방지 할 수 있습니다.


답변

TextInputA가 들어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 );


답변

나는 예외를 좋아하지 않아서 matchesString-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);
        }
    }
});


답변

Java SE 8u40 부터 이러한 요구에 대해 “ 정수 “를Spinner 를 사용하여 키보드의 위쪽 화살표 / 아래쪽 화살표 키 또는 위쪽 화살표 / 아래쪽 화살표 제공 버튼을 사용하여 유효한 정수를 안전하게 선택할 수 있습니다.

min , a max를 정의 할 수도 있습니다.초기 값을 하여 허용되는 값과 단계 당 증가 또는 감소 할 양을 제한 .

예를 들면

// 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 “스피너 가있는 결과의 예

여기에 이미지 설명 입력

스피너 사용자는 값들의 순서화 된 시퀀스에서 다수의 또는 객체를 선택할 수 있도록 한 줄의 텍스트 필드 제어이다. 스피너는 일반적으로 시퀀스 요소를 단계별로 이동할 수있는 한 쌍의 작은 화살표 버튼을 제공합니다. 키보드의 위쪽 화살표 / 아래쪽 화살표 키도 요소를 순환합니다. 사용자는 스피너에 직접 (법적) 값을 입력 할 수도 있습니다. 콤보 상자가 유사한 기능을 제공하지만 스피너는 중요한 데이터를 가릴 수있는 드롭 다운 목록이 필요하지 않고 최대 값에서 최소값으로 다시 래핑하는 등의 기능을 허용하기 때문에 선호되는 경우가 있습니다 (예 : 가장 큰 양의 정수에서 0까지).

Spinner 컨트롤 에 대한 자세한 정보