Übergeben von Parametern JavaFX FXML

194

Wie kann ich Parameter an ein sekundäres Fenster in javafx übergeben? Gibt es eine Möglichkeit, mit dem entsprechenden Controller zu kommunizieren?

Beispiel: Der Benutzer wählt einen Kunden aus a aus TableViewund ein neues Fenster mit den Kundeninformationen wird geöffnet.

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

newStagewäre das neue Fenster. Das Problem ist, dass ich keine Möglichkeit finde, dem Controller mitzuteilen, wo nach Kundeninformationen gesucht werden soll (indem die ID als Parameter übergeben wird).

Irgendwelche Ideen?

Alvaro
quelle
Überprüfen Sie, ob dies auch funktioniert: stackoverflow.com/questions/14370183/…
Dynelight
@ Alvaro: Hast du deine Lösung bekommen? können Sie Parameter übergeben? von einem Controller zu einer anderen Controller-Datei?
Java Man
3
Ja. Jewelsea gab eine Erklärung auf Buchebene. Deshalb habe ich seine Antwort akzeptiert
Alvaro

Antworten:

276

Empfohlener Ansatz

Diese Antwort listet verschiedene Mechanismen zum Übergeben von Parametern an FXML-Controller auf.

Für kleine Anwendungen empfehle ich dringend, Parameter direkt vom Aufrufer an die Steuerung zu übergeben - es ist einfach, unkompliziert und erfordert keine zusätzlichen Frameworks.

Für größere, kompliziertere Anwendungen lohnt es sich zu untersuchen, ob Sie in Ihrer Anwendung Abhängigkeitsinjektions- oder Ereignisbusmechanismen verwenden möchten .

Parameter direkt vom Anrufer an den Controller übergeben

Übergeben Sie benutzerdefinierte Daten an einen FXML-Controller, indem Sie den Controller aus der FXML-Loader-Instanz abrufen und eine Methode auf dem Controller aufrufen, um ihn mit den erforderlichen Datenwerten zu initialisieren.

So etwas wie der folgende Code:

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

Ein neuer FXMLLoader wird wie im Beispielcode gezeigt erstellt, d new FXMLLoader(location). H. Der Speicherort ist eine URL, und Sie können eine solche URL aus einer FXML-Ressource generieren, indem Sie:

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

Achten Sie darauf, KEINE statische Ladefunktion für den FXMLLoader zu verwenden, da Sie sonst Ihren Controller nicht von Ihrer Loader-Instanz abrufen können.

FXMLLoader-Instanzen selbst wissen nie etwas über Domänenobjekte. Sie übergeben anwendungsspezifische Domänenobjekte nicht direkt an den FXMLLoader-Konstruktor, sondern:

  1. Erstellen Sie einen FXMLLoader basierend auf dem fxml-Markup an einem bestimmten Speicherort
  2. Holen Sie sich einen Controller von der FXMLLoader-Instanz.
  3. Rufen Sie Methoden auf dem abgerufenen Controller auf, um dem Controller Verweise auf die Domänenobjekte bereitzustellen.

Dieser Blog (von einem anderen Autor) bietet ein alternatives, aber ähnliches Beispiel .

Festlegen eines Controllers im FXMLLoader

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

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

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

Sie können einen neuen Controller im Code erstellen und alle gewünschten Parameter von Ihrem Aufrufer an den Controller-Konstruktor übergeben. Nachdem Sie einen Controller erstellt haben, können Sie ihn für eine FXMLLoader-Instanz festlegen, bevor Sie die load() Instanzmethode aufrufen .

Um einen Controller auf einem Loader (in JavaFX 2.x) festzulegen, können Sie NICHT auch ein fx:controllerAttribut in Ihrer fxml-Datei definieren.

Aufgrund der Einschränkung der fx:controllerDefinition in FXML ziehe ich es persönlich vor, den Controller vom FXMLLoader zu beziehen, anstatt den Controller in den FXMLLoader zu setzen.

Lassen Sie den Controller Parameter von einer externen statischen Methode abrufen

Diese Methode wird durch die Antwort von Sergey auf Javafx 2.0 How-to Application.getParameters () in einer Controller.java-Datei veranschaulicht .

Verwenden Sie die Abhängigkeitsinjektion

FXMLLoader unterstützt Abhängigkeitsinjektionssysteme wie Guice, Spring oder Java EE CDI, indem Sie eine benutzerdefinierte Controller-Factory auf dem FXMLLoader festlegen können. Dies bietet einen Rückruf, mit dem Sie die Controller-Instanz mit abhängigen Werten erstellen können, die vom jeweiligen Abhängigkeitsinjektionssystem eingespeist werden.

