Pasar parámetros JavaFX FXML


¿Cómo puedo pasar parámetros a una ventana secundaria en javafx? ¿Hay alguna forma de comunicarse con el controlador correspondiente?

Por ejemplo: El usuario elige un cliente de un TableView y se abre una nueva ventana, mostrando la información del cliente.

Stage newStage = new Stage();
try 
{
    AnchorPane page = (AnchorPane) FXMLLoader.load(HectorGestion.class.getResource(fxmlResource));
    Scene scene = new Scene(page);
    newStage.setScene(scene);
    newStage.setTitle(windowTitle);
    newStage.setResizable(isResizable);
    if(showRightAway) 
    {
        newStage.show();
    }
}

newStage sería la nueva ventana. El problema es que no puedo encontrar una manera de decirle al controlador dónde buscar la información del cliente (pasando el id como parámetro).

¿Alguna idea?

Author: RAnders00, 2013-01-07

8 answers

Enfoque Recomendado

Esta respuesta enumera diferentes mecanismos para pasar parámetros a controladores FXML.

Para aplicaciones pequeñas recomiendo pasar parámetros directamente del llamante al controlador: es simple, directo y no requiere marcos adicionales.

Para aplicaciones más grandes y complicadas, valdría la pena investigar si desea usar Inyección de dependencias o Bus de eventos mecanismos dentro de su aplicación.

Pasar Parámetros Directamente del Llamador al Controlador

Pase datos personalizados a un controlador FXML recuperando el controlador de la instancia del cargador FXML y llamando a un método en el controlador para inicializarlo con los valores de datos requeridos.

Algo así como el siguiente código:

public Stage showCustomerDialog(Customer customer) {
  FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
      "customerDialog.fxml"
    )
  );

  Stage stage = new Stage(StageStyle.DECORATED);
  stage.setScene(
    new Scene(
      (Pane) loader.load()
    )
  );

  CustomerDialogController controller = 
    loader.<CustomerDialogController>getController();
  controller.initData(customer);

  stage.show();

  return stage;
}

...

class CustomerDialogController {
  @FXML private Label customerName;
  void initialize() {}
  void initData(Customer customer) {
    customerName.setText(customer.getName());
  }
}

Se construye un nuevo FXMLLoader como se muestra en el código de ejemplo, es decir, new FXMLLoader(location). La ubicación es una URL y usted puede genere dicha URL a partir de un recurso FXML mediante:

new FXMLLoader(getClass().getResource("sample.fxml"));

Tenga cuidado de NO usar una función de carga estática en el FXMLLoader, o no podrá obtener su controlador de la instancia del cargador.

Las instancias de FXMLLoader nunca saben nada acerca de los objetos de dominio. No pasa directamente objetos de dominio específicos de la aplicación al constructor FXMLLoader, sino que:

  1. Construir un FXMLLoader basado en el marcado fxml en un ubicación
  2. Obtiene un controlador de la instancia de FXMLLoader.
  3. Invoque métodos en el controlador recuperado para proporcionar al controlador referencias a los objetos de dominio.

Este blog (de otro escritor) proporciona un ejemplo alternativo, pero similar, .

Configuración de un controlador en el FXMLLoader

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
        "customerDialog.fxml"
    )
);
loader.setController(dialogController);

Pane mainPane = (Pane) loader.load();

Puede construir un nuevo controlador en código, pasando cualquier parámetro que desee de su llamante al controlador constructor. Una vez que haya construido un controlador, puede configurarlo en una instancia de FXMLLoader antes de que invoque load() método instance.

Para configurar un controlador en un cargador (en JavaFX 2.x) tampoco PUEDE definir un atributo fx:controller en su archivo fxml.

Debido a la limitación en la definición fx:controller en FXML, personalmente prefiero obtener el controlador del FXMLLoader en lugar de configurar el controlador en el FXMLLoader.

Hacer que el Controlador Recupere Parámetros de un Método Estático Externo

Este método es ejemplificado por la respuesta de Sergey a Javafx 2.0 How-to Application.getParameters() en un Controlador.archivo java .

Use Inyección de Dependencia

