Reputation: 51
I need to remember about 3 pages of text verbatim, including punctuation. I'm attempting to build a simple JavaFX program to quiz myself on this material.
I'd like to use a text area for the user to type input. The user is to type into the text area - if the input correctly matches the existing string (the stuff I need to remember), the text is allowed as input into the text field. If they make a mistake, the input simply isn't recognized and no incorrect input will appear in the text area.
What's the best approach for something like this? I'm pretty new to JavaFX and Java in general. I've got my UI set up in JavaFX, I'm just unsure how to implement the check on the text area. Thank you.
Upvotes: 0
Views: 453
Reputation: 46255
One option is to use a TextFormatter
that only allows the new text if it resembles the expected text. Here's a proof-of-concept:
import java.util.Objects;
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
TextArea area = new TextArea();
area.setPrefRowCount(5);
area.setPrefColumnCount(25);
area.setTextFormatter(new TextFormatter<>(new ExpectedTextFilter("Hello, World!")));
StackPane root = new StackPane(area);
root.setPadding(new Insets(10));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private static class ExpectedTextFilter implements UnaryOperator<Change> {
private final String expectedText;
ExpectedTextFilter(String expectedText) {
this.expectedText = Objects.requireNonNull(expectedText);
}
@Override
public Change apply(Change change) {
if (change.isContentChange()) {
if (change.isReplaced()) {
// simply don't allow replacements
return null;
} else if (change.isDeleted()) {
// only allow deletions from the end of the control's text
return change.getRangeEnd() == change.getControlText().length() ? change : null;
} else {
return expectedText.startsWith(change.getText(), change.getRangeStart()) ? change : null;
}
}
return change;
}
}
}
The above uses this constructor of TextFormatter
which accepts a UnaryOperator
. This operator, known as the "filter", can intercept changes to the text input control and either allow the change as is, alter it, or reject it altogether:
The filter itself is an
UnaryOperator
that acceptsTextFormatter.Change
object. It should return aTextFormatter.Change
object that contains the actual (filtered) change. Returning null rejects the change [emphasis added].
Upvotes: 3