Javafx – ¿Puede la clase de aplicación ser la clase de controlador

Actualmente me estoy enseñando a mí mismo JavaFX, y he tomado un progtwig de ejemplo simple que está codificado en la vista y lo estoy convirtiendo en uno que usa FXML (sobre todo para poder usar SceneBuilder para comstackr UI). En lugar de escribir una clase de controlador por separado, 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). La vista aparece, pero luego la aplicación @FXML TableView table errores ya que ninguno de los controles se asigna a las variables apropiadas (por lo tanto, para la @FXML TableView table , la table es null ).

Sin embargo, puse en un método initialize() para la depuración, los controles se inyectan en initialize() , y luego vuelven a null cuando initialize() sale.

Entonces, la pregunta es, ¿Java instancia instancia una nueva instancia de la clase de aplicación como una clase de controlador separada? Esto explicaría por qué la variable está fuera del scope. ¿O se trata de otra cosa (por ejemplo, los controles se inyectan solo cuando se los llama desde acciones de JavaFX)?

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, ejecute una excepción; de lo contrario, cree una instancia de la clase 1 especificada y configúrelo como controlador.
  • 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. De manera similar, registre los manejadores 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 Application como la clase controladora usando fx:controller , se crea una segunda instancia de la subclase Application , se inyectan campos @FXML -annotated en esa segunda instancia y se invoca el método initialize() en ese segundo ejemplo. Obviamente, los @FXML @FXML nunca se inicializan en la instancia en la que se invoca start(...) y nunca se invoca el método initialize() en esa instancia.

La pregunta que probablemente quiso decir es:

¿Se puede usar la instancia de la clase de aplicación creada en el lanzamiento como controlador?

La respuesta a esto también es afirmativa, y, aparte de los progtwigs de demostración muy pequeños que tiene la intención de descartar de inmediato, también es probable que sea una muy mala idea. Lo harías 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 alguna parte, termina con una segunda instancia de subclase de Application , que es, en el mejor de los casos, contradictoria (una aplicación con dos instancias de Application …)).

Entonces recomendaría usar una clase separada para el controlador en básicamente cada caso. La subclase Application debe contener un código mínimo y solo debe usarse 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 una 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 (llamando efectivamente a su constructor sin argumentos).

Si ha definido su clase de aplicación para ser el controlador en el archivo FXML, JavaFX, si no recuerdo mal, creará una nueva instancia de su clase de aplicación y usará 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 progtwigció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();