JavaFX JavaFX in mehrere Controller

M

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

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

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.
 
M

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
 
L

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

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

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

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

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.
 
M

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.
 
M

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

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

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).
 
M

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

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? :)
 
M

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

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.
 
M

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
 
kneitzel

kneitzel

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

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

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

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
J JavaFX - mehrere Views, Model durchreichen AWT, Swing, JavaFX & SWT 10
L JavaFX Mehrere JavaFX Szenen mit einem Menü AWT, Swing, JavaFX & SWT 1
S JavaFX - mit Listener Veränderungen in einer TableView abhören AWT, Swing, JavaFX & SWT 3
R Können Animationen in JavaFX "verschluckt" werden? AWT, Swing, JavaFX & SWT 8
dtr84 JavaFX/OpenJFX mittels Apache Ivy einbinden (Java 11) AWT, Swing, JavaFX & SWT 18
D runnable Jar mit Javafx erstellen(Eclipse) AWT, Swing, JavaFX & SWT 10
Monokuma Blöcke erzeugen (JavaFX) AWT, Swing, JavaFX & SWT 1
P JavaFx - Progressbar - Füllen mittels mehreren Tasks AWT, Swing, JavaFX & SWT 0
Rafael.Cupari JavaFx Installer AWT, Swing, JavaFX & SWT 16
Monokuma Swing zu JavaFX AWT, Swing, JavaFX & SWT 3
C JavaFx sound abspielen AWT, Swing, JavaFX & SWT 3
C JavaFX mit CSS in Eclipse AWT, Swing, JavaFX & SWT 2
parrot JavaFX Fehler AWT, Swing, JavaFX & SWT 4
B JavaFX JavaFX Anwendung deployen (entw als runnableJAR oder exe-Datei) AWT, Swing, JavaFX & SWT 15
N JavaFX applikation auf anderen Systemen zum laufen bringen AWT, Swing, JavaFX & SWT 7
W JavaFX JavaFX - TreeView will nicht AWT, Swing, JavaFX & SWT 8
H JavaFX JavaFX - Scene Builder - BorderPane AWT, Swing, JavaFX & SWT 23
D Columns unabhängig voneinander mit Daten füllen JavaFx AWT, Swing, JavaFX & SWT 1
C JavaFX Installation unter IntelliJ IDEA AWT, Swing, JavaFX & SWT 5
J JavaFX Label aktualisieren AWT, Swing, JavaFX & SWT 18
H JavaFX JavaFX Import Fehler AWT, Swing, JavaFX & SWT 4
M JavaFX javaFX Label-Text wird nicht gesetzt AWT, Swing, JavaFX & SWT 3
T Szene wechselen JavaFX mit If in Main Class AWT, Swing, JavaFX & SWT 2
S JavaFx Zufallsfarbe beim Button-Klick AWT, Swing, JavaFX & SWT 22
R javafx erste application AWT, Swing, JavaFX & SWT 12
kneitzel JavaFX - Binding & Co AWT, Swing, JavaFX & SWT 42
S Alternative JavaFX TableView AWT, Swing, JavaFX & SWT 1
B Game of Life in JavaFX AWT, Swing, JavaFX & SWT 5
B eclipse für JavaFx setuppen AWT, Swing, JavaFX & SWT 4
N JavaFX Chioceboxen verküpfen AWT, Swing, JavaFX & SWT 0
J JavaFX Controls AWT, Swing, JavaFX & SWT 4
S JavaFx AWT, Swing, JavaFX & SWT 2
Tashtego JavaFX + Mobile AWT, Swing, JavaFX & SWT 9
JavaTalksToMe JavaFx ExekutorService Problem AWT, Swing, JavaFX & SWT 2
L JavaFX Javafx Dependency-Inversion AWT, Swing, JavaFX & SWT 19
OSchriever JavaFX JavaFX auf Raspberry Pi 4 AWT, Swing, JavaFX & SWT 6
M JavaFX Tab auswählen mit JavaFX AWT, Swing, JavaFX & SWT 9
J JavaFX JavaFX Splitpane - Zugriff auf die Controller der Elemente AWT, Swing, JavaFX & SWT 8
M Java und JavaFX 13 läuft endlich AWT, Swing, JavaFX & SWT 4
N JavaFX Logging des JavaFX Application Threads mit Log4J AWT, Swing, JavaFX & SWT 3
L Java FX JavaFX Effect Attribute ausdrucken AWT, Swing, JavaFX & SWT 1
Hatsi09 JavaFx Mediaplayer seltsames Verhalten AWT, Swing, JavaFX & SWT 0
Tashtego JavaFX - Datenübergabe zwischen Scenes AWT, Swing, JavaFX & SWT 8
Zrebna JavaFX-Projekt mit Bildern funktioniert nicht - um Hilfe wird gebeten AWT, Swing, JavaFX & SWT 14
S Kann javafx.scene.layout.VBoxBuilder nicht importieren AWT, Swing, JavaFX & SWT 3
Bluedaishi JavaFX Programm start mit zwei scenen bzw Fenster AWT, Swing, JavaFX & SWT 1
S Jogl und JavaFX AWT, Swing, JavaFX & SWT 6
Bluedaishi JavaFX ProgressBar AWT, Swing, JavaFX & SWT 10
S JavaFX JavaFX TableView scrollen färbt falsche Zeilen AWT, Swing, JavaFX & SWT 1
F JavaFX JavaFX Builden: JavaFX Runtime components are missing AWT, Swing, JavaFX & SWT 0
F JavaFX wirft zufällig Exceptions AWT, Swing, JavaFX & SWT 5
M JavaFX JAVAFX TreeItem mit Tooltip versehen AWT, Swing, JavaFX & SWT 4
techM JavaFX -> CSS AWT, Swing, JavaFX & SWT 5
J JavaFx TableView mit CheckBox AWT, Swing, JavaFX & SWT 4
J JavaFX Stoppuhr mit javafx.timeline AWT, Swing, JavaFX & SWT 2
B Problem mit JavaFX AWT, Swing, JavaFX & SWT 5
O Zukunft von Swing und JavaFX ? AWT, Swing, JavaFX & SWT 3
L JavaFX auf dem PI 4 installieren AWT, Swing, JavaFX & SWT 2
L JavaFX JavaFX Forms mit Groovy starten AWT, Swing, JavaFX & SWT 1
K JavaFX CSS Border (Verschiebung verhindern) AWT, Swing, JavaFX & SWT 4
K JavaFX Element in HBOX nach rechts verschieben AWT, Swing, JavaFX & SWT 2
M error: package javafx.scene.web is not visible import javafx.scene.web.*; AWT, Swing, JavaFX & SWT 16
J import javafx.fxml* bei JavaFX 13 geht nicht mehr AWT, Swing, JavaFX & SWT 7
F Kein JavaFX mehr im Eclipse Wizard AWT, Swing, JavaFX & SWT 1
N Ausführbare Datei aus JavaFX Projekt erstellen AWT, Swing, JavaFX & SWT 22
N Array mit JavaFX Elementen AWT, Swing, JavaFX & SWT 9
S JavaFX Exception in thread "JavaFX Application Thread" AWT, Swing, JavaFX & SWT 3
W JavaFX JavaFX - Spalten auf ganze SpreadsheetView verteilen AWT, Swing, JavaFX & SWT 16
L Label im JavaFX Thread Updaten AWT, Swing, JavaFX & SWT 3
S Erwaege JavaFX Einstieg AWT, Swing, JavaFX & SWT 27
O JavaFX mini Taschenrechner! AWT, Swing, JavaFX & SWT 35
L JavaFX JavaFX, FXML und Guice? AWT, Swing, JavaFX & SWT 0
B JavaFX habe mein Problem fett markiert AWT, Swing, JavaFX & SWT 2
L Javafx Controller Klasse in Maven AWT, Swing, JavaFX & SWT 7
L JavaFX JavaFX stürtzt durch einen Server#connect Exception AWT, Swing, JavaFX & SWT 3
Shallty JavaFX MenuItem (Info) Icon ändern AWT, Swing, JavaFX & SWT 7
E Aktuelle Uhrzeit auf jeder Stage anzeigen lassen (JavaFX) AWT, Swing, JavaFX & SWT 2
temi JavaFX Problem mit IntelliJ und JavaFx 11 unter XUbuntu AWT, Swing, JavaFX & SWT 3
L Java FX Problem mit Ubuntu 18 und JavaFx AWT, Swing, JavaFX & SWT 27
L JavaFX JavaScript im Javafx Webview AWT, Swing, JavaFX & SWT 4
pkm Ich kann JavaFX nicht installieren AWT, Swing, JavaFX & SWT 4
A JavaFX Daten in eine HTML-Table mit JS schreiben AWT, Swing, JavaFX & SWT 3
L JavaFX JavaFX Diagram Editor AWT, Swing, JavaFX & SWT 3
L JavaFX JavaFX Application mit Preloader sauber runterfahren AWT, Swing, JavaFX & SWT 10
K JavaFX funktioniert nicht AWT, Swing, JavaFX & SWT 2
G JavaFX Slider in JavaFX beide Seiten beschriften AWT, Swing, JavaFX & SWT 2
D JavaFX JavaFX Tutorial AWT, Swing, JavaFX & SWT 8
Bluedaishi JavaFX JFoenix TextField KeyEvent AWT, Swing, JavaFX & SWT 2
B JavaFx TreeView mit file system AWT, Swing, JavaFX & SWT 1
Bluedaishi JavaFX Button Image aus Datenbank AWT, Swing, JavaFX & SWT 13
B JavaFx Scene Builder Problem AWT, Swing, JavaFX & SWT 2
H Feste Positionen und Größen in JavaFX AWT, Swing, JavaFX & SWT 1
B JavaFX JavaFX TableView PropertyValueFactory für Werte aus HashMap AWT, Swing, JavaFX & SWT 2
B JavaFX JavaFX Table gespeichertes Wert auswählen/anvisieren AWT, Swing, JavaFX & SWT 3
FRI3ND JavaFX: Wie kann ich einer Scene Argumente übergeben? AWT, Swing, JavaFX & SWT 14
J Javafx mediaplayer mit sftp AWT, Swing, JavaFX & SWT 0
platofan23 JAVAFX zweites Fenster öffnen AWT, Swing, JavaFX & SWT 2
MoxxiManagarm JavaFX Auch ich versuche mit JavaFX warm zu werden AWT, Swing, JavaFX & SWT 9

Ähnliche Java Themen

Anzeige

Neue Themen


Oben