Search code examples
javafxfocustextfieldfxmlsetfocus

How can I request focus on a text field as soon as FXML loads?


I am creating a JavaFX application using FXML. In this application I have a button that loads a FXML scene in a portion of the Stage, like loading a FXML file as a child of another FXML scene. I want to request focus on a text field so I can type as soon as the child scene loads, without having to manually click the text field with the mouse. I tried using textField.requestFocus() in the initialize() method of my controller class, but nothing happens. I read it is because the text field is not yet visible when the method is run, so it cannot have focus. I also tried using textField.requestFocus() on the button's action handler, after the new scene is added, but for this I had to make the TextField public and static to be accessed outside the controller class. This approach seemed to partially work as the textfield's prompt text disappeared, but I still cannot type inside it without clicking with the mouse. After that, I tried adding a ChangeListener to the textfield's visibleProperty and used:

if(textField.isVisible()) textField.requestFocus();

But this also did nothing. Any ideas how I can achieve this?

Code for the main scene:

    //The controller class for the FXML containing the button that is pressed to go to the FXML with the TextField
public class StartScene {
    @FXML
    BorderPane contentPane; //Where the other fxml file will be loaded
    private String path = "../fxml/fxmlFile.fxml";
    private static Parent component = null;
    @FXML
    private void initialize() throws IOException {

        component = FXMLLoader.load(getClass().getResource(path));
    }
    @FXML
    //This method is set to the onAction of a button
    private void loadComponent(){
        if (!contentPane.getChildren().contains(component)) {
            contentPane.getChildren().clear();
            contentPane.setCenter(component);
        }
        else { System.out.println("Component already inserted"); }
     }
}

Code for the controller of scene with textfield

public class TextFieldSceneController {
    @FXML
    TextField textField;
    @FXML
    private void initialize() {
        textField.setPromptText("Prompt");
    }
}

FXML for StartScene:

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.StackPane?>

<StackPane minHeight="500" minWidth="900" xmlns:fx="http://javafx.com/fxml"
       fx:controller="StartScene" >
    <BorderPane fx:id="borderPane">
        <left>
            <VBox minHeight="500" prefWidth="250" minWidth="10">
                    <Button text="BUTTON" minHeight="30" minWidth="100" prefWidth="250"  alignment="CENTER" onAction="#loadComponent"/>
             </VBox>
        </left>
        <center>
            <BorderPane minWidth="600" minHeight="500" fx:id="contentPane">
                 <children>
                 </children>
            </BorderPane>
        </center>
    </BorderPane>
</StackPane>

FXML for Scene with TextField:

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> 

<StackPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
       fx:controller="TextFieldSceneController" prefHeight="600.0" maxWidth="800.0" fx:id="root">
    <VBox fx:id="base">
        <children>
            <TextField fx:id="textField" alignment="CENTER"/>
        </children>
    </VBox>
</StackPane>

Solution

  • You have a couple of issues.

    First of all, you are loading your component before you actually need it. This means that the initialize() method has been called and finished running long before you actually show the component in your BorderPane.

    Instead, you should load the FXML document only once the button has been clicked (move the loading to your loadComponent() method).

    Then, you may add this to your initialize() method of your TextFieldSceneController class:

    Platform.runLater(() -> textField.requestFocus());

    Calling Platform.runLater() will schedule your TextField to gain focus "at some unspecified time in the future." In this case, it would execute after the Scene has been loaded and rendered.

    You can learn more about Platform.runLater() by reading the JavaFX documentation.