Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
JavaFXBei Scenen-Wechsel im primaryStage wird aktuelle Fenstergröße nicht mit übernommen
habe 4 Guis. Die Elemente werden in eine BorderPane als layout gepackt, die layout in eine Scene. Bei der übergabe des layouts in die Scenen kann man ja im Konstrucktor die Größe angeben, wie z.B. scene1 = new Scene(layout, 800,600);
Die Scenes kann ich per Button-Klick in der primaryStage mit primaryStage.setScene(scene1) usw immer tauschen, so dass die gewünschte GUI jeweils angezeigt wird.
So, mein großes Problem ist, dass wenn der User warum auch immer die Fenstergröße ändert (mit ziehen der Maus an den Rändern), wird die aktuelle Fenstergröße nicht beim Gui-Wechsel mit übernommen. Es wird meine vordefinierte Größe wieder genommen. Ich will aber, dass die aktuelle Fenstergröße beim Gui-Wechsel bleibt.
Ich finde keine Methode, das eingefügte Layout einer Scene abrufen zu können, wie z.B. mit einem Befehl wie scene1.getLayout() oder ähnlichem. Meine Idee wäre gewsen, nicht die Scenes im primaryStage zu wechseln, sondern die layouts im Scene. So wäre vielleicht die Größe der Scene immer gleich geblieben.
Oder zumindes hätte ich, wenn ich eben das layout in der Scene zurückbekommen könnte, die größe ändern können, indem ich die with und height der primaryStage mir hole und den layouts übergebe.
Leider finde ich über google genau das nicht. Ich finde keine Artikel, die genau dieses Problem angehen.
Die Scene hat doch getWidth / getHeight Methoden. Wenn Du Dir also die Scene, die Du gerade in der primare Stage anzeigst, merkst, dann kannst Du eine neue Scene mit der entsprechenden Größe setzen.
Ich habe das einfach einmal kurz getestet mit:
Java:
private void changeScene() {
double x = scene.getWidth();
double y = scene.getHeight();
scene = new Scene(new BorderPane(), x, y);
stage.setScene(scene);
}
scene ist da einfach eine Instanzvariable, die die aktuelle scene speichert und stage ist die Instanzvariable, der ich primaryStage zu gewiesen habe.
Die neue Scene ist natürlich bei mir leer mit einer BorderPane() aber das ist ja erst einmal egal - die Fenstergröße blieb in meinem Test unverändert.
Die Scene hat doch getWidth / getHeight Methoden. Wenn Du Dir also die Scene, die Du gerade in der primare Stage anzeigst, merkst, dann kannst Du eine neue Scene mit der entsprechenden Größe setzen.
Ich habe das einfach einmal kurz getestet mit:
Java:
private void changeScene() {
double x = scene.getWidth();
double y = scene.getHeight();
scene = new Scene(new BorderPane(), x, y);
stage.setScene(scene);
}
scene ist da einfach eine Instanzvariable, die die aktuelle scene speichert und stage ist die Instanzvariable, der ich primaryStage zu gewiesen habe.
Die neue Scene ist natürlich bei mir leer mit einer BorderPane() aber das ist ja erst einmal egal - die Fenstergröße blieb in meinem Test unverändert.
Die Idee hatte ich auch, aber da bei mir der User immer wieder die Scenen wechseln können muss, sagt mir Eclipse, dass das Layout bei mir in der vorherigen Scene drin ist und sie nicht in eine neue Scene eingefügt werden kann.
So eine Lösung, das Layout von der vorherigen zu lösen, habe ich leider auch keine gefunden.
Was genau hast Du denn versucht, als Du diese Meldung bekommen hast? Du machst ja nichts mit einem Layout. Alles was Du machst ist, dass Du Dir eine Referenz auf die aktuelle Scene merkst (Stage hat wohl kein getScene - oder ich habe das übersehen) und wenn Du eine neue Scene erstellst, dann liest Du vorab die Größe aus (Setzt voraus, dass die Scene derzeit sichtbar ist!)
Was genau hast Du denn versucht, als Du diese Meldung bekommen hast? Du machst ja nichts mit einem Layout. Alles was Du machst ist, dass Du Dir eine Referenz auf die aktuelle Scene merkst (Stage hat wohl kein getScene - oder ich habe das übersehen) und wenn Du eine neue Scene erstellst, dann liest Du vorab die Größe aus (Setzt voraus, dass die Scene derzeit sichtbar ist!)
package application;
import conVar.ConVar;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class RunEinnAusgApp extends Application {
private Stage stage;
private MainCon mainCon;
@Override
public void init() throws Exception {
super.init();
mainCon = new MainCon(this);
}
@Override
public void start(Stage primaryStage) {
try {
//primaryStage will becomme global
stage = primaryStage;
//needed at beginning because app should start with Gui-Start-Scene
Scene scene = mainCon.getScStart();
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stage.setScene(scene);
primaryStage.setTitle(ConVar.appName);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
public Stage getPrimStage() {
return stage;
}
}//RunEinnAusgApp
Haupt-Controller
Java:
package application;
import handler.ConAnalyze;
import handler.ConBook;
import handler.ConEdit;
import handler.ConStart;
import javafx.scene.Scene;
import util.EnumScene;
public class MainCon {
RunEinnAusgApp app;
private ConStart conStart;
private ConBook conBook;
private ConEdit conEdit;
private ConAnalyze conAnalyze;
private Scene scStart;
private Scene scBook;
private Scene scEdit;
private Scene scAnalyze;
//testweise
public Scene scene;
public MainCon(RunEinnAusgApp app) {
this.app = app;
conStart = new ConStart(this);
conBook = new ConBook(this);
scStart = conStart.getGStart().getScStart();
scBook = conBook.getGBook().getScBook();
}
//to change Scene in primaryStage (JavaFX-Stuff)
public void changeScene(EnumScene sc) {
if (sc == EnumScene.scStart) {
app.getPrimStage().setScene(scStart);
} else if (sc == EnumScene.scBook) {
app.getPrimStage().setScene(scBook);
} else if (sc == EnumScene.scEdit) {
app.getPrimStage().setScene(scEdit);
} else if (sc == EnumScene.scAnalyze) {
app.getPrimStage().setScene(scAnalyze);
}
}//setScene
//neded only at beginning the app, because primaryStage needs first Scene to show
//in this case it is Scene from Start-Gui
public Scene getScStart() {
return scStart;
}
}//class
Sub-Controller für Start-GUI
Java:
package handler;
import application.MainCon;
import gui.GStart;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import util.EnumAlerts;
import util.EnumScene;
public class ConStart implements EventHandler<ActionEvent> {
MainCon mainCon;
GStart gStart;
public ConStart(MainCon mainCon) {
this.mainCon = mainCon;
gStart = new GStart(this);
}
@Override
public void handle(ActionEvent event) {
Object o = event.getSource();
if (o == gStart.getBtnBook()) {
mainCon.changeScene(EnumScene.scBook);
}
}//handle
public GStart getGStart() {
return gStart;
}
}//class
Gui-Start
Java:
package gui;
//imports sind alle da keine Sorge
public class GStart {
private BorderPane layout;
private Scene sceneBook;
private ConBook conBook;
private final Button btnBook;
public GStart(ConStart conStart) {
this.conBook = conBook;
layout = new BorderPane();
sceneBook = new Scene(layout, ConVar.lyotSizeX, ConVar.lyotSizeY);
Label l = new Label("Start-Seite von meiner App");
l.setFont(Font.font(100));
layout.setCenter(l);
btnBook = new Button("zum Buchen gehts hier lang");
btnBook.setOnAction(this.conBook);
layout.setBottom(btnBook);
BorderPane.setAlignment(btnBook, Pos.CENTER);
}//constr
public Scene getScStart() {
return sceneGStart;
}
public Button getBtnBook() {
return btnBook;
}
}//class
Sub-Controller für Book-GUI
Java:
package handler;
import application.MainCon;
import gui.GBook;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import util.EnumScene;
public class ConBook implements EventHandler<ActionEvent>{
MainCon mainCon;
GBook gBook;
public ConBook(MainCon mainCon) {
this.mainCon = mainCon;
gBook = new GBook(this);
}
@Override
public void handle(ActionEvent event) {
Object o = event.getSource();
if (o == gBook.getBtnBack()) {
mainCon.changeScene(EnumScene.scStart);
}
}//handle
public GBook getGBook() {
return gBook;
}
}//class
Die GUI für Buchen
Java:
package gui;
import conVar.ConVar;
import handler.ConBook;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Font;
public class GBook {
private BorderPane layout;
private Scene sceneBook;
private ConBook conBook;
private final Button btnBack;
public GBook(ConBook conBook) {
this.conBook = conBook;
layout = new BorderPane();
sceneBook = new Scene(layout, ConVar.lyotSizeX, ConVar.lyotSizeY);
Label l = new Label("Seite Gui Book");
l.setFont(Font.font(100));
layout.setCenter(l);
btnBack = new Button("zurück zum Hauptfenster");
btnBack.setOnAction(this.conBook);
layout.setBottom(btnBack);
BorderPane.setAlignment(btnBack, Pos.CENTER);
}//constr
public Scene getScBook() {
return sceneBook;
}
public Button getBtnBack() {
return btnBack;
}
}//GBook
So. Exemplarisch mal 2 Guis. Den echten Inhalt habe ich versucht durch einen kleinen einfachen Inhalt zu ersetzen, weil der echte ziemlich viel und lang ist. Ich hoffe ich habe deshalb keien Fehler eingebaut. Aber, so habe ich den Gui-Wechsel ralisiert. Klappt auch. Aber nur aktuelle Fenstergrößen werden nicht übernommen.
Ich kann im Haup-Controller in der public void changeScene(EnumScene sc) Methode natürlich auch eine neue Scene erstellen, und das layout der jeweiligen Guis holen. Bei der Scene-Erstellung natürlich die aktuelle Breite und aktuelle Höhe der alten Scene übernehmen. Aber hier kriege ich immer die Meldung, dass das layout bereits in einer Scene steckt. So verstehe ich die Meldung zumindest.
if (sc == EnumScene.scStart) {
Scene sc1 = new Scene(conStart.getGStart().getlayoutStart(),
app.getPrimStage().getWidth(), app.getPrimStage().getHeight());
app.getPrimStage().setScene(sc1);
//app.getPrimStage().setScene(scStart);
Dann kommt aber folgende Fehlermeldung
Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: BorderPane@22372720[styleClass=root]is already set as root of another scene
at javafx.graphics/javafx.scene.Scene$8.invalidated(Scene.java:1216)
at javafx.base/javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
at javafx.base/javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
at javafx.graphics/javafx.scene.Scene.setRoot(Scene.java:1178)
at javafx.graphics/javafx.scene.Scene.<init>(Scene.java:356)
at javafx.graphics/javafx.scene.Scene.<init>(Scene.java:236)
at application.MainCon.changeScene(MainCon.java:41)
at handler.ConBook.handle(ConBook.java:25)
at handler.ConBook.handle(ConBook.java:1)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8885)
at javafx.controls/javafx.scene.control.Button.fire(Button.java:203)
at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3890)
at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1885)
at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2618)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:412)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:832)
Ich finde keine Methoden der Klasse Scene, um größe oder layout neu zu setzen, oder zumindest das layout von der Scene zu befreien um es einer neuen zu übergeben.
Es gibt bestimmt die Methoden aber ich finde durch googeln leider seit Stunden nichts.
Ok, das Problem ist also, dass Du keine neue Scene erstellen willst sondern vorhandene (wieder)verwenden möchtest.
Das ist aber auch kein Problem meine ich. Da wäre dann aus meiner Sicht die Idee, dass man sich einfach die Größe der Stage merkt und dann nach setzen der Scene neu setzt.
Ich habe da bei meinem Test einfach ein scene1 und scene2 erstellt und wechsel da hin und her. Das brauchst Du natürlich nicht. Du musst dir aktuelle Scene nicht merken.
Das könnte bei Dir so aussehen:
Java:
//to change Scene in primaryStage (JavaFX-Stuff)
public void changeScene(EnumScene sc) {
Stage stage = app.getPrimStage();
double width = stage.getWidth();
double height = stage.getHeight();
if (sc == EnumScene.scStart) {
stage.setScene(scStart);
} else if (sc == EnumScene.scBook) {
stage.setScene(scBook);
} else if (sc == EnumScene.scEdit) {
stage.setScene(scEdit);
} else if (sc == EnumScene.scAnalyze) {
stage.setScene(scAnalyze);
}
stage.setHeight(height);
stage.setWidth(width);
}//setScene
Wobei das if/else noch zu einem Switch werden könnte denke ich mal - das wäre aus meiner Sicht übersichtlicher ...
Das ist eine sehr gute Idee, die ich auch probiert habe. Hatte aber bei mir den nachteiligen Effekt, dass nach Scenen-Wechsel der Inhalt verzerrt wiedergegeben wurde (zu groß und nach rechts verrückt) und erst nachdem ich ein kleines bischen mit der Muas an der Fenstergröße gespielt hatte wurde der Inhalt korrekt angezeigt. Ein Milimeter hat schon gereicht.
So ist der Anfang
Leider verzerrt
Erst wenn ich mit der Maus an der Fenstergröße ein MikroMeter spiele, dann wird sofort korrekt angezeigt.
Und mit "zurück zum Hauptfenster" Button die verzerrte Version von Start.
Hier wieder auch: Wenn ich an der Fenstergröße spielen würde, würde der Inhalt instantan quasi wieder korrekt angezeigt werden wie oben.
Ich habe nirgends was falsch ausgeschnitten, sonder die Anzeige ist wirklich immer so.
Appropo Fenster. Ich habe eine Frage nebenbei zu Eclipse. Vielleicht hast du eine Idee. Ich hab leider ein kleines Lapi, 11,6 Zoll, also sehr klein. Leider habe ich nichts anderes zur Verfügung. Es ging bis jetzt. Beim Üben und Lernen habe ich nicht viele Klassen gleichzeitig im Projekt gebraucht. Aber jetzt wird es mit dem Bildschirm manchmal echt eng. Eclipse bietet ja die Möglichkeit, noch ein Fenster zu öffnen. Da würde ich dann z.B. meine Util-Klassen oder so auf haben. Würde für mich schon reichen.
Aber leider muss ich das 2. Fenster immer wieder neu öfnnen und meine Klassen darein öffnen. Gibt es eine Möglichkeit bei Eclipse-Start das 2. oder 3. Fenster automatisch mit meinen Klassen öffnen zu lassen? Wenn ja, wäre das der Hammer.
Also bezüglich der verzerrten Darstellung kannst du zwei Dinge probieren:
a) stage.hide() / stage.show() combo.
b) resizeable wegnehmen und neu setzen, also etwas wie
Java:
final boolean resizable = stage.isResizable();
stage.setResizable(!resizable);
stage.setResizable(resizable);
Bringt eines der beiden Ideen eine Besserung? (Hab noch nicht versucht es nachzustellen... Bin nur noch am Handy aktiv ...)
Ich verstehe das Problem, und hätte es auch erst einmal wie @JustNobody gesagt hat, umgesetzt. Aber genau genommen wechsel ich in meinen Apps nie die Scene gewechselt.
Ich würde stattdessen entweder auf SubScene setzen (auch nicht mein favorisierter Ansatz), oder auf eine StackPane als root, deren Inhalt ich ändere - im Zweifelsfall indem ich alle children auf den Stack lege und immer das nach vorne hole (via toFront), welches ich gerade brauche. Kann man dann auch noch nett animieren (opacity=0 - toFront - animiere opacity nach 1 und parallel vom alten Front-Child die opacity nach 0).
Ich weiss, das löst jetzt leider nicht dein konkretes Problem. Ist nur ein anderer Ansatz...
Also bezüglich der verzerrten Darstellung kannst du zwei Dinge probieren:
a) stage.hide() / stage.show() combo.
b) resizeable wegnehmen und neu setzen, also etwas wie
Java:
final boolean resizable = stage.isResizable();
stage.setResizable(!resizable);
stage.setResizable(resizable);
Bringt eines der beiden Ideen eine Besserung? (Hab noch nicht versucht es nachzustellen... Bin nur noch am Handy aktiv ...)
Woww. Hab eben deinen Rat mit stage.hide(); neue Sizes setzen; stage.show() gemacht, funktioniert super. Hat sogar einen leichten "windowing"-Effekt bekommen, weil es kurz aus und wieder eingeblendet wird. Bei meinem Programm ist das wohl absolut egal, aber bei größeren Programmen vielleicht der Geschwidigkeitsnachteil ein Faktor.
Ich verstehe das Problem, und hätte es auch erst einmal wie @JustNobody gesagt hat, umgesetzt. Aber genau genommen wechsel ich in meinen Apps nie die Scene gewechselt.
Ich würde stattdessen entweder auf SubScene setzen (auch nicht mein favorisierter Ansatz), oder auf eine StackPane als root, deren Inhalt ich ändere - im Zweifelsfall indem ich alle children auf den Stack lege und immer das nach vorne hole (via toFront), welches ich gerade brauche. Kann man dann auch noch nett animieren (opacity=0 - toFront - animiere opacity nach 1 und parallel vom alten Front-Child die opacity nach 0).
Ich weiss, das löst jetzt leider nicht dein konkretes Problem. Ist nur ein anderer Ansatz...
Ich bin noch nicht so gut, dass ich alle Methoden und Möglichkeiten kenne . Aber ich werde immer besser . Irgendwann hoffentlich so gut, dass ich mit Programmieren mein Geld verdienen kann. Es macht mir Spaß, aber leider ist die Zeit zum Probieren und Üben knapp.
Ich schau mir mal die SubScenen mal an. Vielleicht sind ja SubScenen genau für solche Fensterwechsel eigentlich gedacht.
Alles auf die StackPane hört sich auch sehr interessant an. Aber man hat ja in der Regel viele Elemente in einem Darstellungsfenster, also auf der Stackpane gleichzeitig. Dann müsste man ja alle gleichzeitig auf toFront setzen. Ist das nicht bissel viel Aufwand? Oder setzt du für ein Darstellungsfenster benötigte Elemente in eine eigenes LayoutContainer und setzt dann diesen in die Stackpane, so dass du mit einem Befehl alle auf Front dann gesetzt bekommst?
Diese automatische Übersetzung lässt es einem ja kalt über den Rücken laufen ... Aber interessant: Wie kommt man da zu Wortschöpfungen wie: Etwas das etwas Anderes enthält, ist ein Enthälter
Gegen den Link selbst wollte ich nichts gesagt haben. Ist bei mir auch in der Link Liste gelandet. Aber halt in der Englischen Version.
Und da fällt mir auch gleich auf: Vergessen den Like Button zu drücken