JavaFX JavaFX in mehrere Controller

macaubas

Mitglied
Hallo,
ich definiere FXML innerhalb eines FXLM-Files. Das funktioniert recht gut mit
<fx:include fx:id="xxx" source="FXMLxxx.fxml" />
Beim Zugriff auf Nodes von diesem auf auf weitere FXML hatte ich erhebliche Probleme.
Die Lösung vom User Knoten (JavaFX in mehrere Controller aufteilen) funktioniert, scheint mir aber recht mühsam mit 100 und mehr Nodes.
Deshalb habe ich eine andere Lösung gesucht, welche ich hier zur Diskussion stellen will.
meine Applikation:
- ich öffne mehrere gleiche Fenster (haben den gleichen Controller)
- ich öffne verschiedene Fenster (haben verschiedene Controller)

da die FXLM in der Reihenfolge FXMLTab1, FXMLTab2, FXMLMain abgearbeitet werden generiere ich nach dem laden von ein Event auf Tab1 und Tab2.
Damit habe ich auch alle Components in Tab1 und Tab2.

Vielleicht hat jemand von euch eine bessere Lösung ??

FXMLMain.xml:
XML:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane fx:id="AnchorPane" id="AnchorPane" prefHeight="329.0" prefWidth="308.0" xmlns="http://javafx.com/javafx/8.0.60"
            xmlns:fx="http://javafx.com/fxml/1"   fx:controller="mytest.FXMLMainController" >
   <children>
      <TabPane layoutX="26.0" layoutY="10.0" prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE">
        <tabs>
          <Tab text="tab1">
            <content>
              <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="106.0" prefWidth="200.0">
                     <children>
                       <fx:include fx:id="tab1" source="FXMLTab1.fxml" /> 
                     </children>
                  </AnchorPane>
            </content>
          </Tab>
          <Tab text="tab2">
            <content>
              <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
                     <children>
                      <fx:include fx:id="tab2" source="FXMLTab2.fxml" /> 
                     </children>
                  </AnchorPane>
            </content>
          </Tab>
        </tabs>
      </TabPane>
      <Button fx:id="but_main" layoutX="126.0" layoutY="290.0" mnemonicParsing="false" onAction="#do_something" text="mainbutton" />
   </children>
</AnchorPane>

FXMLMainController.java:
Java:
package mytest;

import java.net.URL;
import java.util.HashMap;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;

public class FXMLMainController implements Initializable {

    private HashMap<String, Node> Comps = new HashMap<>();
    @FXML private AnchorPane AnchorPane;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        Comps = (HashMap) Cla1.LeseComps(AnchorPane).clone();
        ((Button) Comps.get("B1")).fire();
        ((Button) Comps.get("B2")).fire();
    }

    @FXML
    private void do_something(ActionEvent event) {
    ((Label)Comps.get("label1")).setRotate(((Label)Comps.get("label1")).getRotate()+20);    
    }
}
FXMLTab1.fxml:
XML:
<?xml version="1.0" encoding="UTF-8"?>

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

<AnchorPane id="AnchorPane" fx:id="tab1" prefHeight="126.0" prefWidth="127.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mytest.FXMLTab1Controller">
   <children>
      <Label fx:id="label1" layoutX="44.0" layoutY="14.0" prefHeight="17.0" prefWidth="104.0" text="Label" />
      <Button fx:id="B1" layoutX="14.0" layoutY="87.0" mnemonicParsing="false" onAction="#ini_Comp" text="B1" />
      <TextField fx:id="text1" layoutX="14.0" layoutY="51.0" onAction="#rot_but_main" prefHeight="25.0" prefWidth="126.0" text="Text" />
   </children>
</AnchorPane>
FXMLTab1Controller.java
Java:
package mytest;

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

public class FXMLTab1Controller implements Initializable {

    @FXML private Button B1;
    @FXML private TextField text1;
    private HashMap<String, Node> Comps = new HashMap<>();

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        B1.setVisible(false);
    }
    @FXML private void ini_Comp(ActionEvent event) {
        Comps = (HashMap) Cla1.Comps.clone();
    }
    @FXML private void rot_but_main(ActionEvent event) {
        ((Button) Comps.get("but_main")).setRotate(((Button) Comps.get("but_main")).getRotate() + 20);
    }
}
FXMLTab2.fxml:
XML:
<?xml version="1.0" encoding="UTF-8"?>

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

<AnchorPane id="AnchorPane" fx:id="tab2" prefHeight="135.0" prefWidth="118.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mytest.FXMLTab2Controller">
   <children>
      <CheckBox fx:id="checkbox1" layoutX="23.0" layoutY="25.0" mnemonicParsing="false" onAction="#rot_text1" text="CheckBox" />
      <Button fx:id="B2" layoutX="23.0" layoutY="83.0" mnemonicParsing="false" onAction="#ini_Comp" text="B2" />
   </children>
</AnchorPane>
FXMLTab2Controller.java
Java:
package mytest;

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

public class FXMLTab2Controller implements Initializable {

    @FXML private Button B2;
    @FXML private CheckBox checkbox1;
    private HashMap<String, Node> Comps = new HashMap<>();

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        B2.setVisible(false);
    }
    @FXML private void ini_Comp(ActionEvent event) {
        Comps = (HashMap) Cla1.Comps.clone();
    }
    @FXML private void rot_text1(ActionEvent event) {
        ((TextField) Comps.get("text1")).setRotate(((TextField) Comps.get("text1")).getRotate() + 20);
    }
}
Cla1.java
Java:
package mytest;
import java.util.HashMap;
import javafx.scene.Node;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

public class Cla1 {

    static public HashMap<String, Node> Comps = new HashMap<>();

