ventanas ventana una secundarias otra nueva multiventanas modal manejo desde cerrar boton abrir java dependency-injection controller javafx fxml

ventana - ¿Cómo inicializar los controladores JavaFX con el mismo objeto modelo?



nueva ventana en javafx (3)

Guión

Estoy creando una GUI donde múltiples vistas hacen referencia al mismo objeto modelo.

A lo que estoy acostumbrado

En Swing, si quiero que todas las vistas hagan referencia al mismo modelo, pasaría el modelo al constructor.

Lo que estoy haciendo actualmente

En JavaFX, paso el modelo teniendo un método setter en las vistas / controladores (barras de menú, paneles divididos, pestañas, ...), después de cargar cada vista / controlador. Encuentro esto muy hortera y engorroso. Además, creo que no funcionará porque en ciertas situaciones necesito que el modelo ya exista en un controlador antes de que se inicialicen algunos de los widgets del controlador.

Alternativas Lackluster

(Nota: me refiero a estas preguntas de stackoverflow:

  • Javafx 2.0 How-to Application.getParameters () en un archivo Controller.java
  • Pasar los parámetros JavaFX FXML
  • Múltiples FXML con controladores, compartir objeto
  • Pasar parámetros a un controlador al cargar un FXML )

  • Inyección de dependencia

    • He visto este sitio web, http://www.zenjava.com/2011/10/23/javafx-2-0-fxml-and-spring/ , y he buscado un poco en google Guice, pero no No veo una forma de simplificar dando a cada vista / controlador JavaFX el mismo objeto modelo. Parecía que la inyección inyectaría un modelo diferente para cada vista / controlador.
  • Guardar el objeto modelo como una variable estática pública

    • Esta es una opción, pero por el momento no me gusta la idea de tener un modelo público estático tan abierto y disponible. Obviamente, puedo convertirlo en una variable estática privada y tener getters y setters, pero tampoco me gusta mucho esta idea.
  • Pasar los parámetros de la persona que llama al controlador

    • Quiero que cada controlador se cargue solo en su constructor, y quiero que cada controlador personalizado se inyecte automáticamente en su controlador principal. Por ejemplo, la pestaña de descripción general de la tarjeta se carga así:

      public CardOverviewTab() { FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("card_overview_tab.fxml")); fxmlLoader.setRoot(content); fxmlLoader.setController(this); try { fxmlLoader.load(); } catch (Exception e) { e.printStackTrace(); } }

    • Y el controlador SingleGameSetup tiene la pestaña de información general de la tarjeta inyectada automáticamente en una variable:

      public class SingleGameSetupController extends AnchorPane { @FXML private CardOverviewTab cardOverviewTab; // Rest of the class }

    • Y la parte del fxml que contiene la pestaña de descripción general de la tarjeta se ve así:

      <CardOverviewTab fx:id="cardOverviewTab" />

    • De esta forma, no tengo que preocuparme por cargar manualmente un controlador, pero todavía tengo el problema de configurar el modelo.

  • Configuración de un controlador en el FXMLLoader

    • Esta opción es similar a la que estoy acostumbrado, pasando el modelo como parámetro al constructor, pero todavía tiene el problema de cargar los controladores manualmente con el FXMLLoader.
  • Autobús de eventos

    • No he leído demasiado sobre esto, pero por lo que he leído, el bus del evento parece estar inactivo y desactualizado.
  • Semifallo

    • Esto es similar a tener una referencia pública estática al objeto modelo que los controladores pueden recuperar, pero de nuevo estoy buscando una solución mejor. Además, no quiero un modelo singleton.

Lo que estoy buscando

¿Hay alguna forma de pasar el objeto modelo de una forma menos engorrosa? Estoy buscando una forma tan simple como pasar el modelo a un constructor, pero no quiero ocuparme de cargar los controladores manualmente a través del FXMLLoader, o configurar el modelo después de que se carguen los controladores. Quizás tener una clase para recuperar el modelo es la mejor opción, pero estoy preguntando en caso de que haya una mejor manera.



He estado investigando este problema y he encontrado que la inyección de dependencia junto con un singleton (para el modelo) es mi diseño óptimo. Aprendí que tener un método setter para el modelo en cada uno de mis controladores es una forma de inyección de dependencia, como pasar el modelo a través del constructor. Spring y Guice son marcos para ayudarlo.

Intenté usar Spring como se indica aquí: http://www.zenjava.com/2011/10/23/better-controller-injection/ pero me encontré con el problema cuando la clase de configuración intentó cargar un controlador, no se pudo cargar los controladores miembros. Esto puede no haber sido un problema de primavera, pero pensé que no me importaba lo suficiente como para dedicarle tiempo a hacerlo funcionar. Además, tendría que revisar todos mis archivos de controlador y editarlos de la forma en que fueron creados.

Después de buscar y leer un montón, descubrí que este artículo expresa mejor lo que me gustaría hacer: https://forums.oracle.com/thread/2301217?tstart=0 . Como el artículo se refiere a mejoras sugeridas, estas mejoras aún no existen en JavaFX. Pero cuando lo hagan, los implementaré. Solo por un ejemplo, ser capaz de inyectar un modelo a través del fxml:

<usevar name="model" type="com.mycom.myapp.ModelObject"/>


Actualizar

Además de afterburner.fx, también se puede descargar Gluon Ignite :

Gluon Ignite permite a los desarrolladores utilizar frameworks populares de inyección de dependencias en sus aplicaciones JavaFX, incluso dentro de sus controladores FXML. Gluon Ignite crea una abstracción común sobre varios frameworks de inyección de dependencias populares (actualmente Guice, Spring y Dagger, pero planeamos agregar más a medida que la demanda se vuelve obvia). Con soporte completo de JSR-330, Gluon Ignite hace que el uso de la inyección de dependencias en aplicaciones JavaFX sea trivial.

La inyección de objetos modelo en los controladores también se realiza a través de @Inject, similar a afterburner.fx.

Enfoque sugerido

Como parece que busca un marco de inyección de dependencia, creo que su mejor opción es utilizar el marco afterburner.fx .

afterburner.fx proporciona una manera de insertar objetos de modelo en los controladores JavaFX utilizando la anotación Java @Inject estándar.

Sistemas alternativos de inyección de dependencia

Spring es grande y complicado y, a menos que necesite muchas otras funcionalidades para su aplicación, no se debe considerar debido a su complejidad.

Guice es mucho más simple que Spring y una opción razonable si necesita un marco de inyección de dependencia con numerosas características, como clases de proveedores. Pero por lo que parece, no necesita todas las características que proporciona Guice, ya que solo quiere una forma de pasar alrededor de instancias únicas de objetos en su aplicación sin buscarlos explícitamente.

Por lo tanto, pruebe afterburner.fx y vea si se ajusta a sus necesidades.

afterburner.fx Código de muestra

Aquí hay una muestra de NotesStore inyectar una instancia de modelo ( NotesStore ) en un controlador usando afterburner.fx. La muestra se copia directamente de la documentación de afterburner.fx .

import com.airhacks.afterburner.views.FXMLView; public class NoteListView extends FXMLView { //usually nothing to do, FXML and CSS are automatically //loaded and instantiated } public class AirpadPresenter implements Initializable { @Inject // injected by afterburner, zero configuration required NotesStore store; @FXML // injected by FXML AnchorPane noteList; @Override public void initialize(URL url, ResourceBundle rb) { //view constructed from FXML NoteListView noteListView = new NoteListView(); //fetching and integrating the view from FXML Parent view = noteListView.getView(); this.noteList.getChildren().add(view); } }

followme.fx es una aplicación de muestra básica que demuestra cómo usar afterburner.fx. Tuve algunas dificultades para que followme.fx se ejecutara directamente debido a las incompatibilidades de dependencia de Maven, así que guardé su código y solucioné algunos de los problemas que me impedían usarlo de la caja.

Respuestas a las preguntas de adición de los comentarios

Entonces, del ejemplo de NoteStore, ¿estás diciendo que todo lo que tengo que hacer es agregar la dependencia del marco de postcombustión y poner @Inject en mi variable de modelo?

No, también necesita crear una clase asociada que amplíe FXMLView e instanciar eso con una nueva llamada (similar a cómo se crea NotesListView en el código de ejemplo anterior). Si le interesa continuar investigando el marco de trabajo afterburner.fx, entonces use el proyecto followme.fx como base porque proporciona un código fuente completo para una muestra ejecutable muy simple que utiliza el marco.

Intenté google guice y lo hice funcionar. . . Verás en el constructor que un objeto de configuración del juego se inyecta manualmente.

No creo que debas usar el inyector Guice manualmente así. Creo que puede configurar una fábrica de controladores en una instancia de FXMLLoader para iniciar la inyección. Así es como lo hace el FXMLView en FXMLView . El detalle exacto del mecanismo de inyección utilizado en Guice diferirá del mecanismo afterburner.fx, pero creo que el concepto general de configuración de la fábrica de controladores sigue siendo similar.

Hay una demostración de la fábrica de controles establecida utilizando FXML y Guice en la respuesta a: Asociación de FXML y Controlador en la configuración del Módulo de Guice .

Es una pena que no haya una manera más directa de hacer esto que no te cause tantas dificultades.

Como una nota lateral inconsecuente personal, soy un poco ambivalente en el tema de los marcos de inyección de dependencia en general. Claro, pueden ayudar, pero muchas veces para cosas simples a menudo estoy bien con un singleton con un método getInstance en lugar del marco más sofisticado. Todavía veo cómo en proyectos más grandes pueden ser útiles y ciertamente son muy populares en ciertos frameworks Java.