Ein FXML-File mehrfach einfügen

Øtzi

Mitglied
Hallo Zusammen,

für mein Programm möchte ich ein FXML-File (FourButtons.fxml) in mein Haupt-FXML-File (Main.fxml) mehrfach einfügen. In FourButtons.fxml sind 4 Buttons in einem Grid angeordnet. Durch den Aufruf <fx:include source ="FourButtons.fxml"/> kann ich diese auch in Main.fxml einfügen. Das klappt alles wunderbar, auch wenn ich diese mehrfach einfüge.
Mein Problem ist nun, wie kann ich unterscheiden, ob ich den Button1 aus dem einem FourButtons.fxml gedrückt habe oder aus dem anderen? Den die ID-Bezeichung bleibt ja immer gleich. Ich hab dem Eingefügten ebenfalls eine ID gegeben, weiß aber nicht wie ich dies in meinem Controller einbaue.

Ich hoffe, das hier zusammen getackerte Beispiel erklärt es ein wenig.
Könnt ihr mir weiterhelfen?

Jetzt schon mal: Vielen Dank

[CODE lang="java" title="FourButtons.fxml"]<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>

<GridPane
xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/2.2"
fx:controller="Controller"
hgap="5"
vgap="5">

<children>

<Button
fx:id="button1"
prefHeight="50"
prefWidth="50"
onAction="#actionButton"
GridPane.rowIndex="0" GridPane.columnIndex="0">
</Button>

<Button
fx:id="button2"
prefHeight="50"
prefWidth="50"
onAction="#actionButton"
GridPane.rowIndex="1" GridPane.columnIndex="0">
</Button>

<Button
fx:id="button3"
prefHeight="50"
prefWidth="50"
onAction="#actionButton"
GridPane.rowIndex="0" GridPane.columnIndex="1">
</Button>

<Button
fx:id="button4"
prefHeight="50"
prefWidth="50"
onAction="#actionButton"
GridPane.rowIndex="1" GridPane.columnIndex="1">
</Button>

</children>

</GridPane>[/CODE]

[CODE lang="java" title="Main.fxml"]<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<GridPane
xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/2.2"
fx:controller="Controller"
hgap="5"
vgap="5">

<children>

<HBox
spacing="10"
alignment="bottom_right"
GridPane.rowIndex="0" GridPane.columnIndex="0">
<children>
<fx:include
fx:id="fourButtons1"
source="FourButtons.fxml"/>
</children>
</HBox>

<HBox
spacing="10"
alignment="bottom_right"
GridPane.rowIndex="1" GridPane.columnIndex="0">
<children>
<fx:include
fx:id="fourButtons2"
source="FourButtons.fxml"/>
</children>
</HBox>

</children>

</GridPane> [/CODE]

[CODE lang="java" title="Controller.java"]package test;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;

public class Controller implements Initializable {
@FXML private Button button1;

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

public void actionButton(ActionEvent event) {
if(event.getSource() == button1) {
System.out.println("Ein Button wurde betätigt.");
}
}

}[/CODE]

[CODE lang="java" title="TestProg.java"]package test;

import java.io.IOException;
import java.net.URISyntaxException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class TestProg extends Application {

@Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loaderViewMain = new FXMLLoader(getClass().
getResource("Main.fxml"));
Parent rootViewMain = loaderViewMain.load();
Scene sceneViewMain = new Scene(rootViewMain, 800, 600);
primaryStage.setTitle("TestProg");
primaryStage.setScene(sceneViewMain);
primaryStage.show();
}

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

}[/CODE]
 
Warum machst du nicht aus der FXML eine Komponente, in dem du es als fx:root definierst, dann kannst du die Komponente direkt als Control einfügen anstatt als fxml includes machst?

Ich hatte früher auch sehr viel fx:includes gemacht bis ich irgendwann das Gefühl habe, dass die Performance darunter leidet. Deswegen habe ich bei komplexen Anwendungen das jetzt vermeidet.

Ansonsten kannst du die FourButtons Fxml auch programmatisch laden, dann kriegst du die jeweiligen Controllers.

Aber warum willst du die FourButtons fxml 4 mal in die Main FXML includen?
 
Ansonsten kannst du die FourButtons Fxml auch programmatisch laden, dann kriegst du die jeweiligen Controllers.
Die kann man auch so bekommen, über @FXML FourButtonsController fourButtons2Controller 😉

@Øtzi du solltest für unterschiedliche Views unterschiedliche Controller-Klassen nutzen. In deinem Beispiel gäb es drei Instanzen von Controller, was vermutlich so nicht gewünscht ist 🙂
 
Vielen herzlichen Dank für eure schnelle Antwort.

@lam_tr:
Der Hinweis mit fx:root führt mich scheinbar weiter. Ich muss nur noch herausfinden, wie dies zu implementieren ist. Ich versuch mich da selbst erst einmal.

@mrBrown:
An sich ist das ja eine View bzw. ein Fenster. FourButtons.fxml ist nur ein Teil davon, der immer wieder auftaucht.

