Przekazywanie parametrów JavaFX FXML

194

Jak mogę przekazać parametry do dodatkowego okna w javafx? Czy istnieje sposób komunikowania się z odpowiednim kontrolerem?

Na przykład: użytkownik wybiera klienta z TableViewi otwiera się nowe okno, pokazujące informacje o kliencie.

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

newStagebyłoby nowe okno. Problem polega na tym, że nie mogę znaleźć sposobu, aby powiedzieć kontrolerowi, gdzie ma szukać informacji o kliencie (przekazując identyfikator jako parametr).

Jakieś pomysły?

Alvaro
źródło
Sprawdź, czy to też działa: stackoverflow.com/questions/14370183/…
Dynelight
@Alvaro: dostałeś swoje rozwiązanie? czy możesz przekazać parametr? z jednego kontrolera na inny plik kontrolera?
Java Man
3
Tak. jewelsea podał wyjaśnienie na poziomie książki. Dlatego przyjąłem jego odpowiedź
Alvaro

Odpowiedzi:

276

Zalecane podejście

W odpowiedzi wymieniono różne mechanizmy przekazywania parametrów do kontrolerów FXML.

W przypadku małych aplikacji bardzo polecam przekazywanie parametrów bezpośrednio od dzwoniącego do kontrolera - jest to proste, proste i nie wymaga żadnych dodatkowych ram.

W przypadku większych, bardziej skomplikowanych aplikacji warto zbadać, czy chcesz użyć mechanizmów wstrzykiwania zależności lub magistrali zdarzeń w swojej aplikacji.

Przekazywanie parametrów bezpośrednio od dzwoniącego do kontrolera

Przekaż niestandardowe dane do kontrolera FXML, pobierając kontroler z instancji modułu ładującego FXML i wywołując metodę na kontrolerze, aby zainicjować go wymaganymi wartościami danych.

Coś w rodzaju następującego kodu:

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

Nowy FXMLLoader jest zbudowany jak pokazano w przykładowym kodzie, tj new FXMLLoader(location). Lokalizacja jest adresem URL i możesz wygenerować taki adres URL z zasobu FXML poprzez:

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

Uważaj, aby NIE używać funkcji ładowania statycznego na FXMLLoader, w przeciwnym razie nie będziesz mógł pobrać kontrolera z instancji modułu ładującego.

Instancje FXMLLoader same nigdy nie wiedzą nic o obiektach domeny. Nie przekazujesz bezpośrednio obiektów domeny specyficznych dla aplikacji do konstruktora FXMLLoader, zamiast tego:

  1. Skonstruuj program FXMLLoader na podstawie znaczników fxml w określonej lokalizacji
  2. Pobierz kontroler z instancji FXMLLoader.
  3. Wywołaj metody na pobranym kontrolerze, aby zapewnić kontrolerowi odniesienia do obiektów domeny.

Ten blog (autorstwa innego autora) stanowi alternatywny, ale podobny przykład .

Ustawienie kontrolera w FXMLLoader

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

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

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

Możesz zbudować nowy kontroler w kodzie, przekazując dowolne parametry od twojego programu wywołującego do konstruktora kontrolera. Po zbudowaniu kontrolera możesz ustawić go na instancji FXMLLoader przed wywołaniem load() instancji metody .

Aby ustawić kontroler na fx:controllermodule ładującym (w JavaFX 2.x), NIE MOŻNA również zdefiniować atrybutu w pliku fxml.

Ze względu na ograniczenia fx:controllerdefinicji w FXML osobiście wolę pobieranie kontrolera z FXMLLoadera niż ustawienie kontrolera w FXMLLoader.

Posiadanie przez kontroler parametrów do pobrania z zewnętrznej metody statycznej

Przykładem tej metody jest odpowiedź Siergieja na Javafx 2.0 How-to Application.getParameters () w pliku Controller.java .

Użyj wstrzykiwania zależności

FXMLLoader obsługuje systemy wstrzykiwania zależności, takie jak Guice, Spring lub Java EE CDI, umożliwiając ustawienie niestandardowej fabryki kontrolerów w FXMLLoader. Zapewnia to wywołanie zwrotne, którego można użyć do utworzenia instancji kontrolera z wartościami zależnymi wprowadzonymi przez odpowiedni system wstrzykiwania zależności.

