Parámetros de paso JavaFX FXML

194

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

Por ejemplo: el usuario elige un cliente de TableVieway se abre una nueva ventana que muestra 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();
    }
}

newStageSerí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).

¿Algunas ideas?

Alvaro
fuente
Compruebe si esto también funciona: stackoverflow.com/questions/14370183/…
Dynelight
@Alvaro: ¿obtuviste tu solución? puedes pasar el parámetro? de un controlador a otro archivo de controlador?
Java Man
3
Si. Jewelsea dio una explicación a nivel de libro. Es por eso que acepté su respuesta
Alvaro

Respuestas:

276

Enfoque recomendado

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

Para aplicaciones pequeñas, recomiendo pasar parámetros directamente de la persona que llama al controlador: es simple, directo y no requiere marcos adicionales.

Para aplicaciones más grandes y complicadas, valdría la pena investigar si desea utilizar mecanismos de inyección de dependencia o bus de eventos dentro de su aplicación.

Pasando parámetros directamente de la persona que llama 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 puede generar dicha URL a partir de un recurso FXML al:

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 desde la instancia del cargador.

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

  1. Construya un FXMLLoader basado en el marcado fxml en una ubicación específica
  2. Obtenga un controlador de la instancia 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 los parámetros que desee de su llamante al constructor del controlador. Una vez que haya construido un controlador, puede configurarlo en una instancia de FXMLLoader antes de invocar la load() instancia método de .

Para configurar un controlador en un cargador (en JavaFX 2.x) NO PUEDE definir un fx:controlleratributo en su archivo fxml.

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

Hacer que el controlador recupere los parámetros de un método estático externo

Este método se ejemplifica con la respuesta de Sergey a Javafx 2.0 How-to Application.getParameters () en un archivo Controller.java .

Usar inyección de dependencia

FXMLLoader admite sistemas de inyección de dependencias como Guice, Spring o Java EE CDI al permitirle configurar una fábrica de controladores personalizados en FXMLLoader. Esto proporciona una devolución de llamada que puede usar para crear la instancia del controlador con valores dependientes inyectados por el sistema de inyección de dependencia respectivo.

Un ejemplo de aplicación JavaFX y la inyección de dependencia del controlador con Spring se proporciona en la respuesta a:

Un marco de inyección de dependencia realmente agradable y limpio se ejemplifica en el marco afterburner.fx con una aplicación de muestra de ataques de aire que lo utiliza. afterburner.fx se basa en JEE6 javax.inject para realizar la inyección de dependencia.

Use un bus de eventos

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

EventBus es una API de publicación / suscripción simple pero potente con anotaciones que permite a los POJO 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é regresas a Stage? El método también puede anularse porque ya está dando el comando show (); justo antes de la etapa de regreso; ¿Cómo se planifica el uso devolviendo el escenario?

Es una solución funcional a un problema. Se devuelve una etapa de la showCustomerDialogfunción para que una clase externa pueda almacenar una referencia a ella que tal vez desee hacer algo, como ocultar la etapa con un clic de botón en la ventana principal, más adelante. Una solución alternativa orientada a objetos podría encapsular la funcionalidad y la referencia de etapa dentro de un objeto CustomerDialog o hacer que CustomerDialog extienda Stage. Un ejemplo completo de una interfaz orientada a objetos para un cuadro de diálogo personalizado que encapsula FXML, el controlador y los datos del modelo está más allá del alcance de esta respuesta, pero puede ser una publicación de blog que valga la pena para cualquiera que esté dispuesto a crear una.


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

Ejemplo de inyección de dependencia de arranque de resorte

La pregunta de cómo hacerlo "The Spring Boot Way", hubo una discusión sobre JavaFX 2, que respondí en el enlace permanente adjunto. El enfoque aún es válido y se probó en marzo de 2016, en Spring Boot v1.3.3. PUBLICACIÓN: 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:

Jewelsea
fuente
Los constructores de FXMLLoader solo toman URL como parámetros. ¿Cuál es la forma correcta de instanciar el FXMLLoader?
Alvaro
1
el sitio web del bus de eventos aludió a los estados, "Actualización 3/2013: El EventBus se ha quedado obsoleto ..."
j será el
1
DataFX Controller Frameworks proporciona soporte de inyección para controladores FXML
Hendrik Ebbers
2
Se agregó una sección adicional de preguntas y respuestas para responder a las preguntas adicionales de @Anarkie
jewelsea
77
para Godshake, ¿hay algo simple para hacer este pequeño trabajo en JavaFx? es una característica muy común para pasar datos en constructor y javafx requiere todas estas cosas solo para enviar un nombre o un valor?
Zahan Safallwa
13