Zum Hintergrund:
Wenn ich z.B. das Brettspiel "Mensch ärger dich nicht" programmieren würde (ist nicht meine Absicht, aber das Spiel kennt jeder). Die vier Startfelder von den Spielern Rot, Schwarz, Gelb und Grün sind FourButtons.fxml. Diese kommen in meiner Main.fxml viermal vor. Um ein wenig Überblick zu behalten, wollte ich diese Auslagern. Da es sich um FourButtons.fxml um kein extra Fenster handelt, wollte ich auch nicht ein extra Controller erschaffen, der wieder mit dem Haupt-Controller kommunizieren muss. Oder sollte ich es doch?
Mein Plan war eigentlich, die Buttons in eine Array zu packen und diese über eine for-Schleife abzufragen. Ungefähr so (nur ein Beispiel):

[CODE lang="java" title="Steuerung-Buttons"]public void actionButton(ActionEvent event) {
for(int stein=0; stein<5; stein++) {
for(int farbe=0; farbe<5; farbe++) {
if(event.getSource() == buttons[farbe][stein]) {
System.out.println("Das ist Farbe " + farbe + " Stein " + stein);
}
}
}
}[/CODE]

Da in meinem Programm viele Buttons auftauchen, wäre dieses Vorgehen übersichtlicher und einfacher.
 
Controller legt man nicht pro Fenster, sondern pro "logischer Einheit" an – ist in dem Fall also völlig gerechtfertigt, einen extra Controller für die Buttons zu haben.

Problem ist dabei sonst auch, dass der Controller mehrmals initialisert wird. Um bei dem "Mensch ärger dich nicht"-Beispiel ziu bleiben: es gäbe dann 5 "SpielController", wovon 4 nicht wirklich funktnionieren, weil sie nur die 4 Startfelder kennen, aber sonst nichts.
 
Vielen herzlichen Dank für eure schnelle Antwort.

@lam_tr:
Der Hinweis mit fx:root führt mich scheinbar weiter. Ich muss nur noch herausfinden, wie dies zu implementieren ist. Ich versuch mich da selbst erst einmal.

@mrBrown:
An sich ist das ja eine View bzw. ein Fenster. FourButtons.fxml ist nur ein Teil davon, der immer wieder auftaucht.

Zum Hintergrund:
Wenn ich z.B. das Brettspiel "Mensch ärger dich nicht" programmieren würde (ist nicht meine Absicht, aber das Spiel kennt jeder). Die vier Startfelder von den Spielern Rot, Schwarz, Gelb und Grün sind FourButtons.fxml. Diese kommen in meiner Main.fxml viermal vor. Um ein wenig Überblick zu behalten, wollte ich diese Auslagern. Da es sich um FourButtons.fxml um kein extra Fenster handelt, wollte ich auch nicht ein extra Controller erschaffen, der wieder mit dem Haupt-Controller kommunizieren muss. Oder sollte ich es doch?
Mein Plan war eigentlich, die Buttons in eine Array zu packen und diese über eine for-Schleife abzufragen. Ungefähr so (nur ein Beispiel):

[CODE lang="java" title="Steuerung-Buttons"]public void actionButton(ActionEvent event) {
for(int stein=0; stein<5; stein++) {
for(int farbe=0; farbe<5; farbe++) {
if(event.getSource() == buttons[farbe][stein]) {
System.out.println("Das ist Farbe " + farbe + " Stein " + stein);
}
}
}
}[/CODE]

Da in meinem Programm viele Buttons auftauchen, wäre dieses Vorgehen übersichtlicher und einfacher.
Ist an sich nicht schwer den auf fx:root zu stellen. Schau mal hier in der ersten Antwort vom Post kannst du ein bisschen abgucken https://stackoverflow.com/questions/23600926/how-to-understand-and-use-fxroot-in-javafx
 
Beim Durchsuchen des Forums, habe ich etwas gefunden. Mit Hilfe der unteren Codezeile, ist es möglich die fx:id von fx:include abzufragen.

Java:
String farbe = ((Node) event.getSource()).getParent().getId();

Dadurch würde die Button-Abfrage im Controller wie folgt aussehen (mit der Voraussetzung die IDs haben den Namen der jeweiligen Farbe):

Java:
public void actionButton(ActionEvent event) {
    String farbe = ((Node) event.getSource()).getParent().getId();
    for(int stein=0; stein<5; stein++) {
        if(event.getSource() == buttons[stein]) {
            System.out.println("Das ist Farbe " + farbe + " Stein " + stein);
        }
    }
}

Durch diese wenigen Zeilen, könnte man am Beispiel von "Mensch ärgere dich nicht" alle 16 Startfelder auf einem Schlag abfragen, ohne zusätzliche Controller. Mein eigentliches Program hat noch viel mehr Buttons und so würde mein Code etwas übersichtlicher sein.
Das ist natürlich nur meine Meinung. Ist das Ganze ein sauberes Vorgehen? Wie würdet ihr es konkret machen (mit Root schaue ich mir noch mal an).
 

Zurück
Oben