Ein Beispiel für die Injektion von JavaFX-Anwendungen und Controller-Abhängigkeiten mit Spring finden Sie in der Antwort auf:

Ein wirklich netter, sauberer Ansatz zur Abhängigkeitsinjektion wird durch das afterburner.fx-Framework mit einer Beispielanwendung für Air-Hacks veranschaulicht , die es verwendet. afterburner.fx verwendet JEE6 javax.inject , um die Abhängigkeitsinjektion durchzuführen.

Verwenden Sie einen Ereignisbus

Greg Brown, der ursprüngliche Ersteller und Implementierer von FXML-Spezifikationen, schlägt häufig vor, die Verwendung eines Ereignisbusses wie des Guava EventBus für die Kommunikation zwischen FXML-instanziierten Controllern und anderer Anwendungslogik in Betracht zu ziehen .

Der EventBus ist eine einfache, aber leistungsstarke Publish / Subscribe-API mit Anmerkungen, mit der POJOs überall in einer JVM miteinander kommunizieren können, ohne aufeinander verweisen zu müssen.

Follow-up-Fragen und Antworten

Warum geben Sie bei der ersten Methode Stage zurück? Die Methode kann auch ungültig sein, da Sie bereits den Befehl show () eingeben. kurz vor der Rückkehr;. Wie planen Sie die Nutzung, indem Sie die Bühne zurückgeben?

Es ist eine funktionale Lösung für ein Problem. Eine Stufe wird von der showCustomerDialogFunktion zurückgegeben, damit ein Verweis darauf von einer externen Klasse gespeichert werden kann, die möglicherweise etwas tun möchte, z. B. die Stufe zu einem späteren Zeitpunkt durch Klicken auf eine Schaltfläche im Hauptfenster ausblenden. Eine alternative, objektorientierte Lösung könnte die Funktionalität und die Bühnenreferenz in einem CustomerDialog-Objekt kapseln oder eine CustomerDialog-Erweiterungsstufe haben. Ein vollständiges Beispiel für eine objektorientierte Schnittstelle zu einem benutzerdefinierten Dialogfeld, das FXML-, Controller- und Modelldaten enthält, würde den Rahmen dieser Antwort sprengen, kann jedoch für jeden, der dazu neigt, einen zu erstellen, einen lohnenden Blog-Beitrag darstellen.


Zusätzliche Informationen vom StackOverflow-Benutzer @dzim

Beispiel für Spring Boot Dependency Injection

Bei der Frage, wie es geht "The Spring Boot Way", gab es eine Diskussion über JavaFX 2, die ich im angehängten Permalink beantwortet habe. Der Ansatz ist noch gültig und wurde im März 2016 auf Spring Boot v1.3.3.RELEASE getestet: https://stackoverflow.com/a/36310391/1281217


Manchmal möchten Sie die Ergebnisse möglicherweise an den Anrufer zurückgeben. In diesem Fall können Sie die Antwort auf die entsprechende Frage überprüfen:

Juwelensee
quelle
FXMLLoader-Konstruktoren verwenden nur URLs als Parameter. Wie kann der FXMLLoader korrekt instanziiert werden?
Alvaro
1
Die Eventbus-Website spielte auf die Staaten "Update 3/2013: Der EventBus ist abgestanden ..." an
j wird
1
Die DataFX Controller Frameworks bieten einige Injektionsunterstützung für FXML-Controller: guigarage.com/2013/12/datafx-controller-framework-preview
Hendrik Ebbers
2
Es wurde ein zusätzlicher Abschnitt mit Fragen und Antworten
hinzugefügt
7
Gibt es für Godshake etwas Einfaches, um diese winzige Arbeit in JavaFx zu erledigen? Es ist eine sehr häufige Funktion, Daten in Konstruktor und Javafx zu übergeben. Benötigt diese Hölle alles zusammen, um nur einen Namen oder einen Wert zu senden?
Zahan Safallwa
13

Mir ist klar, dass dies ein sehr alter Beitrag ist und bereits einige gute Antworten enthält, aber ich wollte ein einfaches MCVE erstellen, um einen solchen Ansatz zu demonstrieren und neuen Programmierern die Möglichkeit zu geben, das Konzept schnell in Aktion zu sehen.