Me doy cuenta de que esta es una publicación muy antigua y que ya tiene excelentes 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, utilizaremos 5 archivos:

  1. Main.java : simplemente se usa para iniciar la aplicación y llamar al primer controlador.
  2. Controller1.java : el controlador para el primer diseño FXML.
  3. Controller2.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 al final de esta publicación.

La meta: Para demostrar que pasa los valores de Controller1a Controller2y viceversa.

El flujo del programa:

  • La primera escena contiene a TextField, a Buttony a Label. Cuando Buttonse hace clic en, se carga y muestra la segunda ventana, incluido el texto ingresado en elTextField .
  • Dentro de la segunda escena, también hay a TextField, a Buttony a Label. El Labelmostrará el texto ingresado en elTextField en la primera escena.
  • Al ingresar texto en la segunda escena TextFieldy hacer clic en él Button, la primera escena Labelse actualiza para mostrar el texto ingresado.

Esta es una demostración muy simple y seguramente podría representar 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.

EL 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();

    }
}

Controller1.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);
    }
}

Controller2.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());
    }

}

Layout1.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>
Céfiro
fuente
1
¿Es posible configurar el controlador en el archivo FXML? Beause quitando la línea: loader.setController(this)y agregar el controlador en el archivo FXML bloquea la aplicación
Halfacht
1
No si el FXML se carga desde el propio controlador. Si carga el FXML de la clase principal, por ejemplo, se puede definir el controlador en el archivo FXML y obtener una referencia a él usandoloader.getController()
Zephyr
Finalmente logré encontrar una solución, un gran ejemplo. Lo implementé en mi proyecto y ahora estoy tratando de hacer que ambas ventanas se abran simultáneamente y que la primera sea modal. Lamentablemente, solo uno se abre. ¿Alguien podría ayudar con eso?
jabba
8

La clase javafx.scene.Node tiene un par de métodos setUserData (Object) y Object getUserData ()

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

Por lo tanto, puede llamar a page.setUserData (info);

Y el controlador puede verificar, si la información está configurada. 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 frase "En la primera versión, handleButtonAction () está etiquetado con @FXML para permitir el marcado definido en el documento del controlador para invocarlo. En el segundo ejemplo, el campo del botón se anota para permitir que el cargador establezca su valor. El método initialize () se anota de manera similar ".

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

Alexander Kirov
fuente
Stage.getScene () -> Scene.getRoot () -> búsqueda recursiva con Parent.getChildrenUnmodifiable (). Esta es una manera muy sucia. Si alguien pudiera sugerir algo mejor, sería genial.
Alexander Kirov
¡Parece que Stage.getScene (). GetRoot () es la forma correcta! Gracias
Alvaro
7

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 Textpara 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();
    }
}
user1503636
fuente
Cabe señalar que algunas teclas se utilizan internamente: por ejemplo FXMLLoader.CONTROLLER_KEYWORD, FXMLLoader.LOCATION_KEY, FXMLLoader.RESOURCES_KEYy cualquier cadena que se utiliza como valor para el fx:idatributo.
fabian
Gracias por esto, mi otra escena es solo un contenedor que muestra el texto previamente mostrado en mi escena principal. Ahora puedo tener un fxml que puedo reutilizar en varios lugares al inicializar el contenido a través de variables de espacio de nombres. No tuve que crear ningún método nuevo ni alterar mi constructor o inicializadores, solo agregué la variable en mi FXML y agregué una línea en mi código fxmloader en el controlador principal.
SystemsInCode
4

Esto funciona ..

Recuerde que la primera vez que imprima el valor de aprobación obtendrá un valor nulo. Puede usarlo después de cargar sus ventanas, 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());
}
diego matos - keke
fuente
1
Esto funciona cuando pasa el parámetro del primer controlador al segundo, pero cómo pasar el parámetro del segundo al primer controlador, es decir, después de cargar first.fxml.
Menai Ala Eddine - Aladdin
@XlintXms vea la pregunta relacionada El parámetro JavaFX FXML que pasa del Controlador A a B y viceversa , que aborda su pregunta adicional.
jewelsea
2

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 configurar la instancia del controlador en la inicialización usando

Context.getInstance().setTabRough(this);

y puedes usarlo desde toda tu aplicación simplemente usando

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

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

CTN
fuente
Utilizamos este enfoque y funciona muy bien. Me gusta que tengo acceso a los datos dentro del constructor o en el método de inicialización y no tengo que configurar los datos en el controlador después de que se construye
Bob
1

Sí tu puedes.
Necesita agregar en el primer controlador:

YourController controller = loader.getController();     
controller.setclient(client);

Luego, en el segundo, declare un cliente, luego en la parte inferior de su controlador:

public void setclien(Client c) {
    this.client = c;
}
Montassar Bouagina
fuente
0

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 puesto, ya que algunos pueden encontrarlo útil.

