JavaFX minWidth/minHeigth der Stage (des Fensters) festlegen

Krappe87

Mitglied
Moin moin,

ich habe folgendes Problem. Ich habe für die Scene bzw. die Stage eine minWidth/minHeigth festgelegt, damit die Applikation nach dem starten nicht kleiner gezogen werden sollte, aber größer gezogen werden darf. Soweit ja ganz einfach. Den View über fxml gebastelt, einer Scene zugewiesen und die Scene dann der Stage hinzugefügt. Dann lass ich das ganze anzeigen und setze an der Stage mit
Java:
primaryStage.setMinWidth(primaryStage.getWidth());
primaryStage.setMinHeight(primaryStage.getHeight());
die minWidth/minHeigth, in dem ich die aktuelle Width/Heigth abfrage. Danach sollte die Stage ja eigentlich die Höhe und Breite haben, die die Stage hätte, wenn das Fenster für den Nutzer angezeigt wird.

Ist leider nicht so. Ich habe nach dem stage.show() mal zwei system.out.println's gesetzt und darauf ein Breakpoint, damit ich mir das ganze im Debug-Modus anschauen kann. Das Fenster ist weder fertig gebaut, noch hat es die richtigen Werte. Ich bekomme die Werte, die die Stage hat, die durch den View festgelegt wurden. Allerdings wird danach noch vom Betriebssystem der Skin drumgebrezelt. Sodass unter Windows z.B. in der Breite 8pix und in der Höhe 34pix dazukommen. Da das ganze aber ja systemunabhängig laufen sollte, kann ich die Werte nicht einfach draufrechnen, weil unter Linux (Ubuntu 14.04) in der Breite keine Pixel mehr dazukommen.

Ich hab jetzt eine Woche lang gegoogelt und auch dieses Forum hier durchforstet (ca. 20 Beiträge, die in der engeren Auswahl standen komplett gelesen), allerdings scheint nichts wirklich mein Problem zu lösen. Es gibt einen Beitrag, in dem z.B. auf die Scene ein ChangeListener gesetzt wird. Interessanter Gedanke, das würde auch theoretisch funktionieren, nur leider ist dann nicht der erste Wert der richtige, sondern betriebssystemabhängig mal der 2. oder 3. Wert durch den ChangeListener.

Was an der Geschichte wirklich lustig ist, gestern ging das, heute wieder nicht. Ich hab 2 PC's und eine Laptop. Auf einem läuft Win7, auf dem anderen Kubuntu 15.04. Auf dem Laptop Ubuntu 14.04. Mit allen drei Systemen habe ich das mit der Javaversion 8.45 getestet. Ich entwickle mit Intellij Idea Version 14.

Ich muss ehrlich sagen, dass ich verwirrt bin. Ich habe von gestern auf heute nichts verändert, außer die Rechner runter- und hochgefahren :D. Weiß vielleicht einer, woran das liegen könnte? Eventuell hat ja jemand ein speziellen Lösungsansatz. Rein theoretisch würde es ja reichen, wenn ich nach dem show() die Werte setze, allerdings ist das Fenster noch nicht richtig aufgebaut, sodass ich also warten muss, bis das passiert ist. Das wäre nach der start()-Methode in der Main. Danach befindet sich die Applikation aber in einem Wartemodus, sodass erst irgendein Event fliegen müsste, damit die Werte abgefragt werden.

Das Problem habe ich mit einer einfachen "Hello World!"-Applikation dargestellt.

Hier die Main:
Java:
package sample;

