JavaFX GUI Elemente einer anderen (FXML)Klasse ansprechen

Nemesys88

Bekanntes Mitglied
Hallo zusammen!

Bin noch JavaFX - Neuling und habe jetzt ein Problem:

Angenommen ich habe 2 Fenster mit entsprechenden Controllern:

A.fxml mit A.java (Controller) und
B.fxml mit B.java (Controller)

Jetzt hab ich in der A.fxml eine Combobox und in der B.fxml einen Button "Bearbeiten".

Ich möchte jetzt gerne im Fenster B auf den Button klicken und eine Action hervorrufen, die in Fenster A der ComboBox ein Item hinzufügt.

Leider bringt es nichts in B.java einfach:

b_bearbeiten.setOnAction(e -> {
A.tueEtwas();
});

aufzurufen und in

A.java:

public void tueEtwas()
{
cb.getItems().add("neues Item");
}

Der Versuch endet trotzdem in einer NullPointerException - obwohl der A-Controller ja seine GUI-Elemente kennen müsste....

Also wie mache ich sowas, so generell?

Danke schonmal!
 
Wenn beide Controller geladen wurden(über den FXMLLoader), hat man die Möglichkeit den Controller(loader.getController()) als Referenz weiterzugeben und auf diesen Operationen auszuführen.
 
Okay, jetzt hab ich in der Buttonverarbeitung (B.java) folgendes stehen:

Java:
        b_bearbeiten.setOnAction(e -> {
          
            FXMLLoader loader = new FXMLLoader(getClass().getResource("A.fxml"));
            try {
                loader.load();
            } catch (Exception e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            A controller = loader.getController();
            controller.tueEtwas();
        
        });

und im Controller von Klasse A steht:

Java:
public void tueEtwas()
{
System.out.println("Test");
cb.getItems().add("neues Item");
}

Er führt jetzt alles ohne zu meckern aus, die Testausgabe kommt, aber die ComboBox reagiert noch nicht...
 
Weil du wahrscheinlich die FXML schon mal geladen hast. Du musst aber den loader verwenden den du zuerst genutzt hast und nicht neu laden!
 
Ja, die wird in der Main.java geladen. Die sieht so aus (gekürzt):

Java:
        Parent root = FXMLLoader.load(getClass().getResource("A.fxml"));

        Scene scene = new Scene(root);

Weiss grade nicht, wie ich das jetzt codetechnisch umsetzen muss... :/
 
Ja und jetzt machst du sowas wie:
Java:
FXMLLoader loader = new FXMLLoader(getClass().getResource("A.fxml"));
AController aController = loader.getController();
FXMLLoader loaderB = ...; // Load B
BController bController = loader.getController();
bController.setAController(aController);

Und schon weiß dein Controller von B welcher der richtige für A ist.
 
Java:
        b_bearbeiten.setOnAction(e -> {
           
            loader = new FXMLLoader(getClass().getResource("A.fxml"));
            AController aController = loader.getController();
            loaderB = new FXMLLoader(getClass().getResource("B.fxml"));
            BController bcontroller = loader.getController();
            bcontroller.setAController(aController);
              
    }

private void setAController(AController aController) {
        loaderB.setController(aController);
    }

Irgendwie kann ich auch nicht glauben, dass das so kompliziert sein soll die ComboBox aus'm AController anzusprechen...

zumal ich nach wie vor nicht verstehe, warum auch der AController die Komponente nicht mehr ansprechen kann wenn ich aus B.java "A.tueEtwas()" aufrufe, findet die Verarbeitung ja in meinem AController (A.java) statt... (siehe start-post)
 
Ein kleine Beispiel dazu:
Java:
import javafx.event.ActionEvent;
import javafx.fxml.FXML;

public class AController {

   private BController bController;
   private int counter;

   public void setbController(BController bController) {
     this.bController = bController;
   }

   @FXML
   private void initialize() {
     counter = 0;
   }

   @FXML
   private void changeLabel(ActionEvent e) {
     bController.setText("Counter: " + counter++);
   }

}
Java:
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class BController {

   @FXML
   private Label textLabel;

   public void setText(String text) {
     textLabel.setText(text);
   }
}
Java:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;


public class Main extends Application {@Override
   public void start(Stage primaryStage) {
     try {
       FXMLLoader aLoader = new FXMLLoader(getClass().getResource("A.fxml"));
       Parent aRoot = aLoader.load();
       AController aController = aLoader.getController();

       FXMLLoader bLoader = new FXMLLoader(getClass().getResource("B.fxml"));
       Parent bRoot = bLoader.load();
       BController bController = bLoader.getController();

       aController.setbController(bController);
       //
       Stage bStage = new Stage();
       bStage.setX(100);
       bStage.setY(150);
       bStage.setScene(new Scene(bRoot, 150, 100));
       bStage.show();

       primaryStage.setScene(new Scene(aRoot, 150, 100));
       primaryStage.setX(400);
       primaryStage.setY(150);
       primaryStage.show();
     } catch (Exception e) {
       e.printStackTrace();
     }
   }

   public static void main(String[] args) {
     launch(args);
   }
}
XML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<BorderPane
  xmlns="http://javafx.com/javafx/8.0.40"
  xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.AController">
  <center>
  <Button fx:id="clickMe" mnemonicParsing="false" onAction="#changeLabel" text="Click me!" BorderPane.alignment="CENTER" />
  </center>
</BorderPane>
XML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<BorderPane
  xmlns="http://javafx.com/javafx/8.0.40"
  xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.BController">
  <center>
  <Label fx:id="textLabel" text="&lt;EMPTY&gt;" />
  </center>
</BorderPane>
 
Danke erstmal.

Ich hab versucht das nachzubauen, komme immernoch auf eine Nullpointer-Exception..

Hier mein aktueller Code:

Klasse B.java:

Java:
    private FXMLLoader loader;
    private A mfcontrol;

            loader = new FXMLLoader(getClass().getResource("A.fxml"));
            mfcontrol = loader.getController();
            mfcontrol.setMain(this);
            mfcontrol.hasResult("Administrator");

(hasResult ist dann die Methode wo im Hauptfenster etwas mit den GUI-Komponenten passieren soll)

Klasse A.java:

Java:
        @SuppressWarnings("unused")
    private Search searchcontrol;

public void setMain(Search searchcontroller) {
        this.searchcontrol = searchcontroller;
    }

Die NullPointerException kommt in der Zeile "mfcontrol.setMain(this)"...

Edit: Dazu gibt es die Main.java mit:
Java:
    Parent root = FXMLLoader.load(getClass().getResource("A.fxml"));

     Scene scene = new Scene(root);
 
Zuletzt bearbeitet:
Das load() hab ich ja rausgenommen weil ich die A.fxml eigentlich schon beim Programmstart lade... (hab es 2 Posts weiter oben editiert damit der Zusammenhang klar wird). (Und wenn ich trotzdem noch ein load() dazwischen mache, dann macht er halt die Testausgaben aber arbeitet nicht mit den Komponenten weil ich sozusagen ne neue Instanz vom Controller erzeuge) Nur wie komm ich denn an die alte Instanz dran? -.-
 
Zuletzt bearbeitet:
Habe mich jetzt einer anderen Lösungsmöglichkeit bedient, glaub die Idee hatte ich sogar mal von dir bekommen, in einem anderen Projekt 😉

Main.java:

Java:
FXMLLoader loader = A.getInstance();
Parent root = loader.load();
Scene scene = new Scene(root);

B.java:

Java:
loader = A.getInstance();
mfcontrol = loader.getController();
mfcontrol.hasResult("Teststring");

A.java:

Java:
public static FXMLLoader getInstance() 
{ if (INSTANCE == null) { INSTANCE = new FXMLLoader(A.class.getResource("A.fxml")); } 
return INSTANCE; }

Danke nochmals für die Hilfe und für deine Geduld... 😀
 

Zurück
Oben