Przykład aplikacji JavaFX i wstrzykiwania zależności kontrolera za pomocą Springa znajduje się w odpowiedzi na:

Naprawdę ładne, czyste podejście do wstrzykiwania zależności jest zilustrowane przez framework afterburner.fx z przykładową aplikacją hakerską, która go używa. afterburner.fx opiera się na JEE6 javax.inject wykonuje iniekcję zależności .

Użyj magistrali zdarzeń

Greg Brown, oryginalny twórca i implementator specyfikacji FXML, często sugeruje rozważenie zastosowania magistrali zdarzeń, takiej jak Guava EventBus , do komunikacji między kontrolerami utworzonymi przez FXML i inną logiką aplikacji.

EventBus to prosty, ale potężny interfejs API do publikowania / subskrybowania z adnotacjami, który pozwala POJO komunikować się ze sobą w dowolnym miejscu w JVM bez konieczności odwoływania się do siebie.

Dalsze pytania i odpowiedzi

przy pierwszej metodzie, dlaczego zwracasz scenę? Metoda może być również nieważna, ponieważ już dajesz komendę show (); tuż przed etapem powrotu; Jak planujesz użycie, zwracając stół montażowy

Jest to funkcjonalne rozwiązanie problemu. Etap jest zwracany z showCustomerDialogfunkcji, aby odwołanie do niego mogło zostać zapisane przez klasę zewnętrzną, która może chcieć coś zrobić, na przykład ukryć scenę na podstawie kliknięcia przycisku w oknie głównym w późniejszym czasie. Alternatywne, obiektowe rozwiązanie może zawierać funkcjonalność i odniesienie do etapu wewnątrz obiektu CustomerDialog lub może mieć rozszerzenie Stage CustomerDialog. Pełny przykład zorientowanego obiektowo interfejsu niestandardowego okna dialogowego zawierającego dane FXML, dane kontrolera i modelu wykracza poza zakres tej odpowiedzi, ale może stanowić cenny post na blogu dla każdego, kto chce go utworzyć.


Dodatkowe informacje dostarczone przez użytkownika StackOverflow o nazwie @dzim

Przykład wstrzykiwania zależności rozruchu wiosennego

Pytanie jak to zrobić „The Spring Boot Way”, odbyła się dyskusja na temat JavaFX 2, na którą odpowiedziałem w załączonym permalink. Podejście to jest nadal aktualne i przetestowane w marcu 2016 r. Na Spring Boot v1.3.3. UWOLNIENIE: https://stackoverflow.com/a/36310391/1281217


Czasami możesz chcieć przekazać wyniki dzwoniącemu, w którym to przypadku możesz sprawdzić odpowiedź na powiązane pytanie:

jewelsea
źródło
Konstruktory FXMLLoader biorą tylko adresy URL za parametry. Jaki jest prawidłowy sposób tworzenia instancji FXMLLoader?
Alvaro
1
strona internetowa autobusu zdarzeń zawiera aluzję do słów: „Aktualizacja 3/2013: EventBus zestarzał się ...”
j będzie
1
Framework kontrolera DataFX zapewnia pewne wsparcie wstrzykiwania dla kontrolerów FXML
Hendrik Ebbers
2
Dodano dodatkową sekcję pytań i odpowiedzi, aby odpowiedzieć na dalsze pytania @Anarkie
jewelsea
7
na Godshake, czy jest coś prostego do wykonania tej drobnej pracy w JavFx? jest to bardzo powszechna funkcja przesyłania danych do konstruktora i javafx wymaga tego wszystkiego do diabła, aby wysłać tylko jedną nazwę lub jedną wartość?
Zahan Safallwa
13

Zdaję sobie sprawę, że jest to bardzo stary post i ma już kilka świetnych odpowiedzi, ale chciałem zrobić prosty MCVE, aby zademonstrować jedno takie podejście i pozwolić nowym programistom na szybkie zobaczenie koncepcji w działaniu.

W tym przykładzie użyjemy 5 plików:

  1. Main.java - Po prostu służy do uruchomienia aplikacji i wywołania pierwszego kontrolera.
  2. Controller1.java - Kontroler pierwszego układu FXML.
  3. Controller2.java - Kontroler drugiego układu FXML.
  4. Layout1.fxml - układ FXML dla pierwszej sceny.
  5. Layout2.fxml - układ FXML dla drugiej sceny.

