且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Initialize方法在Main类中的指定方法之前运行

更新时间:2023-11-18 16:37:40


似乎initialize方法在setRowData方法之前运行。

It seems that the initialize-method runs BEFORE the setRowData-method.

这是确实正确。作为执行 load的一部分, initialize()方法由 FXMLLoader 调用( ),必须在您有机会调用 setRowData(...)之前发生。

This is indeed correct. The initialize() method is called by the FXMLLoader as part of the execution of load(), which necessarily happens before you have a chance to call setRowData(...).


我在这里错过了什么?

What am I missing out on here?

没什么,真的。

有几种可能的修复方法。一个是简单地避免让 initialize()方法依赖于传递给 setRowData()的数据,并移动任何依赖于这些数据到后一种方法的代码。在这种情况下,这非常简单:

There are a couple of possible fixes. One is simply to avoid having the initialize() method depend on the data you pass to setRowData(), and move any code that does depend on those data to the latter method. This is pretty trivial in this case:

public void setRowData(Trip rowData) {
    this.rowData = rowData;
    populateTravelPlan();
}

@Override
public void initialize(URL arg0, ResourceBundle arg1) {
    addTraveler.setDisable(true);
    removeTraveler.setDisable(true);
    // don't do this here:
    // populateTravelPlan();
}

如果您不喜欢该解决方案,另一个选择是设置代码中的控制器而不是FXML中的控制器。简而言之,您将从FXML文件中删除 fx:controller 属性,然后在加载FXML时,创建一个控制器实例,调用 setRowData( )(或以其他方式初始化行数据,例如通过构造函数),并在 FXMLLoader setController() / code>。 传递参数JavaFX FXML 中详细介绍了此解决方案。

If you don't like that solution, one other option is to set the controller in code instead of in the FXML. Briefly, you would remove the fx:controller attribute from the FXML file, and then when you load the FXML, create a controller instance, call setRowData() (or otherwise initialize the row data, e.g. via a constructor), and call setController() on the FXMLLoader. This solution is described in detail in Passing Parameters JavaFX FXML.

一个该解决方案的缺点是,如果将Scene Builder用于FXML设计,Scene Builder将失去一些功能(因为它不再知道用于创建控制器的类)。如果你想保留这个功能,可以使用控制器工厂。

One disadvantage of that solution is that if you use Scene Builder for the FXML design, Scene Builder will lose some functionality (as it's no longer aware of the class used to create the controller). If you want to keep that functionality, you can use a controller factory.

对于那个解决方案,我会修改控制器,以便将行数据传递给构造函数:

For that solution, I would modify the controller so that the row data were passed to a constructor:

public class BookViewController implements Initializable {

    // @FXML-annotated fields omitted...

    private final Trip rowData;

    public BookViewController(Trip rowData) {
        this.rowData = rowData ;
    }

    // remove this, it is now initialized in the constructor
    // public void setRowData(Trip rowData) {
    //     this.rowData = rowData;
    //     // The below println is never printed to the console..
    //     System.out.println("Test");
    // }

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        addTraveler.setDisable(true);
        removeTraveler.setDisable(true);
        populateTravelPlan();
    }

    // etc...
}

此构造函数将阻止 FXMLLoader 使用其默认方法创建控制器实例,因此将控制器工厂提供给加载程序以覆盖控制器的创建方式:

This constructor will prevent the FXMLLoader from creating a controller instance using its default method, so supply a controller factory to the loader to override how the controller is created:

public static void showBookView(Trip rowData) throws IOException {
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(Main.class.getResource("BookView.fxml"));
    loader.setControllerFactory(type -> {
        if (type == BookViewController.class) {
            return new BookViewController(rowData);
        }
        // default behavior: need this in case there are <fx:include> in the FXML
        try {
            return type.getConstructor().newInstance();
        } catch (Exception exc) {
            // fatal...
            throw new RuntimeException(exc);
        }
    });
    BorderPane bookTrip = loader.load();

    Stage bookStage = new Stage();                
    bookStage.setTitle("BookingApp");
    bookStage.initModality(Modality.WINDOW_MODAL);
    bookStage.initOwner(primaryStage);
    Scene scene = new Scene(bookTrip);
    bookStage.setScene(scene);
    bookStage.showAndWait();
}