    public static HashMap LeseComps(Node N) {
        Comps.clear();
        LeseCompsSub(N);
        return Comps;
    }

    private static void LeseCompsSub(Node N) {
        int anz = ((AnchorPane) N).getChildren().size();
        for (int i = 0; i < anz; i++) {
            Node N1 = ((AnchorPane) N).getChildren().get(i);
            switch (N1.getClass().getSimpleName()) {
                case "AnchorPane": {
                     LeseCompsSub(((AnchorPane) N1));
                    break;
                }
                case "TabPane": {
                    int CNT = ((TabPane) N1).getTabs().size();
                    for (int j = 0; j < CNT; j++) {
                        LeseCompsSub((((TabPane) N1).getTabs().get(j)).getContent());
                        //Comps = LeseCompsSub((((TabPane) ((AnchorPane) N).getChildren().get(i)).getTabs().get(j)).getContent(), Comps);    

                    }
                    break;
                }
                case "SplitPane": {
                    int CNT = ((SplitPane) N1).getItems().size();
                    for (int j = 0; j < CNT; j++) {
                        LeseCompsSub(((SplitPane) N1).getItems().get(j));
                    }
                    break;
                }
                case "VBox": {
                    int CNT = ((VBox) N1).getChildren().size();
                    for (int j = 0; j < CNT; j++) {
                        if (((VBox) N1).getChildren().get(j).getClass().getSimpleName().equals("AnchorPane")) {
                            LeseCompsSub(((VBox) N1).getChildren().get(j));
                        } else {
                            Comps.put(((VBox) N1).getChildren().get(j).getId(), ((VBox) N1).getChildren().get(j));
                        }
                    }
                    break;
                }
                case "HBox": {
                    int CNT = ((HBox) N1).getChildren().size();
                    for (int j = 0; j < CNT; j++) {
                        if (((HBox) N1).getChildren().get(j).getClass().getSimpleName().equals("AnchorPane")) {
                            LeseCompsSub(((HBox) N1).getChildren().get(j));
                        } else {
                            Comps.put(((HBox) N1).getChildren().get(j).getId(), ((HBox) N1).getChildren().get(j));
                        }
                    }
                    break;
                }
                default:
                    Comps.put(((AnchorPane) N).getChildren().get(i).getId(), ((AnchorPane) N).getChildren().get(i));
                    switch (((AnchorPane) N).getChildren().get(i).getClass().getSimpleName()) {
                        case "TextArea": {
                            ((TextArea) ((AnchorPane) N).getChildren().get(i)).setText("gaga");
                            break;
                        }
                    }
            }
        }
    }
}
 

mrBrown

Super-Moderator
Mitarbeiter
Was für ein Problem versuchst du denn zu lösen?


Der Code sieht schon sehr ... schrecklich aus. Grad wenn das größer wird stell ich mir das ziemlich unwartbar vor
 

mrBrown

Super-Moderator
Mitarbeiter
Hier mal ein Beispiel wie man das sauberer lösen kann: https://gitlab.com/mrBrown/fxml-demo-1

Im wesentlichen:
  • Zustand der View (zB die Rotation) wird in einem View-Model gehalten
  • Abhängige Controls binden sich jeweils daran (direkt in FXML)
  • Actions verändern das View-Model
  • Die Controller müssen jeweils nur das ViewModel (und das kann man noch aufteilen und durch Interfaces abstrahieren) und ihre eigenen Controls kennen
Du braucht dann "Cla1" nicht mehr und man muss nicht mehr wild mit Strings und Casts hantieren.
 

macaubas

Mitglied
Hallo MrBrown,
ich danke dir erstmals für deinen Einsatz hier.
Meine Statements zum Rotieren waren lediglich zur Visualisierung der richtigen Funktionalität gedacht, machen ja sonnst wenig Sinn.
Ich habe bei meiner Applikation rund 40 verschiedene Fenster (wie FXMLMains.fxml) und hier innerhalb von SplitPanes je 25 verschiedene included Panes (wie FXMLTab1.fxml) mit ca 60 Controls (Labels, TexFields, ..) und 30 included Panes (wie FXMLTab2.fxml) mit ca 100 Controls.
Mit einem Iterator auf Comps kann ich nun einfach die Felder lesen/schreiben und eine DB manipulieren.
Bei deiner Lösung wären das ja wohl ca 45'000 Statements zu schreiben, macht 2 Monate Arbeit und 2 Monate Tests.
Ich bin ja auch nicht zufrieden, dass ich die Comps aus FXML-Controller nicht überall lesen kann und wäre für einen besseren Lösungsvorschlag glücklich.
Gruss Macaubas
 

lam_tr

Top Contributor
Hallo MrBrown,
ich danke dir erstmals für deinen Einsatz hier.
Meine Statements zum Rotieren waren lediglich zur Visualisierung der richtigen Funktionalität gedacht, machen ja sonnst wenig Sinn.
Ich habe bei meiner Applikation rund 40 verschiedene Fenster (wie FXMLMains.fxml) und hier innerhalb von SplitPanes je 25 verschiedene included Panes (wie FXMLTab1.fxml) mit ca 60 Controls (Labels, TexFields, ..) und 30 included Panes (wie FXMLTab2.fxml) mit ca 100 Controls.
Mit einem Iterator auf Comps kann ich nun einfach die Felder lesen/schreiben und eine DB manipulieren.
Bei deiner Lösung wären das ja wohl ca 45'000 Statements zu schreiben, macht 2 Monate Arbeit und 2 Monate Tests.
Ich bin ja auch nicht zufrieden, dass ich die Comps aus FXML-Controller nicht überall lesen kann und wäre für einen besseren Lösungsvorschlag glücklich.
Gruss Macaubas
Hi Macaubas,