Wszystkie pliki są wymienione w całości na dole tego postu.

Cel: wykazać przekazywanie wartości od Controller1doController2 i odwrotnie.

Przebieg programu:

  • Pierwsza scena zawiera a TextField, a Buttoni a Label. KiedyButton kliknięciu drugie okno jest ładowane i wyświetlane, w tym tekst wprowadzony w TextField.
  • W drugiej scenie znajdują się również a TextField, a Buttoni a Label. LabelWyświetli tekst wprowadzony w TextFieldna pierwszym miejscu.
  • Po wprowadzeniu tekstu w drugiej scenie TextFieldi kliknięciu jejButton , pierwsza scena Labeljest aktualizowana, aby pokazać wprowadzony tekst.

Jest to bardzo prosta demonstracja, która z pewnością może oznaczać pewną poprawę, ale powinna wyjaśnić tę koncepcję bardzo wyraźnie.

Sam kod jest również komentowany z pewnymi szczegółami o tym, co się dzieje i jak.

KOD

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>
Zefir
źródło
1
Czy można ustawić kontroler w pliku FXML? Ponieważ usunięcie wiersza: loader.setController(this)i dodanie kontrolera do pliku FXML powoduje awarię aplikacji
Halfacht
1
Nie, jeśli FXML jest ładowany z poziomu samego kontrolera. Jeśli na przykład załadujesz FXML z klasy Main, możesz zdefiniować kontroler w pliku FXML i uzyskać do niego odwołanie za pomocąloader.getController()
Zephyr
W końcu udało mi się znaleźć rozwiązanie, świetny przykład. Zaimplementowałem to w moim projekcie i teraz staram się, aby oba okna były otwarte jednocześnie, a pierwsze z nich były modalne. Niestety otwiera się tylko jeden. Czy ktoś mógłby w tym pomóc?
jabba
8

Klasa javafx.scene.Node ma parę metod setUserData (Object) i Object getUserData ()

Którego możesz użyć, aby dodać swoje informacje do Węzła.

Możesz więc wywołać page.setUserData (informacje);

Kontroler może sprawdzić, czy ustawione są informacje. W razie potrzeby można również użyć ObjectProperty do przesyłania danych wstecz.

Zapoznaj się z dokumentacją tutaj: http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html Przed frazą „W pierwszej wersji uchwytButtonAction () jest oznaczony @FXML aby umożliwić wywołanie znaczników zdefiniowanych w dokumencie kontrolera. W drugim przykładzie pole przycisku jest opatrzone adnotacjami, aby umożliwić modułowi ładującemu ustawienie swojej wartości. Metoda initialize () jest podobnie opisana. ”

Musisz więc powiązać kontroler z węzłem i ustawić dane użytkownika w węźle.

Alexander Kirov
źródło
Stage.getScene () -> Scene.getRoot () -> wyszukiwanie rekurencyjne za pomocą Parent.getChildrenUnmodifiable (). To bardzo brudny sposób. Gdyby ktoś mógł coś zasugerować lepiej - byłoby świetnie.
Alexander Kirov
Wygląda na to, że Stage.getScene (). GetRoot () to właściwy sposób! Dzięki
Alvaro,
7

Oto przykład przekazywania parametrów do dokumentu fxml przez przestrzeń nazw.

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

Zdefiniuj wartość External Textdla zmiennej przestrzeni nazw 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();
    }
}
użytkownik1503636
źródło
Należy zauważyć, że niektóre klawisze są używane wewnętrznie: np FXMLLoader.CONTROLLER_KEYWORD, FXMLLoader.LOCATION_KEY, FXMLLoader.RESOURCES_KEYi dowolny ciąg znaków używany jako wartość dla fx:idatrybutu.
fabian
Dziękuję za to, moja druga scena jest tylko kontenerem, który pokazuje tekst poprzednio pokazany na mojej głównej scenie. Teraz mogę mieć jeden plik fxml, którego mogę ponownie użyć w wielu miejscach, inicjując zawartość za pomocą zmiennych Namepace. Nie musiałem tworzyć żadnych nowych metod ani modyfikować mojego konstruktora lub inicjalizatorów - po prostu dodałem zmienną w moim FXML i dodałem jedną linię do mojego kodu fxmloader w głównym kontrolerze.
SystemsInCode,
4