import controller.Controller;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(getClass().getResource("/view/sample.fxml"));
        
        Parent root = fxmlLoader.load();
        
        Controller controller = fxmlLoader.getController();
        controller.setStage(primaryStage);
        
        primaryStage.setTitle("Hello World");
        
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);

        System.out.println("before show sceneW " + scene.getWidth());
        System.out.println("before show sceneH " +  scene.getHeight());
        System.out.println("before show stageW " + primaryStage.getWidth());
        System.out.println("before show stageH " + primaryStage.getHeight());

        primaryStage.show();

        System.out.println("after show sceneW " + scene.getWidth());
        System.out.println("after show sceneH " +  scene.getHeight());
        System.out.println("after show stageW " + primaryStage.getWidth());
        System.out.println("after show stageH " + primaryStage.getHeight());

        primaryStage.setMinWidth(primaryStage.getWidth());
        primaryStage.setMinHeight(primaryStage.getHeight());
    }


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

Hier der Code vom View:
HTML:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>

<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="275.0" minWidth="300.0" prefHeight="275.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.Controller">
  <columnConstraints>
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
  </columnConstraints>
  <rowConstraints>
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
  </rowConstraints>
   <children>
      <Button mnemonicParsing="false" onAction="#btnAction" text="Button" />
   </children>
</GridPane>

Hier der Code des Controllers zur Ausgabe der Werte nach dem fertigen Fensteraufbau:
Java:
package controller;

import javafx.fxml.FXML;
import javafx.stage.Stage;

public class Controller {
    private Stage stage;

    @FXML
    public void btnAction() {
        System.out.println("stageW " + stage.getWidth());
        System.out.println("stageH " + stage.getHeight());
    }

    public void setStage(Stage stage) {
        this.stage = stage;
    }
}

Mit freundlichen Grüßen
Krappe87
 

Tom299

Bekanntes Mitglied
Vielleicht vor dem show noch ein
Java:
primaryStage.sizeToScene();

Aber ich hatte auch schon Probleme mit der Height/Width und daß die Stage erst im Controller NACH dem initialize fertig aufgebaut ist. Deswegen hab ich meinem Controller eine initScreen-Methode per Interface spendiert, darin kann ich dann Code ausführen, der nach dem kompletten Aufbau der GUI ausgeführt wird, so wie bei dir mit btnAction.
Ich würde also die minHeight/minWidth erst setzen, nachdem der 1. Screen angezeigt wird. Kann auch sein, daß vorher height/width = 0 sind. Du kannst dir ja globale width/heigt oder ein boolean hinterlegen, damit du nur beim 1. Mal das Minimum setzt.
Bei mir läuft das ähnlich, ich setze den Login-Screen erst mal fest (z.B. 800*600) und bei einem Resize merk ich mir dann die neuen Größen und setze diese für alle folgenden Stages.
 

Krappe87

Mitglied
Moin,

danke erstmal für deine Antwort. Leider funktioniert das mit dem sizeToScene() nicht. Das blöde an der Geschichte ist auch, dass der Aufbau des Fenster betriebssystemabhängig ist. Ich hab das hier auf allen drei meiner Systeme ausgeführt
Java:
primaryStage.show();

        scene.widthProperty().addListener(new ChangeListener<Number>() {
            int num = 0;
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                System.out.println(num++ + ". sceneWidth new:" + newValue + " \told: " + oldValue);
            }
        });
        scene.heightProperty().addListener(new ChangeListener<Number>() {
            int num = 0;
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                System.out.println(num++ + ". sceneHeight new:" + newValue + " \told: " + oldValue);
            }
        });

        primaryStage.widthProperty().addListener(new ChangeListener<Number>() {
            int num = 0;
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                System.out.println(num++ + ". stageWidth new:" + newValue + " \told: " + oldValue);
            }
        });
        primaryStage.heightProperty().addListener(new ChangeListener<Number>() {
            int num = 0;
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                System.out.println(num++ + ". stageHeight new:" + newValue + " \told: " + oldValue);
            }
        });
unter den beiden Linux-Systemen kam eine Ausgabe, unter Windows nicht. Der ChangeListener zieht aber (unter Linux) nicht erst, wenn der User das Fenster resized, sondern das Betriebssystem verändert schon die Werte beim Bau des Fensters.