Ich habe eine andere Frage, wie lässt sich die Anwendung starten. Ist sie überhaupt noch startbar, so viele Fxmls wie du da hast, kann ich mir schon vorstellen dass es aufwendig sein kann.

Kannst du eventuell dein Anwendungsfall ein bisschen genauer erklären? Ich kann mir gerade nicht wirklich viel darunter vorstellen.

Grüße
Lam
 

mrBrown

Super-Moderator
Mitarbeiter
Meine Statements zum Rotieren waren lediglich zur Visualisierung der richtigen Funktionalität gedacht, machen ja sonnst wenig Sinn.
Schon klar, ich hab die auch nur übernommen, weil du sie schon hattest (und halt zusätzlich etwas mehr eingebaut).

Mit einem Iterator auf Comps kann ich nun einfach die Felder lesen/schreiben und eine DB manipulieren.
Bei deiner Lösung wären das ja wohl ca 45'000 Statements zu schreiben, macht 2 Monate Arbeit und 2 Monate Tests.

Hast du die beiden Varianten mal verglichen?

  • Deine Lösung braucht mehr Code (in diesem Beispiel braucht deins knapp 50% mehr Code)
  • Da du Tests ansprichst: deine Lösung ist nicht sinnvoll mit Unit-Tests abdeckbar – alles greift auf eine gemeinsame statische Variable zu, die Units sind also das Gegenteil von unabhängig. In meinem sind einmal alle Controller unabhängig und einzeln test-/ausführbar, und zusätzlich ist die im ViewModel stehende Logik mit simplen Unit-Tests abdeckbar
  • Deine Lösung greift auf alle Felder statisch über die ID zu, die als String (also ohne statische Überprüfbarkeit) quer über das gesamte Programm verteilt sind – jede Klasse kennt also (implizit über IDs) alle anderen Klassen, das kann doch gar nicht skalieren und ist völlig unmodularisierbar
  • Deine Cla1 bricht schon, wenn man ein minimal anderes Layout wählt – einfach nur die FXML ein bisschen ändern, damit zwei Dinge nebeneinander statt untereinander stehen, schon wird direkt ein NullPointer beim Start geworfen
Also mMn im gesamten: kürzer, testbar, unabhängige Klassen (und damit modularisierbar), wartbarer und stabiler.

Vielleicht übersehe ich den großen Vorteil deiner Lösung (dein Beispiel versteckt den dann allerdings ziemlich gut ;) ) – vielleicht hast du noch mal ein anderes Beispiel oder eine Erklärung, wo deines einen Vorteil bietet?



(Oh, bevor ich's vergesse: da ich static kritisiere und trotzdem selber static für das ViewModel nutze: wie dabei steht gehört das per DI injiziert, wollte nur in das Beispiel jetzt kein Framework mit reinziehen, und das sollte natürlich nicht eine Klasse sein, sondern in X kohärente Klassen geteilt werden, auch aufgrund der Kürze (und der fehlenden Problemdomäne) nicht gemacht)
 

mrBrown

Super-Moderator
Mitarbeiter
Ich habe eine andere Frage, wie lässt sich die Anwendung starten. Ist sie überhaupt noch startbar, so viele Fxmls wie du da hast, kann ich mir schon vorstellen dass es aufwendig sein kann.
In wie fern meinst du aufwändig?

Ob man jetzt eine oder zehn per include nutzt, macht doch keinen wirklichen Unterschied, zumindest aus Programmiersicht?
 

lam_tr

Top Contributor
In wie fern meinst du aufwändig?

Ob man jetzt eine oder zehn per include nutzt, macht doch keinen wirklichen Unterschied, zumindest aus Programmiersicht?
Ich meine es von Javafx aus, da die Fxmls per Reflections eingelesen werden, ist das so oder so schon in der Regel langsamer wie Programmtische Vorgehensweise. Ich habe auch schon Anwendungen mit 2-3 includes mit komplexen formen gehabt, die Anwendung hatte auch um einiges länger gebraucht wie ohne includes.
 

dzim

Top Contributor
Ich glaube, @lam_tr war wegen der erwähnung von dutzenden von "Fenstern" verwirrt. Würde mich auch verwirren, wenn eine Applikation so viel Fenster aufmacht. Ich glaube aber der OP meinte damit nur Views innerhalb eines OS-Fensters (win32, Cocoa, GTK, etc.).

Ich bin mittlerweile auch von FXMLs abgerückt. Sie sind praktisch, UI von Controller zu trennen, bringen aber im schlimmsten Fall auch Performanceeinbußen mit.
 

macaubas

Mitglied
Hallo lam_tr,
ich habe in JavaFX14, die Installation in diesem Forum mein Vorgehen beschrieben.
MacaubasMain.java beinhaltet das Menü welches beliebig erweitert werden kann. Window starteAx(xxx) startet je ein jfxtra-Fenster wie wir das z.B. von EXCEL kennen. Damit sind ja nicht alle Fenster gleichzeitig offen.
Wenn du nicht weiterkommst, so melde dich nochmals.
 

macaubas

Mitglied
Hallo mrBrown,
ich teile deinen Meinung absolut nicht. CLA1 ist weitgehend vollständig und kann bei völlig verschiedenen FXMLMain aufgerufen werden.
Leider ist sie statisch und ich muss sie mit Comps = (HashMap) Cla1.LeseComps(AnchorPane).clone(); kopieren. Dies deshalb, damit ich sie nochmals für Tab1 und Tab2 lesen kann. Somit ist die HashMap in FXMLMain hier nicht mehr statisch und für jedes Fenster eineindeutig.
Jedenfalls hat jeder HashMap-Key immer den korrekten Node.
- Bei mir erhalte ich keine Exception bei CLA1 bei massiv verschiedenen Layouts (warum auch).
- auch bei sehr vielen Controls wird der Code nicht wesentlich grösser da CLA1 so bleibt. Damit ist dein Code schon massiv aufwändiger.

Ich beanspruche hier ja keine Absolution, Das halten der HashMap in Tab1 und Tab2 gefällt mir ja auch nicht
 

mrBrown

Super-Moderator
Mitarbeiter
Leider ist sie statisch und ich muss sie mit Comps = (HashMap) Cla1.LeseComps(AnchorPane).clone(); kopieren

Sie muss übrigens nicht statisch sein und auch das ganze Klonen kann man weglassen. Man kann die über ganz normale Methodenaufrufe dorthin reichen, wo man sie braucht. Das Klone kann man auch einfach weglassen – das ist vor allem nur Symptombekämpfung, weil jeder von überall drauf zugreifen kann, muss man defensive Kopien antworten.

Dies deshalb, damit ich sie nochmals für Tab1 und Tab2 lesen kann.
Sie wird doch gar nicht nochmal gelesen, nur kopiert? Und das passiert auf eine völlig umständliche Art und Weise.

In jeder Komponente liegt ein Button, den du direkt auf unsichtbar schaltest, damit du dann den Button aus einer anderen Komponente feuern kannst, um zum Start Code in dessen Event-Handler auszuführen. Von hinten durch die Brust in's Auge wäre noch eine mehr als nette Umschreibung


- Bei mir erhalte ich keine Exception bei CLA1 bei massiv verschiedenen Layouts (warum auch).
Weil deine CLA1 Fehler enthält. Mit der von dir hier geposteten wirft es zB mit diesem FXMLTab1.fxml Fehler:
Java:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" fx:id="tab1" prefHeight="126.0" prefWidth="127.0" xmlns="http://javafx.com/javafx/8"
            xmlns:fx="http://javafx.com/fxml/1" fx:controller="mytest.FXMLTab1Controller">
    <HBox>
        <VBox>
            <Label fx:id="label1" layoutX="44.0" layoutY="14.0" prefHeight="17.0" prefWidth="104.0" text="Label"/>
            <Button fx:id="B1" layoutX="14.0" layoutY="87.0" mnemonicParsing="false" onAction="#ini_Comp" text="B1"/>
        </VBox>
        <TextField fx:id="text1" layoutX="14.0" layoutY="51.0" onAction="#rot_but_main" prefHeight="25.0" prefWidth="126.0" text="Text"/>
    </HBox>
</AnchorPane>

Natürlich kann man die Fehler alle relativ einfach beheben – allerdings kannst du nichts davon sinnvoll mit automatisierten Unit-Tests abdecken.

- auch bei sehr vielen Controls wird der Code nicht wesentlich grösser da CLA1 so bleibt. Damit ist dein Code schon massiv aufwändiger.
Wenn man Controls ohne Verhalten hinzufügt, wird der Code nicht größer. Und Überraschung, das ist bei meiner Variante genauso ;)