To działa ..

Pamiętaj, że po pierwszym wydrukowaniu wartości przechodzącej otrzymasz null, możesz jej użyć po załadowaniu systemu Windows, to samo dla wszystkiego, co chcesz zakodować dla dowolnego innego komponentu.

Pierwszy kontroler

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

Kolejny kontroler

public void initVariable(Long id_usuario){
    this.id_usuario = id_usuario;
    label_usuario_nombre.setText(id_usuario.toString());
}
diego matos - keke
źródło
1
Działa to, gdy przekazujesz parametr z pierwszego kontrolera do drugiego, ale jak przekazać parametr z drugiego kontrolera do drugiego, to znaczy po załadowaniu pliku first.fxml.
Menai Ala Eddine - Aladdin
@XlintXms zobacz powiązane pytanie Parametr JavaFX FXML przekazywany z kontrolera A do B iz powrotem , który odpowiada na twoje dodatkowe pytanie.
jewelsea
2

Musisz utworzyć jedną klasę kontekstu.

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

Musisz tylko ustawić wystąpienie kontrolera podczas inicjalizacji za pomocą

Context.getInstance().setTabRough(this);

i możesz go używać z całej aplikacji, używając tylko

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

Teraz możesz przekazać parametr do dowolnego kontrolera z całej aplikacji.

CTN
źródło
Używamy tego podejścia i działa świetnie. Podoba mi się, że mam dostęp do danych wewnątrz konstruktora lub w metodzie inicjalizacji i nie muszę ustawiać danych w kontrolerze po jego zbudowaniu
Bob
1

Tak, możesz.
Musisz dodać w pierwszym kontrolerze:

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

Następnie w drugim zadeklaruj klienta, a następnie u dołu kontrolera:

public void setclien(Client c) {
    this.client = c;
}
Montassar Bouagina
źródło
0

Oto przykład użycia kontrolera wprowadzonego przez Guice'a.

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

Oto konkretne wdrożenie modułu ładującego:

public class ConcreteViewLoader extends GuiceFxmlLoader {

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

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

Zauważ, że ten przykład ładuje widok do środka BoarderPane, który jest korzeniem Sceny na stole montażowym. Nie ma to znaczenia dla przykładu (szczegóły implementacji mojego konkretnego przypadku użycia), ale postanowiłem go zostawić, ponieważ niektórzy mogą uznać go za użyteczny.

jenglert
źródło
-1

Możesz zdecydować się na użycie publicznej listy obserwowalnej do przechowywania danych publicznych lub po prostu utworzyć publiczną metodę ustawiania do przechowywania danych i pobierania z odpowiedniego kontrolera

Nospaniol Noah
źródło
-3

Po co odpowiadać na 6-letnie pytanie?
Jedną z najbardziej podstawowych koncepcji pracy z dowolnym językiem programowania jest nawigacja z jednego okna (formularza, strony lub strony) do drugiego. Również podczas tej nawigacji programista często chce przekazywać dane z jednego (okna, formularza lub strony) i wyświetlać lub wykorzystywać przekazane dane.
Podczas gdy większość odpowiedzi tutaj zawiera dobre lub doskonałe przykłady, jak to osiągnąć, myśleliśmy, że będziemy to robić wycięcie, dwa lub trzy
Powiedzieliśmy trzy, ponieważ będziemy nawigować między trzema (okno, formularz lub strona) i używać pojęcia zmiennych statycznych do przekazywania danych wokół (okna, formularza lub strony)
Będziemy również zawierać kod decyzyjny podczas nawigujemy

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

Uruchom kontroler

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

}

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

}    

}

Poniżej znajdują się wszystkie pliki 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>

Wektor
źródło
3
Przykro mi, ale opublikowanie setki wierszy kodu bez wyjaśnienia, co robi i dlaczego robisz to tak, jak to robisz, nie jest bardzo dobrą odpowiedzią. Ponadto opublikowany kod jest bardzo źle zorganizowany i trudny do przestrzegania.
Zephyr
Osoba pytająca nie musi być niegrzeczna. Wszyscy jesteśmy tutaj, aby się uczyć
Zeyad