FXMLLoader soporta sistemas de inyección de dependencias como Guice, Spring o Java EE CDI permitiéndole establecer una fábrica de controladores personalizados en el FXMLLoader. Esto proporciona una devolución de llamada que se puede utilizar para crear la instancia del controlador con valores dependientes inyectados por el respectivo sistema de inyección de dependencias. Hay un ejemplo de integración de FXML con el sistema de inyección de dependencias de resorte (desafortunadamente el enlace está muerto y el contenido se ha ido, si alguien sabe de un ejemplo similar, por favor edite esta pregunta para hacer referencia a él), aunque es un poco más torpe de lo que sería usar las nuevas características de fábrica de controladores personalizados disponibles en JavaFX 2.2.

A el enfoque de inyección de dependencia realmente agradable y limpio se ejemplifica con el postquemador .fx framework con un ejemplo aplicación air-hacks que lo utiliza. postcombustión.fx se basa en JEE6 javax.inyecte para realizar la inyección de dependencia.

Usar un Bus de eventos

Greg Brown, el creador e implementador original de la especificación FXML, a menudo sugiere considerar el uso de un bus de eventos para la comunicación entre controladores instanciados de FXML y otra lógica de aplicación.

El EventBus es una API de publicación/suscripción simple pero potente con anotaciones que permite a los POJOs comunicarse entre sí en cualquier lugar de una JVM sin tener que referirse entre sí.

Preguntas y respuestas de seguimiento

En el primer método, ¿por qué devuelves la Etapa? El método también puede ser void porque ya está dando el comando show (); justo antes de return stage;. ¿Cómo planifica el uso devolviendo el Etapa

Es una solución funcional a un problema. Una etapa es devuelta desde la función showCustomerDialog para que una referencia a ella pueda ser almacenada por una clase externa que desee hacer algo, como ocultar la etapa basándose en un clic de botón en la ventana principal, en un momento posterior. Una solución alternativa orientada a objetos podría encapsular la funcionalidad y la referencia de etapa dentro de un objeto CustomerDialog o tener una etapa CustomerDialog extend. Un ejemplo completo para un objeto orientado interfaz a un diálogo personalizado encapsulando datos FXML, controlador y modelo está más allá del alcance de esta respuesta, pero puede hacer una entrada de blog que valga la pena para cualquier persona inclinada a crear uno.


Información adicional proporcionada por el usuario de StackOverflow llamado @dzim

Ejemplo para Inyección de Dependencia de Arranque de Resorte

La pregunta de cómo hacerlo "La Forma de Arranque de primavera", hubo una discusión sobre JavaFX 2, que anserwered en el enlace permanente adjunto. El enfoque sigue siendo válido y probado en marzo de 2016, en Spring Boot v1.3.3.LANZAR: https://stackoverflow.com/a/36310391/1281217


A veces, es posible que desee pasar los resultados a la persona que llama, en cuyo caso puede consultar la respuesta a la pregunta relacionada:

 220
Author: jewelsea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-11 23:18:21

Javafx.escena.La clase Node tiene un par de métodos setUserData(Objeto) y Objeto getUserData ()

Que podría usar para agregar su información al nodo.

Entonces, puedes llamar a page.setUserData(info);

Y el controlador puede comprobar, si la información está establecida. Además, puede usar ObjectProperty para la transferencia de datos hacia atrás, si es necesario.

Observe una documentación aquí: http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html Antes de la phrase " En la primera versión, el handleButtonAction () está etiquetado con @FXML para permitir que el marcado definido en el documento del controlador lo invoque. En el segundo ejemplo, el campo botón está anotado para permitir que el cargador establezca su valor. El método initialize () está anotado de manera similar."

Por lo tanto, debe asociar un controlador con un nodo y establecer un dato de usuario en el nodo.

 8
Author: Alexander Kirov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-07 00:02:20

Aquí hay un ejemplo para pasar parámetros a un documento fxml a través del espacio de nombres.

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1">
    <BorderPane>
        <center>
            <Label text="$labelText"/>
        </center>
    </BorderPane>
</VBox>

Defina el valor External Text para la variable de espacio de nombres labelText:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class NamespaceParameterExampleApplication extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("namespace-parameter-example.fxml"));

        fxmlLoader.getNamespace()
                  .put("labelText", "External Text");

        final Parent root = fxmlLoader.load();

        primaryStage.setTitle("Namespace Parameter Example");
        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
    }
}
 6
Author: user1503636,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-12 04:05:07

Esto FUNCIONA ..

Recuerde que la primera vez que imprima el valor de paso obtendrá null, Puede usarlo después de que se cargue Windows , lo mismo para todo lo que desee codificar para cualquier otro componente.

