Issue
I'm trying to make a Sudoku game in JavaFX but I can't figure out how to only allow one letter to be entered. Would the answer to this to be to call the textfield and do:
myTextField.setOnKeyPressed(e ->
{
if (!myTextField.getText().length().isEmpty())
{
// Somehow reject the key press?
}
}
The above way won't work with copy pasting... or tons of other things, etc. Using key press listeners like this seems like an AWFUL idea. There must be something better? Is there a property of text fields to allow only certain characters to be inputted, or only allow certain number of characters to be inputted?
Thank you!
Solution
You can use a TextFormatter
to do this. The TextFormatter
can modify the changes that are made to the text in the text field if it has a filter associated with it. The filter is a function that takes a TextFormatter.Change
object and returns an object of the same type. It can return null
to veto the change entirely, or modify it.
So you can do
TextField textField = new TextField();
textField.setTextFormatter(new TextFormatter<String>((Change change) -> {
String newText = change.getControlNewText();
if (newText.length() > 1) {
return null ;
} else {
return change ;
}
});
Note though that the TextFormatter
can also be used to convert the text to a value of any type you like. In your case, it would make sense to convert the text to an Integer
, and also to only allow integer input. As a final addition to the user experience, you can modify the change so that if the user types a digit, it replaces the current content (instead of ignoring it if there are too many characters). The whole thing would look like this:
TextField textField = new TextField();
// converter that converts text to Integers, and vice-versa:
StringConverter<Integer> stringConverter = new StringConverter<Integer>() {
@Override
public String toString(Integer object) {
if (object == null || object.intValue() == 0) {
return "";
}
return object.toString() ;
}
@Override
public Integer fromString(String string) {
if (string == null || string.isEmpty()) {
return 0 ;
}
return Integer.parseInt(string);
}
};
// filter only allows digits, and ensures only one digit the text field:
UnaryOperator<Change> textFilter = c -> {
// if text is a single digit, replace current text with it:
if (c.getText().matches("[1-9]")) {
c.setRange(0, textField.getText().length());
return c ;
} else
// if not adding any text (delete or selection change), accept as is
if (c.getText().isEmpty()) {
return c ;
}
// otherwise veto change
return null ;
};
TextFormatter<Integer> formatter = new TextFormatter<Integer>(stringConverter, 0, textFilter);
formatter.valueProperty().addListener((obs, oldValue, newValue) -> {
// whatever you need to do here when the actual value changes:
int old = oldValue.intValue();
int updated = newValue.intValue();
System.out.println("Value changed from " + old + " to " + updated);
});
textField.setTextFormatter(formatter);
Answered By - James_D