In diesem Beispiel werden 5 Dateien verwendet:

  1. Main.java - Wird einfach verwendet, um die Anwendung zu starten und den ersten Controller aufzurufen.
  2. Controller1.java - Der Controller für das erste FXML-Layout.
  3. Controller2.java - Der Controller für das zweite FXML-Layout.
  4. Layout1.fxml - Das FXML-Layout für die erste Szene.
  5. Layout2.fxml - Das FXML-Layout für die zweite Szene.

Alle Dateien werden in ihrer Gesamtheit am Ende dieses Beitrags aufgelistet.

Das Ziel: Übergeben von Werten von Controller1nach Controller2und umgekehrt.

Der Programmablauf:

  • Die erste Szene enthält a TextField, a Buttonund a Label. Wenn Sie auf Buttonklicken, wird das zweite Fenster geladen und angezeigt, einschließlich des in das Feld eingegebenen Texts TextField.
  • Innerhalb der zweiten Szene gibt es auch a TextField, a Buttonund a Label. Das Labelzeigt den Text an, der TextFieldin der ersten Szene eingegeben wurde .
  • Wenn Sie Text in die zweite Szene eingeben TextFieldund darauf klicken Button, wird die erste Szene Labelaktualisiert, um den eingegebenen Text anzuzeigen.

Dies ist eine sehr einfache Demonstration und könnte sicherlich für eine Verbesserung stehen, sollte aber das Konzept sehr klar machen.

Der Code selbst wird auch mit einigen Details darüber kommentiert, was wie passiert.

DER CODE

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>
Zephyr
quelle
1
Ist es möglich, den Controller in der FXML-Datei einzustellen? Beause entfernt die Zeile: loader.setController(this)und das Hinzufügen des Controllers in der FXML-Datei stürzt die Anwendung ab
Halfacht
1
Nicht, wenn die FXML aus dem Controller selbst geladen wird. Wenn Sie beispielsweise die FXML aus der Main-Klasse laden, können Sie den Controller in der FXML-Datei definieren und mitloader.getController()
Zephyr
Endlich habe ich eine Lösung gefunden, tolles Beispiel. Ich habe es in mein Projekt implementiert und jetzt versuche ich, beide Fenster gleichzeitig zu öffnen und zuerst modal zu machen. Leider öffnet nur einer. Könnte jemand dabei helfen?
Jabba
8

Die Klasse javafx.scene.Node verfügt über zwei Methoden: setUserData (Object) und Object getUserData ()

Mit denen Sie Ihre Daten zum Knoten hinzufügen können.

Sie können also page.setUserData (info) aufrufen.

Und der Controller kann prüfen, ob Informationen eingestellt sind. Bei Bedarf können Sie ObjectProperty auch für die Rückwärtsübertragung von Daten verwenden.

Beachten Sie hier eine Dokumentation: http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html Vor dem Satz "In der ersten Version ist handleButtonAction () mit @FXML gekennzeichnet Damit das im Dokument des Controllers definierte Markup es aufrufen kann. Im zweiten Beispiel wird das Schaltflächenfeld mit Anmerkungen versehen, damit der Loader seinen Wert festlegen kann. Die Methode initialize () ist ebenfalls mit Anmerkungen versehen. "

Sie müssen also einen Controller einem Knoten zuordnen und Benutzerdaten für den Knoten festlegen.

Alexander Kirov
quelle
Stage.getScene () -> Scene.getRoot () -> rekursive Suche mit Parent.getChildrenUnmodizable (). Das ist sehr schmutzig. Wenn jemand etwas Besseres vorschlagen könnte - das wäre großartig.
Alexander Kirov
Es scheint, dass Stage.getScene (). GetRoot () der richtige Weg ist! Danke
Alvaro
7

Hier ist ein Beispiel für die Übergabe von Parametern an ein fxml-Dokument über den Namespace.

<?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>

Wert External Textfür Namespace-Variable definieren 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
quelle
Es sollte beachtet werden , dass einige Tasten intern verwendet: zum Beispiel FXMLLoader.CONTROLLER_KEYWORD, FXMLLoader.LOCATION_KEY, FXMLLoader.RESOURCES_KEYund eine beliebige Zeichenfolge als Wert für das verwendete fx:idAttribut.
Fabian
Vielen Dank dafür, meine andere Szene ist nur ein Container, der den Text zeigt, der zuvor in meiner Hauptszene gezeigt wurde. Jetzt kann ich eine fxml haben, die ich an mehreren Stellen wiederverwenden kann, indem ich Inhalte über Namepace-Variablen initialisiere. Ich musste keine neuen Methoden erstellen oder meinen Konstruktor oder Initialisierer ändern - füge einfach eine Variable in meine FXML ein und füge eine Zeile in meinen fxmloader-Code im Hauptcontroller ein.
SystemsInCode
4

