Подтвердить что ты не робот

Контроллер JavaFX FXML - конструктор против метода инициализации

Мой класс Application выглядит следующим образом:

public class Test extends Application {

    private static Logger logger = LogManager.getRootLogger();

    @Override
    public void start(Stage primaryStage) throws Exception {

        String resourcePath = "/resources/fxml/MainView.fxml";
        URL location = getClass().getResource(resourcePath);
        FXMLLoader fxmlLoader = new FXMLLoader(location);

        Scene scene = new Scene(fxmlLoader.load(), 500, 500);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

FXMLLoader создает экземпляр соответствующего контроллера (указанный в файле FXML через fx:controller), сначала вызывая конструктор по умолчанию, а затем метод initialize:

public class MainViewController {

    public MainViewController() {
        System.out.println("first");
    }

    @FXML
    public void initialize() {
        System.out.println("second");
    }
}

Вывод:

first
second

Итак, почему существует метод initialize? В чем разница между использованием конструктора или метода initialize для инициализации требуемых элементов контроллера?

Спасибо за ваши предложения!

4b9b3361

Ответ 1

В нескольких словах: сначала вызывается конструктор, затем заносятся любые аннотированные поля @FXML, затем вызывается initialize(). Поэтому конструктор НЕ имеет доступа к полям @FXML, относящимся к компонентам, определенным в файле .fxml, тогда как initialize() имеет к ним доступ.

Цитата из Введение в FXML:

[...] контроллер может определить метод initialize(), который будет вызываться один раз на контроллере реализации, когда содержимое его связанного документа полностью загружено [...] Это позволяет реализовать класс реализации любая необходимая пост-обработка содержимого.

Ответ 2

Метод initialize вызывается после того, как все @FXML добавлены аннотированные члены. Предположим, у вас есть табличный вид, который вы хотите заполнить данными:

class MyController { 
    @FXML
    TableView<MyModel> tableView; 

    public MyController() {
        tableView.getItems().addAll(getDataFromSource()); // results in NullPointerException, as tableView is null at this point. 
    }

    @FXML
    public void initialize() {
        tableView.getItems().addAll(getDataFromSource()); // Perfectly Ok here, as FXMLLoader already populated all @FXML annotated members. 
    }
}

Ответ 3

В дополнение к приведенным выше ответам, вероятно, следует отметить, что существует традиционный способ осуществления инициализации. Есть интерфейс Initializable из библиотеки fxml.

import javafx.fxml.Initializable;

class MyController implements Initializable {
    @FXML private TableView<MyModel> tableView;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        tableView.getItems().addAll(getDataFromSource());
    }
}

Параметры:

location - The location used to resolve relative paths for the root object, or null if the location is not known.
resources - The resources used to localize the root object, or null if the root object was not localized. 

И примечание к документам о том, почему простой способ использования @FXML public void initialize() работает:

NOTE Этот интерфейс был заменен автоматическим введением свойств местоположения и ресурсов в контроллер. FXMLLoader теперь будет автоматически вызывать любой соответствующим образом аннотированный метод no-arg initialize(), определенный контроллером. Рекомендуется использовать инъекционный подход всякий раз, когда это возможно.