Code:
0. sceneWidth new:8.0     old: 300.0
0. sceneHeight new:33.0     old: 275.0
0. stageWidth new:16.0     old: 300.0
0. stageHeight new:66.0     old: 275.0
1. sceneWidth new:300.0     old: 8.0
1. sceneHeight new:275.0     old: 33.0
1. stageWidth new:308.0     old: 16.0
1. stageHeight new:308.0     old: 66.0

Entweder brauche ich die beiden ersten scene-Werte (8 und 33) oder die letzten Stage Werte. Das war aber unter Kubuntu, unter Ubuntu sieht die Ausgabe direkt wieder anders aus. Das heißt ja, dass man, bis die start()-Methode abgearbeitet ist, gar keinen Code diesbezüglich ausführen braucht, da es je Betriebssystem andere Auswirkungen hat.

Ich probier mal weiter rum, vielleicht fällt mir ja irgendwann was gescheites ein :D
 

Tom299

Bekanntes Mitglied
Ich hab den WindowListener benutzt:
Java:
                // Listener zum Resizen der Scenen
                scene.windowProperty().addListener(new ChangeListener<Window>() {
                    @Override
                    public void changed(ObservableValue<? extends Window> observable, Window oldValue, Window newValue) {
                        if (newValue != null) {
                            if (newValue.getWidth() != Globals.applicationWidth.doubleValue() && newValue.getWidth() > 0.0) {
                                Globals.applicationWidth = newValue.getWidth();
                            }
                            if (newValue.getHeight() != Globals.applicationHeight.doubleValue() && newValue.getHeight() > 0.0) {
                                Globals.applicationHeight = newValue.getHeight();
                            }
                        }
                    }
                });
 

Krappe87

Mitglied
Moin moin,

danke für deinen Hinweis, allerdings hat das bei mir keinerlei Auswirkungen gezeigt. Dafür habe ich mich mal weiter mit dem Thema befasst und ich habe mit dem einfachen EventHandler etwas experimentiert. Festgestellt habe ich, dass die Werte die ich benötige - unabhängig vom System - nie vorhanden sind, wenn die start()-Methode nicht fertig abgearbeitet und das Fenster endgültig - für den Nutzer sichtbar - aufgebaut ist. Daher bediene ich mich der Variante, dass ich den Code einfach später ausführen lasse.

Java:
private void initEventHandler() {
    eventHandler = new EventHandler<Event>() {
        @Override
        public void handle(Event event) {
            if (event.getEventType().getName().equals("WINDOW_SHOWING")) {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        if ((tempStage.getHeight() > height) && (tempStage.getWidth() >= width)) {
                            removeStageEventHandler();
                            setStageMinWidthAndHeight(tempStage.getWidth(),
                                    tempStage.getHeight());
                        } else {
                            System.out.println("Werte zu klein.");
                        }
                    }
                });
            }
        }
    };
}


Mit dem bin ich zu meinem gewünschten Ergebnis gekommen. Beim Aufbau des Fensters gibt es zwei Events, die für mich relevant sind: WINDOW_SHOWING und WINDOW_SHOW. Beim WINDOW_SHOWING grätsch ich dazwischen und setze meine Werte. Beim rumexperimentieren habe ich mir eine globale Stage "tempStage" und zwei double Variablen "height, width" erstellt. Die Methode removeStageEventHandler() entfernt den EventHandler für die Stage, bevor ich die gewünschten Werte setze, damit das ganze nicht in einer Endlosschleife endet.

Java:
private void showRootLayout(final Pane layout) {
    initEventHandler();
    // Show the scene containing the root layout.
    final Scene scene = new Scene(layout);
    setStageValues(primaryStage, layout.getWidth(), layout.getHeight());

    primaryStage.addEventHandler(EventType.ROOT, eventHandler);
    primaryStage.setScene(scene);

    primaryStage.show();
}

private void removeStageEventHandler() {
    tempStage.removeEventHandler(EventType.ROOT, eventHandler);
}