jenglert
fuente
-1

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

Nospaniol Noah
fuente
-3

¿Por qué responder una pregunta de 6 años?
Uno de los conceptos más fundamentales para trabajar con cualquier lenguaje de programación es cómo navegar de uno (ventana, formulario o página) a otro. Además, mientras realiza esta navegación, el desarrollador a menudo quiere pasar datos de uno (ventana, formulario o página) y mostrar o usar los datos pasados.
Si bien la mayoría de las respuestas aquí proporcionan ejemplos de buenos a excelentes cómo lograr esto, pensamos que lo pondríamos en práctica. una muesca o dos o tres
Dijimos tres porque navegaremos entre tres (ventana, formulario o página) y usaremos el concepto de variables estáticas para pasar datos alrededor de la (ventana, formulario o página)
También incluiremos un código de toma de decisiones mientras navegamos

public class Start extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        // This is MAIN Class which runs first
        Parent root = FXMLLoader.load(getClass().getResource("start.fxml"));
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setResizable(false);// This sets the value for all stages
        stage.setTitle("Start Page"); 
        stage.show();
        stage.sizeToScene();
    }

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

Iniciar controlador

public class startController implements Initializable {

@FXML Pane startPane,pageonePane;
@FXML Button btnPageOne;
@FXML TextField txtStartValue;
public Stage stage;
public static int intSETonStartController;
String strSETonStartController;

@FXML
private void toPageOne() throws IOException{

    strSETonStartController = txtStartValue.getText().trim();


        // yourString != null && yourString.trim().length() > 0
        // int L = testText.length();
        // if(L == 0){
        // System.out.println("LENGTH IS "+L);
        // return;
        // }
        /* if (testText.matches("[1-2]") && !testText.matches("^\\s*$")) 
           Second Match is regex for White Space NOT TESTED !
        */

        String testText = txtStartValue.getText().trim();
        // NOTICE IF YOU REMOVE THE * CHARACTER FROM "[1-2]*"
        // NO NEED TO CHECK LENGTH it also permited 12 or 11 as valid entry 
        // =================================================================
        if (testText.matches("[1-2]")) {
            intSETonStartController = Integer.parseInt(strSETonStartController);
        }else{
            txtStartValue.setText("Enter 1 OR 2");
            return;
        }

        System.out.println("You Entered = "+intSETonStartController);
        stage = (Stage)startPane.getScene().getWindow();// pane you are ON
        pageonePane = FXMLLoader.load(getClass().getResource("pageone.fxml"));// pane you are GOING TO
        Scene scene = new Scene(pageonePane);// pane you are GOING TO
        stage.setScene(scene);
        stage.setTitle("Page One"); 
        stage.show();
        stage.sizeToScene();
        stage.centerOnScreen();  
}

private void doGET(){
    // Why this testing ?
    // strSENTbackFROMPageoneController is null because it is set on Pageone
    // =====================================================================
    txtStartValue.setText(strSENTbackFROMPageoneController);
    if(intSETonStartController == 1){
      txtStartValue.setText(str);  
    }
    System.out.println("== doGET WAS RUN ==");
    if(txtStartValue.getText() == null){
       txtStartValue.setText("");   
    }
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    // This Method runs every time startController is LOADED
     doGET();
}    
}

Controlador de página uno

public class PageoneController implements Initializable {

@FXML Pane startPane,pageonePane,pagetwoPane;
@FXML Button btnOne,btnTwo;
@FXML TextField txtPageOneValue;
public static String strSENTbackFROMPageoneController;
public Stage stage;