Mehr Code wird es erst, wenn Controls Verhalten haben sollen. Entsprechende Action-Handler braucht es dann in beiden Varianten.

Was einen Unterschied macht, ist die Logik. Bei dir steckt die direkt im Action-Handler und manipuliert direkt die entsprechenden Komponenten. Bei mir ist die Logik ausgelagert – braucht etwas mehr Code, hat aber eben auch Vorteile:

Deine Variante ist absolut nicht testbar. Die gesamte GUI muss aufgebaut werden, damit überhaupt irgendetwas getestet werden kann. Dann kann man das ganze nur über die GUI direkt testen, die Logik (in deinem Beispiel das drehen um 20 Grad) ist nicht unabhängig von der GUI – automatisiertes Testen der Einzelnen Komponenten nicht möglich.

In meiner Variante kann man dagegen jede einzelne Komponente einzeln testen: sowohl die Logik einzeln (Rotation wird erhöht um 20 Grad), die Kommunikation Button<->Logik (Klick auf den Button erhöht Rotation um 20 Grad), Kommunikation Label<->Logik (Änderung der Rotation rotiert den Button auch wirklich) und das gesamte (Klick auf Button rotiert Label).


BTW: deine Map kann man im wesentlichen auch durch Node#lookup ersetzen, man muss dann nur den Root rumreichen.
 

mrBrown

Super-Moderator
Mitarbeiter
Um das aber noch einmal kurz zusammenzufassen, was für mich die Probleme bei der Lösung sind und Diskussionswerte Punkte sind, würde mich freuen wenn du jeweils drauf eingehst :)

  1. Absolut keine Möglichkeit für automatisierte Unit-Tests: Verhalten direkt in Action-Handlern manipuliert direkt die Componenten
  2. Geringe Wartbarkeit und Stabilität: kleine Änderungen brechen direkt das ganze Programm
  3. Keine Möglichkeit, das ganze zu modularisieren: alle Komponenten kennen sich über die ID gegenseitig, man kann sie also nicht voneinander trennen und zB unterschiedliche Views in unterschiedliche Module auslagern
  4. Und bedingt dadurch auch schlechte Skalierbarkeit: für so wenige Controls kann ich mir noch vorstellen, dass man den Überblick behält. Bei allerdings hunderten Views halte ich es zumindest für mich völlig unmöglich, so den Überblick zu behalten

Und besonders würde mich auch interessieren, wo du bei mir den massiv aufwändigeren Code siehst? Grad im Zusammenhang damit, dass du offensichtlich bei meiner Variante deutlich mehr Bedarf für Tests siehst, während in deiner Variante nicht mal eine Möglichkeit für sinnvolle Tests existiert. (Oder ich übersehe das, würde mich sehr interessieren wie du beide Varianten Testen würdest).
 

