I have an EventHandler that I set as an event filter on TextFields. When I write the class, I get the source TextField by calling getSource() on the event and casting it to a TextField.
The code for the EventHandler:
public class NumberFilter implements EventHandler<KeyEvent> {
public final int maxLength;
public NumberFilter(int maxLength) {
this.maxLength = maxLength;
}
@Override
public void handle(KeyEvent event) {
TextField textField = (TextField) event.getSource(); //<-- is this cast okay?
//consume event if there is too much text or the input isn't a number.
if (textField.getText().length() >= maxLength || !event.getCharacter().matches("[0-9]")) {
event.consume();
}
}
}
Is this cast okay by standard java conventions? How can I write the class so that it can't be used anywhere except as an event filter for a TextField?
Andy Turner's answer provides a robust general approach to allowing event handlers to be added to only one type of Node
. However, for the specific case of vetoing changes to the text in a TextField
(or other text input control), the approach of using key event handlers is not a good one for the following reasons:
KEY_PRESSED
, KEY_RELEASED
, or KEY_TYPED
events? Are you sure the events used internally by the text field will remain the same from one JavaFX release to the next?For completeness, the preferred approach for this particular use case is as follows:
Use a TextFormatter
, which is the supported mechanism for vetoing or modifying text entry to a text input control (as well as providing mechanisms to format or parse text in the control). You can make this reusable by implementing the filter in a standalone class:
public class NumberFilter implements UnaryOperator<TextFormatter.Change> {
private final Pattern pattern ;
public NumberFilter(int maxLength) {
pattern = Pattern.compile("[0-9]{0,"+maxLength+"}");
}
@Override
public TextFormatter.Change apply(TextFormatter.Change c) {
String newText = c.getControlNewText() ;
if (pattern.matcher(newText).matches()) {
return c ;
} else {
return null ;
}
}
}
And now you can do
TextField textField = new TextField();
textField.setTextFormatter(new TextFormatter<String>(new NumberFilter(5)));