Reputation: 2486
Background
I am creating a JavaFX form that requires the user to enter an IP address. Instead of having a single TextField
, my design currently uses four TextField
s, one for each byte of the address. My intention is that once the user has entered three characters in one field, the focus should automatically jump to the next one.
Problem
When the user types the third character, focus remains in the text field. When they then type a fourth character, the character they typed gets placed at the beginning of the field (instead of the end), and then focus changes to the next field. Have I made a mistake in my handler methods? Should I be responding to a different event?
View
<FlowPane fx:id="paneIPAddress" fx:controller="Controller" alignment="CENTER">
<children>
<TextField fx:id="textIP1" alignment="CENTER" onKeyTyped="#ip1Change" prefWidth="40.0" />
<Label text=".">
<font>
<Font name="SansSerif Bold" size="18.0" />
</font>
</Label>
<TextField fx:id="textIP2" alignment="CENTER" onKeyTyped="#ip2Change" prefWidth="40.0" />
<Label text=".">
<font>
<Font name="SansSerif Bold" size="18.0" />
</font>
</Label>
<TextField fx:id="textIP3" alignment="CENTER" onKeyTyped="#ip3Change" prefWidth="40.0" />
<Label text=".">
<font>
<Font name="SansSerif Bold" size="18.0" />
</font>
</Label>
<TextField fx:id="textIP4" alignment="CENTER" prefWidth="40.0" />
</children>
<VBox.margin>
<Insets bottom="25.0" />
</VBox.margin>
</FlowPane>
Controller
public class Controller {
public void ip1Change() {
if (textIP1.getText().length() >= 3) {
textIP2.requestFocus();
}
}
public void ip2Change() {
if (textIP2.getText().length() >= 3) {
textIP3.requestFocus();
}
}
public void ip3Change() {
if (textIP3.getText().length() >= 3) {
textIP4.requestFocus();
}
}
}
Upvotes: 0
Views: 3779
Reputation: 209694
It's probably better to avoid using a key event listener to manage this (what happens if the user uses the mouse to copy and paste text, for example). Instead, register listeners with the text properties of the text fields.
This example doesn't use FXML, but you can do exactly the same thing in your controller's initialize method:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class IPAddressEntry extends Application {
@Override
public void start(Stage primaryStage) {
TextField ipAdd1 = new TextField();
TextField ipAdd2 = new TextField();
TextField ipAdd3 = new TextField();
TextField ipAdd4 = new TextField();
registerListener(ipAdd1, ipAdd2);
registerListener(ipAdd2, ipAdd3);
registerListener(ipAdd3, ipAdd4);
GridPane root = new GridPane();
root.addRow(0, ipAdd1, ipAdd2, ipAdd3, ipAdd4);
Scene scene = new Scene(root, 250, 50);
primaryStage.setScene(scene) ;
primaryStage.show();
}
private void registerListener(TextField tf1, TextField tf2) {
tf1.textProperty().addListener((obs, oldText, newText) -> {
if (oldText.length() < 3 && newText.length() >= 3) {
tf2.requestFocus();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 1