macaubas

Mitglied
Hallo MrBrown,
ich hatte nun endlich Zeit, deine Variante auszutesten.
Da funktioniert ja etwas wesentliches nicht. Ich benutze jfxtras und habe in meiner Applikation mehrere Fenster gleichzeitig offen (auch mit dem gleichen Controller). Es werden jeweils alle Controls gedreht und nicht nur jene im aktuelle Fenster. Das war bei meiner Variante ebenfalls so, gerade deshalb muss ich die Comps clonen, sonst habe ich den gleichen Effekt.
Aber du hattest recht, meine CLA1 hatte Fehler, deshalb nun hier ein neuer Anlauf mit verschachtelten HBox und VBox.

Die Lösung mit dem fire Event auf Tab1 und Tab2 gefällt mir natürlich auch nicht, vielleicht hat ja jemand anders eine besser Variante.

Aber ich danke dir für deinen Einsatz

die neue CLA1.java
Java:
public class Cla1 {

    static public HashMap<String, Node> Comps = new HashMap<>();

    public static HashMap LeseComps(Node N) {
        Comps.clear();
        LeseCompsSub(N);
        return Comps;
    }

    public static HashMap LeseCompsSub(Node N) {
        int CNT = 0;
        switch (N.getClass().getSimpleName()) {
            case "AnchorPane": {
                CNT = ((AnchorPane) N).getChildren().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub(((AnchorPane) N).getChildren().get(i));
                }
                break;
            }
            case "HBox": {
                CNT = ((HBox) N).getChildren().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub(((HBox) N).getChildren().get(i));
                }
                break;
            }
            case "VBox": {
                CNT = ((VBox) N).getChildren().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub(((VBox) N).getChildren().get(i));
                }
                break;
            }
            case "TabPane": {
                CNT = ((TabPane) N).getTabs().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub((((TabPane) N).getTabs().get(i)).getContent());
                }
                break;
            }
            case "SplitPane": {
                CNT = ((SplitPane) N).getItems().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub(((SplitPane) N).getItems().get(i));
                }
                break;
            }
            default: {
                Comps.put(N.getId(), N);
                System.out.println(N.getId());
            }
        }
        return Comps;
    }
 

mrBrown

Super-Moderator
Mitarbeiter
Schade, dass du auf keinerlei Kritikpunkte an deiner Lösung eingehst.

Da funktioniert ja etwas wesentliches nicht. Ich benutze jfxtras und habe in meiner Applikation mehrere Fenster gleichzeitig offen (auch mit dem gleichen Controller). Es werden jeweils alle Controls gedreht und nicht nur jene im aktuelle Fenster.
Wenn du mehrere unterschiedliche Zustände benötigst, ist die einfache Lösung: mehrere Zustände erstellen, und jedem Fenster seinen eigenen geben, das funktioniert völlig problemlos ;)

Die Lösung mit dem fire Event auf Tab1 und Tab2 gefällt mir natürlich auch nicht, vielleicht hat ja jemand anders eine besser Variante.
Wenn du mit einem anderen Controller was machen musst, greif doch einfach direkt auf den jeweiligen Controller zu. Der Umweg über eine Action ist wirklich in jeder Hinsicht unschön.

die neue CLA1.java
Was passiert eigentlich, wenn ich jetzt ein BorderPane einbaue? :)
 

macaubas

Mitglied
Hallo mrBrown,
ja ich werde meine Lösung sicher noch kritisch hinterfragen und zur gegebene Zeit hier schreiben, aber vorerst muss ich deine Lösung zum laufen bringen:

- ich habe da von einem Fenster mit gleichem Controller mehrere gleichzeitig offen, es könnten ja durchaus 10 sein. Ich habe da deinen Vorschlag
nicht verstanden, muss ich diese dann selber verwalten und wenn ja, wie ?

- die Comps in FXMLMainControlle ist bei mir nicht mehr static. Jedes Fenster hat dadurch seine eigene Comps. Deshalb kann ich von FXMLTab1Controller nicht darauf zugreifen. Habe ich da was falsches verstanden ?

- meine CLA1 ist sicher nicht vollständig, ist auf meine Bedürfnisse zugeschnitten. Jederman kann hier auch ergänzen.
Gruss
Macaubas
 

mrBrown

Super-Moderator
Mitarbeiter
ja ich werde meine Lösung sicher noch kritisch hinterfragen und zur gegebene Zeit hier schreiben, aber vorerst muss ich deine Lösung zum laufen bringen:
In "meiner" Lösung gibts keine Comps ;)

- ich habe da von einem Fenster mit gleichem Controller mehrere gleichzeitig offen, es könnten ja durchaus 10 sein. Ich habe da deinen Vorschlag
nicht verstanden, muss ich diese dann selber verwalten und wenn ja, wie ?
Jeder Controller kann auf seine "Untercontroller" zugreifen. enauso wie es einen Baum aus Nodes gibt, gibt es auch einen Baum aus Controllern. Und die Kind-Controller kannst du, genauso wie Nodes, mit @FXML injecten lassen. Selber verwalten musst du da nicht wirklich was, das verknüpfen übernimmt JavaFX für dich.

- die Comps in FXMLMainControlle ist bei mir nicht mehr static. Jedes Fenster hat dadurch seine eigene Comps. Deshalb kann ich von FXMLTab1Controller nicht darauf zugreifen. Habe ich da was falsches verstanden ?

