ventana - Javafx: ¿puede la clase de aplicación ser la clase de controlador?
javafx ventanas secundarias (2)
El comportamiento predeterminado de
FXMLLoader
es crear una nueva instancia de la clase de controlador y usar esa instancia como controlador.
Específicamente,
FXMLLoader
hace algo como:
-
Lea el elemento raíz FXML.
-
Si el elemento raíz FXML tiene un atributo
fx:controller
, entonces- Si ya existe un controlador, inicie una excepción; de lo contrario, cree una instancia de la clase 1 especificada y configúrela como controlador
-
Si el elemento raíz FXML tiene un atributo
-
Continúa analizando el archivo FXML.
Si los elementos tienen un atributo
fx:id
y existe un controlador (por cualquier mecanismo), inyecte esos campos en el controlador. Del mismo modo, registre los controladores de eventos como llamadas a métodos en la instancia del controlador. -
Invoque
initialize()
en el controlador, si existe un controlador y tiene dicho método.
Entonces, la pregunta que hiciste:
¿Puede la clase de aplicación ser la clase de controlador?
Sí, pero probablemente sea una idea terrible.
Si simplemente especifica la subclase de
Application
como la clase de controlador utilizando
fx:controller
, se crea una segunda instancia de la subclase de
Application
, se inyectan campos
@FXML
@FXML en esa segunda instancia y se invoca el método
initialize()
en esa segunda ejemplo.
Obviamente, los
@FXML
@FXML nunca se inicializan en la instancia en la que se invoca
start(...)
, y el método
initialize()
nunca se invoca en esa instancia.
La pregunta que probablemente quisiste decir es:
¿Se puede usar la instancia de clase de aplicación creada en el lanzamiento como controlador?
La respuesta a esto también es sí, y, aparte de los programas de demostración muy pequeños que tiene la intención de descartar de inmediato, también es probablemente una muy mala idea. Harías esto por
public class MyApp extends Application {
@FXML
private Node someNode ;
public void initialize() {
// do something with someNode
}
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file.fxml"));
loader.setController(this);
Parent root = loader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Tenga en cuenta que para usar este código, su archivo FXML
no debe
tener un atributo
fx:controller
.
El problema con esto es que no tienes separación ni flexibilidad.
(Por ejemplo, si crea una segunda instancia de la vista definida en su archivo FXML en algún lugar, terminará con una segunda instancia de subclase de
Application
, que en el mejor de los casos es contraintuitiva (una aplicación con dos instancias de
Application
...)).
Así que recomendaría usar una clase separada para el controlador básicamente en todos los casos.
La subclase de la
Application
debe contener un código mínimo y debe usarse solo para iniciar la aplicación.
1
Este paso es en realidad un poco más complejo.
Si se especifica una clase en el atributo
fx:controller
y ya no existe ningún controlador,
FXMLLoader
busca un
controllerFactory
.
Si existe, el controlador se establece como resultado de pasar la
Class
especificada al método
call()
controllerFactory
; de lo contrario, se crea llamando a
newInstance()
en la clase especificada (efectivamente llamando a su constructor sin argumentos).
Actualmente me estoy enseñando JavaFX, y he tomado un programa de ejemplo simple que codifica la vista y lo estoy convirtiendo en uno que usa FXML (principalmente para poder usar SceneBuilder para construir interfaces de usuario).
En lugar de escribir una clase de controlador separada, estoy usando la clase de aplicación (por lo que hay 1 archivo Java y 1 archivo FXML).
No estoy usando un método
initialize()
ya que es un flujo lineal (muestra la interfaz de usuario, llena los campos, espera la entrada).
Aparece la vista, pero luego la aplicación falla, ya que ninguno de los controles está asignado a las variables apropiadas (por lo que para la
@FXML TableView<...> table
, la
table
es
null
).
Sin embargo, puse un método
initialize()
para la depuración, los controles se inyectan durante
initialize()
, y luego vuelvo a nulo cuando sale
initialize()
.
Entonces la pregunta es, ¿JavaFX crea una instancia nueva de la clase de aplicación como una clase de controlador separada? Esto explicaría por qué las variables están fuera de alcance. ¿O es algo más (por ejemplo, los controles se inyectan solo cuando se les devuelve la llamada desde acciones JavaFX)?
Si ha definido que su clase de aplicación sea el controlador en el archivo FXML, JavaFX, si no recuerdo mal, creará una nueva instancia de su clase de aplicación y utilizará la nueva instancia como controlador. Por lo tanto, su clase de aplicación existente todavía tiene nulo para la tabla.
Sin embargo, puede definir el controlador mediante programación en su clase de aplicación para usar su propia instancia:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("example.fxml"));
fxmlLoader.setController(this);
Parent root = (Parent)fxmlLoader.load();