Kontroler JavaFX FXML - konstruktor vs metoda inicjalizacji

89

Moja Applicationklasa wygląda tak:

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

FXMLLoaderPowoduje wystąpienie odpowiedniego regulatora (podano w FXMLpliku za pośrednictwem fx:controller) poprzez wywoływanie pierwszy konstruktora domyślne, a następnie initializemetodę:

public class MainViewController {

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

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

Wynik to:

first
second

Dlaczego więc initializeistnieje metoda? Jaka jest różnica między używaniem konstruktora lub initializemetody inicjowania wymaganych rzeczy kontrolera?

Dzięki za sugestie!

mrbela
źródło

Odpowiedzi:

129

W kilku słowach: najpierw wywoływany jest konstruktor, następnie @FXMLwypełniane są wszystkie pola z adnotacjami, a następnie initialize()wywoływany.

To oznacza, że konstruktor ma nie mieć dostępu do @FXMLpól odnoszących się do elementów zdefiniowanych w pliku .fxml, a initialize() nie mają do nich dostępu.

Cytat z wprowadzenia do FXML :

[...] kontroler może zdefiniować metodę initialize (), która zostanie wywołana raz na kontrolerze implementującym, gdy zawartość powiązanego z nim dokumentu zostanie całkowicie załadowana [...] Pozwala to klasie implementującej na wykonanie dowolnego niezbędnego wpisu -przetwarzanie treści.

Nikos Paraskevopoulos
źródło
2
Nie rozumiem. Sposób, w jaki to robi, jest zakończony FXMLLoader, prawda? Więc nie widzę korzyści z czekania na initialize()metodę -. Gdy tylko FXML zostanie załadowany, następujący kod ma dostęp do @FXMLzmiennych. Jasne, robi to w metodzie startowej a nie w konstruktorze, ale czy initialize()przyniesie to jakąś korzyść w jego przypadku?
Codepleb
93

initializeMetoda jest wywoływana po wszystkie @FXMLadnotacją członkowie zostali wstrzykiwany. Załóżmy, że masz widok tabeli, który chcesz wypełnić danymi:

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. 
    }
}
Itai
źródło
11

Oprócz powyższych odpowiedzi należy prawdopodobnie zauważyć, że istnieje starsza metoda realizacji inicjalizacji. Istnieje interfejs o nazwie Initializable z biblioteki 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());
    }
}

Parametry:

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. 

I uwaga w dokumentach, dlaczego prosty sposób użycia @FXML public void initialize()działa:

NOTETen interfejs został zastąpiony przez automatyczne wstrzykiwanie właściwości lokalizacji i zasobów do kontrolera. FXMLLoader będzie teraz automatycznie wywoływał dowolną metodę inicjalizacji no-arg initialize () z odpowiednimi adnotacjami zdefiniowaną przez kontroler. Zaleca się, aby w miarę możliwości stosować metodę iniekcji.

gkhaos
źródło