private void setStageMinWidthAndHeight(final double width, final double height) {
    tempStage.setMinHeight(height);
    tempStage.setMinWidth(width);
}

private void setStageValues(final Stage stage, final double width, final double height) {
    this.tempStage = stage;
    this.width = width;
    this.height = height;
}

Die initEventHandler()-Methode kann man im Prinzip auch im Konstruktor aufrufen, wenn man den EventHandler öfters benutzen möchte. Den hab ich jetzt nur in die Methode gepackt, damit nachvollziehbar ist, wann der gesetzt werden muss.

Das nur zur Erklärung für diejenigen, die vielleicht auch mal so ein Problem haben und keine Lösung dafür finden.

Gruß Krappe
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
Juelin Probleme bei Stage.close() AWT, Swing, JavaFX & SWT 23
T Elemente auf vorheriger Stage, nach Wechsel der Stage ansprechen AWT, Swing, JavaFX & SWT 32
T Getter und Setter für eine Stage AWT, Swing, JavaFX & SWT 6
missy72 JavaFX Wiederholen einer IF-Abfrage beim erneuten Öffnen einer Stage AWT, Swing, JavaFX & SWT 11
OSchriever Auf Stage von FXML-Controller zugreifen AWT, Swing, JavaFX & SWT 12
temi JavaFX Mehrere Views mit Stage.setScene() oder mit Scene.setRoot()? AWT, Swing, JavaFX & SWT 7
D JavaFX Ein Parameter von einem Stage(GUI) zu einem anderen übergeben AWT, Swing, JavaFX & SWT 6
E Aktuelle Uhrzeit auf jeder Stage anzeigen lassen (JavaFX) AWT, Swing, JavaFX & SWT 2
ralfb1105 JavaFX Wie Text Label in neuem Window von Main Stage setzen? AWT, Swing, JavaFX & SWT 6
R Größe von Scene und stage an grid anpassen AWT, Swing, JavaFX & SWT 4
Neumi5694 java.awt.Window nach javafx.stage.Window AWT, Swing, JavaFX & SWT 1
S JavaFX GridPane Zellen Seitenverhätnis passend ändern mit der Stage AWT, Swing, JavaFX & SWT 0
MiMa Jumping Stage Flash AWT, Swing, JavaFX & SWT 8
K Globaler Stage- und Taskmanager AWT, Swing, JavaFX & SWT 3
MaxG. JavaFX JavaFX Stage nicht minimierbar machen AWT, Swing, JavaFX & SWT 2
K JavaFX Stage wird nicht angezeigt AWT, Swing, JavaFX & SWT 9
F JavaFX Mit einer Methode auf Stage zugreifen. AWT, Swing, JavaFX & SWT 8
E JavaFX Stage.show() in ursprünglichem Thread starten AWT, Swing, JavaFX & SWT 7
Tort-E JavaFX Stage reload, refresh ... AWT, Swing, JavaFX & SWT 3
C Java FX Probleme beim Schließen einer Stage AWT, Swing, JavaFX & SWT 11
Z JAVAFX Stage über Controller weitergeben um css-file zu laden AWT, Swing, JavaFX & SWT 4
N JavaFX Stage aktualisieren AWT, Swing, JavaFX & SWT 5
M JavaFX Stage in einer FXML-Controllerklasse ermitteln? AWT, Swing, JavaFX & SWT 5
JAVAnnik JavaFX Maximize undecorated Stage (JavaFX 8) AWT, Swing, JavaFX & SWT 3
V kann ich in einer Klasse stage und scene deklarieren, aber in anderen Klassen Inhalte hinzufügen ? AWT, Swing, JavaFX & SWT 5
Tort-E JavaFX Mehere "Widgets" in einer Stage AWT, Swing, JavaFX & SWT 1
R JavaFX Stage.close() funktioniert nicht im jar-File AWT, Swing, JavaFX & SWT 2

Ähnliche Java Themen

Neue Themen


Oben