Das funktioniert ..

Denken Sie daran, dass Sie beim ersten Drucken des übergebenen Werts null erhalten. Sie können ihn nach dem Laden Ihrer Fenster verwenden. Dies gilt auch für alles, was Sie für eine andere Komponente codieren möchten.

Erster Controller

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

Ein anderer Controller

public void initVariable(Long id_usuario){
    this.id_usuario = id_usuario;
    label_usuario_nombre.setText(id_usuario.toString());
}
diego matos - keke
quelle
1
Dies funktioniert, wenn Sie Parameter vom ersten Controller an den zweiten übergeben, aber wie Sie Parameter vom zweiten an den ersten Controller übergeben, ich meine, nachdem first.fxml geladen wurde.
Menai Ala Eddine - Aladdin
@XlintXms siehe die verwandte Frage JavaFX FXML Parameter, der von Controller A nach B und zurück übergeben wird und Ihre zusätzliche Frage beantwortet.
Jewelsea
2

Sie müssen eine Kontextklasse erstellen.

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;
    }
}

Sie müssen nur die Instanz des Controllers bei der Initialisierung mit festlegen

Context.getInstance().setTabRough(this);

und Sie können es aus Ihrer gesamten Anwendung nur mit verwenden

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

Jetzt können Sie Parameter aus der gesamten Anwendung an jeden Controller übergeben.

CTN
quelle
Wir verwenden diesen Ansatz und er funktioniert hervorragend. Ich mag es, dass ich Zugriff auf Daten im Konstruktor oder in der Initialisierungsmethode habe und die Daten nicht in der Steuerung festlegen muss, nachdem sie erstellt wurden
Bob
1

Ja, du kannst.
Sie müssen im ersten Controller hinzufügen:

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

Dann deklarieren Sie im zweiten einen Client und dann am unteren Rand Ihres Controllers:

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

Hier ist ein Beispiel für die Verwendung eines von Guice injizierten Controllers.

/**
 * 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;
}

Hier ist eine konkrete Implementierung des Laders:

public class ConcreteViewLoader extends GuiceFxmlLoader {

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

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

Beachten Sie, dass in diesem Beispiel die Ansicht in die Mitte eines BoarderPane geladen wird, das die Wurzel der Szene in der Bühne darstellt. Dies ist für das Beispiel irrelevant (Implementierungsdetail meines spezifischen Anwendungsfalls), hat jedoch beschlossen, es beizubehalten, da einige es möglicherweise nützlich finden.

jenglert
quelle
-1

Sie können eine öffentlich beobachtbare Liste zum Speichern öffentlicher Daten verwenden oder einfach eine öffentliche Setter-Methode erstellen, um Daten zu speichern und vom entsprechenden Controller abzurufen

Nospaniol Noah
quelle
-3

Warum eine 6 Jahre alte Frage beantworten?
Eines der grundlegendsten Konzepte für jede Programmiersprache ist die Navigation von einem (Fenster, Formular oder Seite) zum anderen. Auch während dieser Navigation möchte der Entwickler häufig Daten von einem (Fenster, Formular oder Seite) übergeben und die übergebenen Daten anzeigen oder verwenden.
Während die meisten Antworten hier gute bis hervorragende Beispiele dafür liefern, dachten wir, wir würden sie auf den Kopf stellen eine Kerbe oder zwei oder drei
Wir sagten drei, weil wir zwischen drei (Fenster, Formular oder Seite) navigieren und das Konzept der statischen Variablen verwenden, um Daten um das Fenster (Formular, Formular oder Seite)
herumzuleiten wir navigieren

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

Starten Sie den Controller

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

Page One Controller

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

}

Seite Zwei Controller

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) {

}    

}

Unten finden Sie alle FXML-Dateien

<?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>

Vektor
quelle
3
Es tut mir leid, aber hundert Codezeilen zu veröffentlichen, ohne zu erklären, was es tut oder warum Sie es so machen, wie Sie es tun, ist keine sehr gute Antwort. Außerdem ist der von Ihnen veröffentlichte Code sehr schlecht organisiert und schwer zu befolgen.
Zephyr
Es ist nicht nötig, unhöflich gegenüber der fragenden Person zu sein. Wir sind alle hier, um zu lernen
Zeyad