如何在 FXML 加载后立即请求关注文本字段?

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

我正在使用 FXML 创建 JavaFX 应用程序。在这个应用程序中,我有一个按钮可以在舞台的一部分中加载一个 FXML 场景,就像加载一个 FXML 文件作为另一个 FXML 场景的子场景一样。我想请求将焦点放在一个文本字段上,这样我就可以在子场景加载后立即键入内容,而无需使用鼠标手动单击文本字段。我尝试在控制器 class 的 initialize() 方法中使用 textField.requestFocus(),但没有任何反应。我读到是因为当方法是运行时,文本字段还不可见,所以它不能有焦点。在添加新场景后,我还尝试在按钮的操作处理程序上使用 textField.requestFocus(),但为此我必须将 TextField public 和静态设置为在控制器外部访问 class。这种方法似乎部分起作用,因为文本字段的提示文本消失了,但我仍然无法在不单击鼠标的情况下在其中键入内容。之后,我尝试将 ChangeListener 添加到文本字段的 visibleProperty 并使用:

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

但这也没有任何作用。 有什么想法可以实现吗?

主场景代码:

    //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"); }
     }
}

带文本框的场景控制器代码

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

StartScene 的 FXML:

<?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>

带有 TextField 的场景的 FXML:

<?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>

你有几个问题。

首先,您在实际需要 component 之前加载它。这意味着 initialize() 方法在 BorderPane.

中实际显示 component 之前很久就已被调用并完成 运行

相反,您应该仅在单击按钮后加载 FXML 文档(将加载移至您的 loadComponent() 方法)。

然后,您可以将此添加到 TextFieldSceneController class 的 initialize() 方法中:

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

调用 Platform.runLater() 将安排您的 TextField 获得焦点 "at some unspecified time in the future." 在这种情况下,它会在 Scene 加载和呈现后执行。

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

我为视图控制器使用一个公共超类(它是我编写的框架的一部分,用于使用 IoC 容器以声明方式加载和注入 view/controller 元组到其他托管 bean 中,但这不是解决此问题所必需的问题)。

在 ViewController 超类中,我为子类创建了生命周期回调方法以覆盖超类实现调用的生命周期回调方法,以响应观察到的视图属性的变化。这是相关位...

public abstract class ViewController<V extends Parent> {

  private final ReadOnlyObjectWrapper<V> view = new ReadOnlyObjectWrapper<>(this, "view");

  protected ViewController() {
    viewProperty().addListener((observable, oldValue, newValue) -> {
      if (newValue != null) {
        viewDidLoad(newValue);
      }
    });
  }

  public ReadOnlyObjectProperty<V> viewProperty() {
    return view.getReadOnlyProperty();
  }

  void setView(V view) {
    this.view.set(view);
  }

  public V getView() {
    return viewProperty().get();
  }

  protected void viewDidChangeScene(Scene oldScene, Scene newScene) {}

  protected void viewDidLoad(V view) {
    view.sceneProperty().addListener((o, oldScene, newScene) -> this.viewDidChangeScene(oldScene, newScene));
  }

}

在我想确保文本字段在加载时获得焦点的子类中,我添加了...

@Override
protected void viewDidChangeScene(Scene oldScene, Scene newScene) {
  usernameField.requestFocus();
}