it-swarm.com.ru

Контроллер 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 для инициализации требуемых контроллером вещей?

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

59
mrbela

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

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

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

79
Nikos Paraskevopoulos

Метод 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. 
    }
}
70
Itai

В дополнение к приведенным выше ответам, вероятно, следует отметить, что существует лучший способ осуществить инициализацию. Есть интерфейс 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 (), определенный контроллером. Рекомендуется использовать инъекционный подход всякий раз, когда это возможно.

2
moneydhaze