JavaFX: ¿cómo obtener la etapa del controlador durante la inicialización?
initialization javafx-2 (5)
Quiero manejar eventos de escenario (es decir, ocultos) de mi clase de controlador. Entonces, todo lo que tengo que hacer es agregar un oyente a través de
((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);
pero el problema es que la inicialización comienza justo después
Parent root = FXMLLoader.load(getClass().getResource("MyGui.fxml"));
y antes
Scene scene = new Scene(root);
stage.setScene(scene);
por lo tanto .getScene () devuelve null.
La única solución que encontré por mi cuenta es agregar un oyente a myPane.sceneProperty (), y cuando no es nulo, obtengo una escena, agrego a ella .windowProperty () my! Goddamn! manejo del oyente que finalmente recupero el escenario. Y todo termina con la configuración de oyentes deseados para escenificar eventos. Creo que hay demasiados oyentes. ¿Es la única manera de resolver mi problema?
La forma más sencilla de obtener el objeto de etapa en el controlador es:
Agregue un método adicional en la propia clase de controlador creado (será un método setter para establecer el escenario en la clase de controlador),
private Stage myStage; public void setStage(Stage stage) { myStage = stage; }
Obtener el controlador en el método de inicio y establecer el escenario
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml")); OwnController controller = loader.getController(); controller.setStage(this.stage);
Ahora puedes acceder al escenario en el controlador
Puede obtener con node.getScene
, si no llama desde Platform.runLater
, el resultado es un valor nulo.
ejemplo valor nulo:
node.getScene();
ejemplo, no hay valor nulo:
Platform.runLater(() -> {
node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
//your event
});
});
Puede obtener la instancia del controlador desde el FXMLLoader después de la inicialización a través de getController()
, pero luego debe instanciar un FXMLLoader en lugar de usar los métodos estáticos.
Pasaría el escenario después de llamar a load()
directamente al controlador después:
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyGui.fxml"));
Parent root = (Parent)loader.load();
MyController controller = (MyController)loader.getController();
controller.setStageAndSetupListeners(stage); // or what you want to do
Sé que no es la respuesta que desea, pero IMO las soluciones propuestas no son buenas (y su propia manera es). ¿Por qué? Porque dependen del estado de la aplicación. En JavaFX, un control, una escena y un escenario no dependen el uno del otro. Esto significa que un control puede vivir sin ser agregado a una escena y una escena puede existir sin estar conectada a un escenario. Y luego, en un instante de tiempo t1, el control puede unirse a una escena y en el instante t2, esa escena se puede agregar a una etapa (y eso explica por qué son propiedades observables entre sí).
Entonces, el enfoque que sugiere obtener la referencia del controlador e invocar un método, pasando el escenario a él agrega un estado a su aplicación. Esto significa que debe invocar ese método en el momento adecuado, justo después de que se haya creado la etapa. En otras palabras, debe seguir un pedido ahora: 1- Crear la etapa 2- Pasar esta etapa creada al controlador a través de un método.
No puede (o no debería) cambiar este orden en este enfoque. Entonces perdiste la apatridia. Y en el software, en general, el estado es malo. Idealmente, los métodos no deberían requerir ningún orden de llamada.
Entonces, ¿cuál es la solución correcta? Hay dos alternativas:
1- Su enfoque, en las propiedades de escucha del controlador para obtener el escenario. Creo que este es el enfoque correcto. Me gusta esto:
pane.sceneProperty().addListener((observableScene, oldScene, newScene) -> {
if (oldScene == null && newScene != null) {
// scene is set for the first time. Now its the time to listen stage changes.
newScene.windowProperty().addListener((observableWindow, oldWindow, newWindow) -> {
if (oldWindow == null && newWindow != null) {
// stage is set. now is the right time to do whatever we need to the stage in the controller.
((Stage) newWindow).maximizedProperty().addListener((a, b, c) -> {
if (c) {
System.out.println("I am maximized!");
}
});
}
});
}
});
2- Hace lo que necesita hacer donde crea el Stage
(y eso no es lo que quiere):
Stage stage = new Stage();
stage.maximizedProperty().addListener((a, b, c) -> {
if (c) {
System.out.println("I am maximized!");
}
});
stage.setScene(someScene);
...
Todo lo que necesita es darle una ID a AnchorPane
, y luego puede obtener el Stage
de eso.
@FXML private AnchorPane ap;
Stage stage = (Stage) ap.getScene().getWindow();
Desde aquí, puede agregar al Listener
que necesita.
Editar: como se indica por EarthMind a continuación, no tiene que ser el elemento AnchorPane
; puede ser cualquier elemento que hayas definido.