    @FXML
private void onBTNONE() throws IOException{

        stage = (Stage)pageonePane.getScene().getWindow();// pane you are ON
        pagetwoPane = FXMLLoader.load(getClass().getResource("pagetwo.fxml"));// pane you are GOING TO
        Scene scene = new Scene(pagetwoPane);// pane you are GOING TO
        stage.setScene(scene);
        stage.setTitle("Page Two"); 
        stage.show();
        stage.sizeToScene();
        stage.centerOnScreen();
}

@FXML
private void onBTNTWO() throws IOException{
    if(intSETonStartController == 2){
        Alert alert = new Alert(AlertType.CONFIRMATION);
        alert.setTitle("Alert");
        alert.setHeaderText("YES to change Text Sent Back");
        alert.setResizable(false);
        alert.setContentText("Select YES to send 'Alert YES Pressed' Text Back\n"
                + "\nSelect CANCEL send no Text Back\r");// NOTE this is a Carriage return\r
        ButtonType buttonTypeYes = new ButtonType("YES");
        ButtonType buttonTypeCancel = new ButtonType("CANCEL", ButtonData.CANCEL_CLOSE);

        alert.getButtonTypes().setAll(buttonTypeYes, buttonTypeCancel);

        Optional<ButtonType> result = alert.showAndWait();
        if (result.get() == buttonTypeYes){
            txtPageOneValue.setText("Alert YES Pressed");
        } else {
            System.out.println("canceled");
            txtPageOneValue.setText("");
            onBack();// Optional
        }
    }
}

@FXML
private void onBack() throws IOException{

    strSENTbackFROMPageoneController = txtPageOneValue.getText();
    System.out.println("Text Returned = "+strSENTbackFROMPageoneController);
    stage = (Stage)pageonePane.getScene().getWindow();
    startPane = FXMLLoader.load(getClass().getResource("start.fxml")); 
    Scene scene = new Scene(startPane);
    stage.setScene(scene);
    stage.setTitle("Start Page"); 
    stage.show();
    stage.sizeToScene();
    stage.centerOnScreen(); 
}

private void doTEST(){
    String fromSTART = String.valueOf(intSETonStartController);
    txtPageOneValue.setText("SENT  "+fromSTART);
    if(intSETonStartController == 1){
       btnOne.setVisible(true);
       btnTwo.setVisible(false);
       System.out.println("INTEGER Value Entered = "+intSETonStartController);  
    }else{
       btnOne.setVisible(false);
       btnTwo.setVisible(true);
       System.out.println("INTEGER Value Entered = "+intSETonStartController); 
    }  
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    doTEST();
}    

}

Controlador de página dos

public class PagetwoController implements Initializable {

@FXML Pane startPane,pagetwoPane;
public Stage stage;
public static String str;

@FXML
private void toStart() throws IOException{

    str = "You ON Page Two";
    stage = (Stage)pagetwoPane.getScene().getWindow();// pane you are ON
    startPane = FXMLLoader.load(getClass().getResource("start.fxml"));// pane you are GOING TO
    Scene scene = new Scene(startPane);// pane you are GOING TO
    stage.setScene(scene);
    stage.setTitle("Start Page"); 
    stage.show();
    stage.sizeToScene();
    stage.centerOnScreen();  
}

@Override
public void initialize(URL url, ResourceBundle rb) {

}    

}

Debajo están todos los archivos FXML

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane id="AnchorPane" fx:id="pagetwoPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atwopage.PagetwoController">
   <children>
      <Button layoutX="227.0" layoutY="62.0" mnemonicParsing="false" onAction="#toStart" text="To Start Page">
         <font>
            <Font name="System Bold" size="18.0" />
         </font>
      </Button>
   </children>
</AnchorPane>

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane id="AnchorPane" fx:id="startPane" prefHeight="200.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atwopage.startController">
   <children>
      <Label focusTraversable="false" layoutX="115.0" layoutY="47.0" text="This is the Start Pane">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <Button fx:id="btnPageOne" focusTraversable="false" layoutX="137.0" layoutY="100.0" mnemonicParsing="false" onAction="#toPageOne" text="To Page One">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Label focusTraversable="false" layoutX="26.0" layoutY="150.0" text="Enter 1 OR 2">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <TextField fx:id="txtStartValue" layoutX="137.0" layoutY="148.0" prefHeight="28.0" prefWidth="150.0" />
   </children>
</AnchorPane>

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane id="AnchorPane" fx:id="pageonePane" prefHeight="200.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atwopage.PageoneController">
   <children>
      <Label focusTraversable="false" layoutX="111.0" layoutY="35.0" text="This is Page One Pane">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <Button focusTraversable="false" layoutX="167.0" layoutY="97.0" mnemonicParsing="false" onAction="#onBack" text="BACK">
         <font>
            <Font size="18.0" />
         </font></Button>
      <Button fx:id="btnOne" focusTraversable="false" layoutX="19.0" layoutY="97.0" mnemonicParsing="false" onAction="#onBTNONE" text="Button One" visible="false">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Button fx:id="btnTwo" focusTraversable="false" layoutX="267.0" layoutY="97.0" mnemonicParsing="false" onAction="#onBTNTWO" text="Button Two">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Label focusTraversable="false" layoutX="19.0" layoutY="152.0" text="Send Anything BACK">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <TextField fx:id="txtPageOneValue" layoutX="195.0" layoutY="150.0" prefHeight="28.0" prefWidth="150.0" />
   </children>
</AnchorPane>

Vector
fuente
3
Lo siento, pero publicar cien líneas de código sin ninguna explicación de lo que hace o por qué lo hace de la manera en que lo hace, no es una muy buena respuesta. Además, el código que publicaste está muy mal organizado y es difícil de seguir.
Zephyr
No hay necesidad de ser grosero con la persona que pregunta. Todos estamos aquí para aprender
Zeyad