Primer Controlador

try {
                                Stage st = new Stage();
                                 FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/inty360/free/form/MainOnline.fxml"));

                                Parent sceneMain = loader.load();

                                MainOnlineController controller = loader.<MainOnlineController>getController();
                                controller.initVariable(99L);

                                Scene scene = new Scene(sceneMain);
                                st.setScene(scene);
                                st.setMaximized(true);
                                st.setTitle("My App");
                                st.show();
                            } catch (IOException ex) {
                                Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, ex);
                            }

Otro Controlador

public void initVariable(Long id_usuario){
        this.id_usuario = id_usuario;
        label_usuario_nombre.setText(id_usuario.toString());

    }
 3
Author: diego matos - keke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-28 22:25:12

Me doy cuenta de que este es un post muy antiguo y ya tiene algunas grandes respuestas, pero quería hacer un MCVE simple para demostrar uno de esos enfoques y permitir a los nuevos programadores una forma de ver rápidamente el concepto en acción.

En este ejemplo, usaremos 5 archivos:

  1. Main.java - Se usa simplemente para iniciar la aplicación y llamar al primer controlador.
  2. Controlador1.java - El controlador para el primer FXML diseño.
  3. Controlador2.java - El controlador para el segundo diseño FXML.
  4. Layout1.fxml - El diseño FXML para la primera escena.
  5. Layout2.fxml - El diseño FXML para la segunda escena.

Todos los archivos se enumeran en su totalidad en la parte inferior de esta publicación.

El Objetivo: Demostrar valores de paso de Controller1 a Controller2 y viceversa.

El Programa Flujo:

  • La primera escena contiene a TextField, a Button, y a Label. Cuando se hace clic en Button, se carga y muestra la segunda ventana, incluido el texto introducido en TextField.
  • Dentro de la segunda escena, también hay un TextField, a Button, y Label. El Label mostrará el texto introducido en el TextField en la primera escena.
  • Al introducir texto en la segunda escena TextField y hacer clic en su Button, la primera escena Label se actualiza para mostrar el texto.

Esta es una demostración muy simple y seguramente podría significar alguna mejora, pero debería dejar el concepto muy claro.

El código en sí también se comenta con algunos detalles de lo que está sucediendo y cómo.

LAS CÓDIGO

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Create the first controller, which loads Layout1.fxml within its own constructor
        Controller1 controller1 = new Controller1();

        // Show the new stage
        controller1.showStage();

    }
}