Nein, du musst jetzt halt selbst dafür sorgen, dass jeder Controller die entsprechenden Comps bekommt. Mit static fängst du dir nur Probleme ein, grad wenn du mit mehreren Fenstern arbeitest, ohne static zu arbeiten ist da die sinnvollere und saubere Lösung.
 

macaubas

Mitglied
Hallo mrBrown,
zuerst mal meine Ueberlegungen zu deinen Kritiken:

zu 1: Ich teste ja das ganze User-Interface. Wenn alles in Ordnung ist kann ich beliebige weitere Masken erstellen, die sind dann auch fehlerfrei. Anschliessend kann ich mich voll auf Applikation konsentrieren, denn hier ist meine wirkliche Herausforderung.
zu 2: das sehe ich absolut nicht so wie du.
zu 3: gerade das ist ja erwünscht. Die Komponenten in einer Instanz kennen sich gegenseitig, aber die Komponenten in anderen Instanzen nicht. Das habe ich ja auch wenn ich alles in einer FXML habe.

Aber nun zu meinem Problem:

Ich will immer noch deinem Vorschlag nachgehen und bin bis jetzt nicht weitergekommen.
Wenn ich die Controller mit z.B. "@FXML private FXMLTab1Controller fXMLTab1Controller;" aufführe habe ich ja nur Zugriff auf den Controller aber nicht auf die Instanzen darin. Somit dreht sich ja alles wieder im Kreis.
Vielleicht könntest du oder jemand anders mir mit einem einfachen Beispiel zeigen wie du auf Instanzen von Tab2 auf Instanzen von Tab1 zugreifts.
Schon mal Danke an alle, die mir eine Antwort haben.
Gruss Macaubas
 
K

kneitzel

Gast
Wenn du Zugriff auf Instanzvariablen haben willst, dann erstell Getter für diese oder mach sie nicht mehr private.
 

macaubas

Mitglied
Hallo JustNobody,
schön wärs, aber ich habe FXMLMain mit korrespondierenden FXMLTab1 und FXMLTab2 und jede hat gleichzeitig mehre Instanzen. Ich habe so mein Ziel nicht erreicht.
 

mrBrown

Super-Moderator
Mitarbeiter
zu 1: Ich teste ja das ganze User-Interface. Wenn alles in Ordnung ist kann ich beliebige weitere Masken erstellen, die sind dann auch fehlerfrei. Anschliessend kann ich mich voll auf Applikation konsentrieren, denn hier ist meine wirkliche Herausforderung.
Wie testest du das mit automatisierten Unit-Tests?

zu 2: das sehe ich absolut nicht so wie du.
Einfaches Beispiel: die Änderung einer ID in einer FXML oder einfach nur den Typ von Label zu TextField ändern kann jeder andere Klasse in der Applikation betreffen, da jede Klasse die ID und deren Typen kennen muss. Siehe auch den nächsten Punkt.

Warum meinst du, das ist nicht so?

zu 3: gerade das ist ja erwünscht. Die Komponenten in einer Instanz kennen sich gegenseitig, aber die Komponenten in anderen Instanzen nicht. Das habe ich ja auch wenn ich alles in einer FXML habe.
Eigentlich hat sich schon vor Jahrzehnten herausgestellt, dass genau sowas absolut unerwünscht ist.

Relevant ist auch erstmal eher, welche Klassen sich gegenseitig kennen. Allgemein ist da das Zeil, das möglich wenig Abhängigkeiten zwischen den einzelnen Klassen bestehen, im Idealfall sogar keinerlei direkt zwischen Klassen sondern nur über Interfaces.
Das was du hast ist ja das genaue Gegenteil davon – jede Klasse muss jede andere kennen, und das nicht mal mit der Möglichkeit für den Compiler, dass zu überprüfen, sondern nur über Strings.


Wenn ich die Controller mit z.B. "@FXML private FXMLTab1Controller fXMLTab1Controller;" aufführe habe ich ja nur Zugriff auf den Controller aber nicht auf die Instanzen darin.
Richtig - das ist der Sinn des ganzen: den inneren Zustand kapseln und nach außen möglichst wenig herausgeben.

Vielleicht könntest du oder jemand anders mir mit einem einfachen Beispiel zeigen wie du auf Instanzen von Tab2 auf Instanzen von Tab1 zugreifts.
Wenn man es zwingend muss über Getter, wie @JustNobody schon sagte. Besser ist aber, wenn kein Zugriff nötig ist, dann kann man das zB über ein gemeinsames Model lösen.

schön wärs, aber ich habe FXMLMain mit korrespondierenden FXMLTab1 und FXMLTab2 und jede hat gleichzeitig mehre Instanzen. Ich habe so mein Ziel nicht erreicht.
Doch. Ob man einer oder 700 Instanzen des ganzen Baumes hat ist egal, solange man sauber dazwischen trennt.
 

mrBrown

Super-Moderator
Mitarbeiter
Wie schon mal gesagt: Gib doch mal ein halbwegs "echtes" Beispiel, woran man die beiden Varianten sinnvoll vergleichen könnte :)

An dem initialen Beispiel erkennt man den Vorteil zumindest nicht, da war meins sowohl kürzer, als auch so ziemlich jeder Metrik nach besser – von daher würde mich ein Beispiel interessieren, an dem man den Vorteil sehen könnte.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
K JavaFX in mehrere Controller aufteilen AWT, Swing, JavaFX & SWT 29
K JavaFX in mehrere Controller aufteilen AWT, Swing, JavaFX & SWT 0
K JavaFX unterschiedliche (mehrere Fenster) in seperater Main Methode AWT, Swing, JavaFX & SWT 26
J JavaFX - mehrere Views, Model durchreichen AWT, Swing, JavaFX & SWT 10
L JavaFX Mehrere JavaFX Szenen mit einem Menü AWT, Swing, JavaFX & SWT 1
Juelin starten maven javafx programm ohne netbeans AWT, Swing, JavaFX & SWT 35
Juelin javax.swing in javafx AWT, Swing, JavaFX & SWT 1
MiMa JUnit5 im JavaFX Projekt AWT, Swing, JavaFX & SWT 2
Juelin in javafx Event auslösen AWT, Swing, JavaFX & SWT 4
MiMa Fonts, Icons, Bilder in JavaFX Anwendung AWT, Swing, JavaFX & SWT 5
MiMa SwingFXUtils in JavaFX 20 AWT, Swing, JavaFX & SWT 6
MiMa JavaFX Fenstertitel zu klein. AWT, Swing, JavaFX & SWT 1
MiMa JavaFX JAR unter Windows ausführen schlägt fehl? AWT, Swing, JavaFX & SWT 5
I JavaFX JavaFx-Anwendung für die Erstellung einer Windows-Anwendung? AWT, Swing, JavaFX & SWT 6
Hatsi09 Javafx MediaPlayer spielt nicht immer AWT, Swing, JavaFX & SWT 3
Hatsi09 Javafx Neuladen von ImageView und MediaView AWT, Swing, JavaFX & SWT 3
Maxim6394 JavaFX Umlaute in JavaFX GUI AWT, Swing, JavaFX & SWT 12
Maxim6394 JavaFX Scene Builder - Crash bei eigener Komponente AWT, Swing, JavaFX & SWT 2
Ernesto95 JavaFX JavaFX GUI mit sehr vielen Update requests AWT, Swing, JavaFX & SWT 4
Telisti Javafx Image wird nicht richtig integiert AWT, Swing, JavaFX & SWT 8
J Netbeans die JavaFX-Anwendung wird nicht ausgeführt AWT, Swing, JavaFX & SWT 16
MartinNeuerlich Kann mir jemand, der einen Mac mit einem m1 oder m2-Chip hat, eine POM geben mit der Javafx-Fullscreen beim Mac mit m-Chip funktioniert? AWT, Swing, JavaFX & SWT 1
tommybalbor JavaFx Anwendung klappt nicht für macOs Nutzern, wenn ich zwei dependecies bei maven hinzufüge AWT, Swing, JavaFX & SWT 6
JavaSchmecktLecker JavaFX JavaFX Ordner automatisch verlinken AWT, Swing, JavaFX & SWT 2
melaniemueller Taschenrechner JavaFX AWT, Swing, JavaFX & SWT 4
R auto. Importanweisungen für javafx funktioniert in Eclipse nicht mehr AWT, Swing, JavaFX & SWT 4
thor_norsk JavaFX Anwendung stürzt ab AWT, Swing, JavaFX & SWT 4
berserkerdq2 Skalieren sich javafx objekte automatisch auf die Bildschirmgröße AWT, Swing, JavaFX & SWT 6
berserkerdq2 Wie füge ich ein Bild in javafx mit dem Scenebuilder ein, das automatisch mitgezogen wird, wenn das Fenster vergrößert wird oder Vollbildmodus AWT, Swing, JavaFX & SWT 6
B Java Projekt mit JavaFX und jfoenix ausführbar machen AWT, Swing, JavaFX & SWT 46
H JavaFX wie JavaFX Projekt aufsetzen? AWT, Swing, JavaFX & SWT 10
thor_norsk JavaFX - Grafikkarte AWT, Swing, JavaFX & SWT 7
MiHimbert Rückmeldung an den aufrufenden JAVAFX-Dialog AWT, Swing, JavaFX & SWT 1
MiMa JavaFX Runtime components are Missing??? AWT, Swing, JavaFX & SWT 3
J JavaFx PDF in einem Element in einem Fenster anzeigen. AWT, Swing, JavaFX & SWT 11
B JavaFX Sprachumschaltung mit Button auf der HMI AWT, Swing, JavaFX & SWT 6
H JavaFX Fehlende JavaFX Package AWT, Swing, JavaFX & SWT 10
_user_q Kann man ein 2. JavaFX-Fenster auch beenden (exit) statt schließen (close) lassen? AWT, Swing, JavaFX & SWT 8
G JavaFX Line Chart mit Farbverlauf/Gradient in Linie AWT, Swing, JavaFX & SWT 1
thor_norsk JavaFX, FXML und SceneBuilder AWT, Swing, JavaFX & SWT 6
_user_q Über installDist exportiertes Programm wirft "Unsupported JavaFX configuration" AWT, Swing, JavaFX & SWT 0
CodingBerlin JavaFX Programm läuft nur unter Eclipse AWT, Swing, JavaFX & SWT 1
H Fehler: Zum Ausführen dieser Anwendung benötigte JavaFX-Runtime-Komponenten fehlen AWT, Swing, JavaFX & SWT 44
temi JavaFX "Frames" in JavaFx - passende Komponente? AWT, Swing, JavaFX & SWT 13
G JavaFX Steuerung bzw. Test von externer JavaFX Anwendung (liegt nur als jar vor) AWT, Swing, JavaFX & SWT 9
_user_q [JavaFX] Spinner so einstellen, dass er nicht leer bleiben darf? AWT, Swing, JavaFX & SWT 6
S Javafx getResource-Pfad wird nicht erkannt AWT, Swing, JavaFX & SWT 7
A JavaFX exportierte Jar ohne beim starten die Libs hinzufügen? AWT, Swing, JavaFX & SWT 2
J JavaFX Schiffe versenken mit JavaFX und Scene builder AWT, Swing, JavaFX & SWT 3
Encera ArrayList mit eigenen Objekten in JavaFX sortieren und ausgeben AWT, Swing, JavaFX & SWT 50
L JavaFx Textformatierung mittels Datenbank und Funktion anpassen AWT, Swing, JavaFX & SWT 5
sserio Wie funktioniert ein Controller bei JavaFx? AWT, Swing, JavaFX & SWT 1
sserio Kann man bei JavaFx ein Fenster aufkommen lassen? AWT, Swing, JavaFX & SWT 1
Jose05 JavaFx Fxml: GUI aus einer anderen Klasse starten AWT, Swing, JavaFX & SWT 1
Tassos JavaFX/Problem mit der Maussteuerung in Stackpane AWT, Swing, JavaFX & SWT 7
S Ich bringe Code mit JavaFX unter Apache NetBeans IDE 12.6 nicht zum laufen. AWT, Swing, JavaFX & SWT 14
K Bekomme (u.a) javafx.fxml.LoadException trotz "korrektem" Code AWT, Swing, JavaFX & SWT 8
S JavaFX: voneinander abhängige TextFields AWT, Swing, JavaFX & SWT 33
M Gluon will JavaFX in den Browser stecken AWT, Swing, JavaFX & SWT 0
H javafx application does not exist AWT, Swing, JavaFX & SWT 16
A JavaFX Controller Problem AWT, Swing, JavaFX & SWT 1
izoards JavaFX TableView mit Array Inhalt füllen AWT, Swing, JavaFX & SWT 1
M Javafx versuch Bibliothek zu erstellen AWT, Swing, JavaFX & SWT 0
N JavaFX Javafx intelij Projekt zu ausführbaren jar Datei Machen AWT, Swing, JavaFX & SWT 1
K JavaFx, Sound Aufnahme und Thread AWT, Swing, JavaFX & SWT 0
izoards JavaFX TextFlow - Sonderzeichen AWT, Swing, JavaFX & SWT 1
maximstein JavaFX WebView - java.lang.NoSuchMethodError: 'boolean com.sun.prism.ResourceFactory.isDisposed()' AWT, Swing, JavaFX & SWT 4
N JavaFX Unicode zeichnen in javafx Label verwenden AWT, Swing, JavaFX & SWT 2
MiHimbert javaFX openfx (17) datepicker AWT, Swing, JavaFX & SWT 3
A Mit JavaFX einzelne Zeilen in TableView farbig markieren AWT, Swing, JavaFX & SWT 5
melaniemueller JavaFX Taschenrechner mit SceneBuilder AWT, Swing, JavaFX & SWT 12
Jose05 Javafx Label Höhe=Breite AWT, Swing, JavaFX & SWT 1
Jose05 JavaFX: eigene FXML-Datei für einen Button AWT, Swing, JavaFX & SWT 3
izoards JavaFX editierbare Tabelle AWT, Swing, JavaFX & SWT 4
N javafx Position der Bustaben finden label AWT, Swing, JavaFX & SWT 1
D Verschieden Scenen ansprechen mit dem Scene Builder und JavaFX (Eclipse) AWT, Swing, JavaFX & SWT 16
izoards JavaFX Background Task warten auf Knopfdruck AWT, Swing, JavaFX & SWT 4
M Zufallsgenerator bei JavaFx AWT, Swing, JavaFX & SWT 1
N Label Schriftart Ändern javafx AWT, Swing, JavaFX & SWT 2
L JavaFX JavaFX, MVVM und SceneBuilder AWT, Swing, JavaFX & SWT 4
S JavaFx Album AWT, Swing, JavaFX & SWT 137
I JavaFX - Pane wechseln über 2. Controller AWT, Swing, JavaFX & SWT 5
melaniemueller JavaFX Beispiel kann nicht ausgeführt werden AWT, Swing, JavaFX & SWT 4
T FXML Datei in Java Code einbinden: javafx.fxml.LoadException AWT, Swing, JavaFX & SWT 2
J JavaFX - Included FXML - Entfernen feststellen AWT, Swing, JavaFX & SWT 2
J JavaFX JavaFX/ Taskmenu / UML Klassendiagramm AWT, Swing, JavaFX & SWT 2
Davee JavaFX JavaFX Jar ausführbar jedoch nicht alle Stages AWT, Swing, JavaFX & SWT 3
2 JavaFX die ChoiceBox leitet den String nicht weiter oder es komm zu einem NullPointer AWT, Swing, JavaFX & SWT 8
C MouseEvent JavaFX AWT, Swing, JavaFX & SWT 4
L JavaFX javafx.fxml.LoadException bei einem Taschenrechner AWT, Swing, JavaFX & SWT 5
M4cM4rco0707 JavaFX Custom-Komponente mit Custom-Controller AWT, Swing, JavaFX & SWT 3
M Error occurred during initialization of boot layer java.lang.module.FindException: Module javafx.controls not found AWT, Swing, JavaFX & SWT 14
G javafx build.fxbuild in eclipse und ANT AWT, Swing, JavaFX & SWT 9
M Kollisionensbehandlung mit JavaFX AWT, Swing, JavaFX & SWT 1
N JavaFX - Toolkit not initialized AWT, Swing, JavaFX & SWT 6
G JavaFX , Duplicate erzeugt aber ich weis nicht wo AWT, Swing, JavaFX & SWT 4
K Javafx Plugin (javafx:jlink) mit moditect Plugin nutzen ... AWT, Swing, JavaFX & SWT 5
J Saubere Trennung Model, View, Controller Javafx AWT, Swing, JavaFX & SWT 10
G JavaFX BarChart während der Runtime aktualisieren AWT, Swing, JavaFX & SWT 4
Z JavaFX JavaFX Spinner AWT, Swing, JavaFX & SWT 1

Ähnliche Java Themen

Neue Themen


Oben