Controlador1.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller1 {

    // Holds this controller's Stage
    private final Stage thisStage;

    // Define the nodes from the Layout1.fxml file. This allows them to be referenced within the controller
    @FXML
    private TextField txtToSecondController;
    @FXML
    private Button btnOpenLayout2;
    @FXML
    private Label lblFromController2;

    public Controller1() {

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout1.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout1");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    /**
     * The initialize() method allows you set setup your scene, adding actions, configuring nodes, etc.
     */
    @FXML
    private void initialize() {

        // Add an action for the "Open Layout2" button
        btnOpenLayout2.setOnAction(event -> openLayout2());
    }

    /**
     * Performs the action of loading and showing Layout2
     */
    private void openLayout2() {

        // Create the second controller, which loads its own FXML file. We pass a reference to this controller
        // using the keyword [this]; that allows the second controller to access the methods contained in here.
        Controller2 controller2 = new Controller2(this);

        // Show the new stage/window
        controller2.showStage();

    }

    /**
     * Returns the text entered into txtToSecondController. This allows other controllers/classes to view that data.
     */
    public String getEnteredText() {
        return txtToSecondController.getText();
    }

    /**
     * Allows other controllers to set the text of this layout's Label
     */
    public void setTextFromController2(String text) {
        lblFromController2.setText(text);
    }
}

Controlador2.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller2 {

    // Holds this controller's Stage
    private Stage thisStage;

    // Will hold a reference to the first controller, allowing us to access the methods found there.
    private final Controller1 controller1;

    // Add references to the controls in Layout2.fxml
    @FXML
    private Label lblFromController1;
    @FXML
    private TextField txtToFirstController;
    @FXML
    private Button btnSetLayout1Text;

    public Controller2(Controller1 controller1) {
        // We received the first controller, now let's make it usable throughout this controller.
        this.controller1 = controller1;

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout2.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout2");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    @FXML
    private void initialize() {

        // Set the label to whatever the text entered on Layout1 is
        lblFromController1.setText(controller1.getEnteredText());

        // Set the action for the button
        btnSetLayout1Text.setOnAction(event -> setTextOnLayout1());
    }

    /**
     * Calls the "setTextFromController2()" method on the first controller to update its Label
     */
    private void setTextOnLayout1() {
        controller1.setTextFromController2(txtToFirstController.getText());
    }

}

Disposición 1.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="This is Layout1!"/>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToSecondController"/>
            <Button fx:id="btnOpenLayout2" mnemonicParsing="false" text="Open Layout2"/>
        </HBox>
        <VBox alignment="CENTER">
            <Label text="Text From Controller2:"/>
            <Label fx:id="lblFromController2" text="Nothing Yet!"/>
        </VBox>
    </VBox>
</AnchorPane>

Layout2.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="Welcome to Layout 2!"/>
        <VBox alignment="CENTER">
            <Label text="Text From Controller1:"/>
            <Label fx:id="lblFromController1" text="Nothing Yet!"/>
        </VBox>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToFirstController"/>
            <Button fx:id="btnSetLayout1Text" mnemonicParsing="false" text="Set Text on Layout1"/>
        </HBox>
    </VBox>
</AnchorPane>
 3
Author: Zephyr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-26 20:20:45

Tienes que crear una Clase de contexto.

public class Context {
    private final static Context instance = new Context();
    public static Context getInstance() {
        return instance;
    }

    private Connection con;
    public void setConnection(Connection con)
    {
        this.con=con;
    }
    public Connection getConnection() {
        return con;
    }

    private TabRoughController tabRough;
    public void setTabRough(TabRoughController tabRough) {
        this.tabRough=tabRough;
    }

    public TabRoughController getTabRough() {
        return tabRough;
    }
}

Solo tiene que establecer la instancia del controlador en la inicialización usando

Context.getInstance().setTabRough(this);

Y puede usarlo desde toda su aplicación simplemente usando

TabRoughController cont=Context.getInstance().getTabRough();

Ahora puede pasar el parámetro a cualquier controlador desde toda la aplicación.

 1
Author: CTN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-22 14:14:44

Aquí hay un ejemplo para usar un controlador inyectado por Guice.

/**
 * Loads a FXML file and injects its controller from the given Guice {@code Provider}
 */
public abstract class GuiceFxmlLoader {

   public GuiceFxmlLoader(Stage stage, Provider<?> provider) {
      mStage = Objects.requireNonNull(stage);
      mProvider = Objects.requireNonNull(provider);
   }

   /**
    * @return the FXML file name
    */
   public abstract String getFileName();

   /**
    * Load FXML, set its controller with given {@code Provider}, and add it to {@code Stage}.
    */
   public void loadView() {
      try {
         FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource(getFileName()));
         loader.setControllerFactory(p -> mProvider.get());
         Node view = loader.load();
         setViewInStage(view);
      }
      catch (IOException ex) {
         LOGGER.error("Failed to load FXML: " + getFileName(), ex);
      }
   }

   private void setViewInStage(Node view) {
      BorderPane pane = (BorderPane)mStage.getScene().getRoot();
      pane.setCenter(view);
   }

   private static final Logger LOGGER = Logger.getLogger(GuiceFxmlLoader.class);

   private final Stage mStage;
   private final Provider<?> mProvider;
}

Aquí hay una implementación concreta del cargador:

public class ConcreteViewLoader extends GuiceFxmlLoader {

   @Inject
   public ConcreteViewLoader(Stage stage, Provider<MyController> provider) {
      super(stage, provider);
   }

   @Override
   public String getFileName() {
      return "my_view.fxml";
   }
}

Tenga en cuenta que este ejemplo carga la vista en el centro de un BoarderPane que es la raíz de la Escena en el Escenario. Esto es irrelevante para el ejemplo (detalle de implementación de mi caso de uso específico), pero decidí dejarlo ya que algunos pueden encontrarlo útil.

 0
Author: jenglert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-20 00:48:45

Puede decidir usar una lista pública observable para almacenar datos públicos, o simplemente crear un método de configuración pública para almacenar datos y recuperarlos del controlador correspondiente

 0
Author: Nospaniol Noah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-15 12:29:43