Easiest way to make my JavaFX application scale with different screen sizes

As my code is now, the content of the app will be cut off by the window if the screen isn’t large enough to support the fixed size I set. I want to know the easiest way to fix this. Rather than changing every UI element, I was hoping there is some way to simply make the UI adjust to whatever size the window is. So the window should be able to be pulled from a corner to resize and the UI moves with it.

Here’s the stage class:

public class Login {
    
    public void show(Stage primaryStage) {
        
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Login.fxml"));
            Parent root = loader.load();
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setResizable(false);
            primaryStage.show();
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

Here’s the .fxml file:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.image.Image?>


<AnchorPane prefHeight="960.0" prefWidth="540.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.login.LoginController">
   <children>
      <ImageView fitHeight="261.0" fitWidth="210.0" layoutX="171.0" layoutY="302.0" pickOnBounds="true" preserveRatio="true">
        <Image url="@ledger.png" />
      </ImageView>
      <Label layoutX="134.0" layoutY="132.0" text="EffortLoggerV2">
         <font>
            <Font size="42.0" />
         </font>
      </Label>
      <Label layoutX="43.0" layoutY="768.0" text="Username">
         <font>
            <Font size="24.0" />
         </font>
      </Label>
      <Label layoutX="43.0" layoutY="818.0" text="Password">
         <font>
            <Font size="24.0" />
         </font>
      </Label>
      <TextField layoutX="164.0" layoutY="770.0" prefHeight="25.0" prefWidth="224.0" />
      <TextField layoutX="164.0" layoutY="820.0" prefHeight="25.0" prefWidth="224.0" />
      <Button fx:id="loginButton" layoutX="416.0" layoutY="770.0" mnemonicParsing="false" prefHeight="73.0" prefWidth="86.0" text="Sign In">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Button fx:id="createAccountButton" layoutX="193.0" layoutY="889.0" mnemonicParsing="false" text="Create an Account">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
   </children>
</AnchorPane>

Is there an easy way to do this, or would this require changing every UI element?

  • 1

    See if the ideas here can help.

    – 

  • @SedJ601 In the question you linked the length and height ratio of the UI elements change based on the size of the window. What I want is for all the UI elements to stay where they are and shrink and grow with the size of the window, but maintain the same length and height ratio if that makes sense.

    – 

  • This example preserves the circles’ aspect ratio as the window is resized.

    – 

  • 1

    If you use the layout panes the way there are intended to be used, then this should just work out of the box. Don’t hard-code any sizes or positions. Use the appropriate layout pane, depending on how you want the components laid out (AnchorPane is almost never what you want). Read the layout tutorial. Eden Coding also has a good tutorial

    – 




  • ”What I want is for all the UI elements to stay where they are and shrink and grow with the size of the window”. This doesn’t really make sense. If the elements stayed where they were and grew, they would overlap each other, which would make them unusable. You need to precisely define how you want the elements to resize and reposition relative to each other when the window resizes. But again, this behaviour is exactly the behaviour implemented by the various layout panes.

    – 

Preface:

It is impossible to create a screen size adaptive layout with fixed sizes.

You can see the problem in combining the words “fixed” and “adaptive”.

Solution:

On every elements, including *Panes:

  1. Use computed sizes as minimum sizes everywhere.
  2. Use computed sizes as preferred sizes everywhere.
  3. Use maxValue as maximum sizes everywhere.
  4. Use hGrow always everywhere where it does not work only with the above.
  5. Set multiline true for Labels.

I am not sure what you are exactly trying to achieve, but using ideas from here, I came up with a starting point that I hope will help. You may need to do a few tutorials on JavaFX Layouts/Parent Nodes. ImageView can be kinda tricky, though. I hope this can help guide you in the right direction. For a login view, in my opinion, that’s one of the few cases where it is okay to set the min, pref, and max sizes to the same values or use a screen that can’t be resized.

FXML

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>


<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="940.0" minWidth="540.0" prefHeight="960.0" prefWidth="540.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Label alignment="CENTER" maxWidth="1.7976931348623157E308" text="EffortLoggerV2">
         <font>
            <Font size="42.0" />
         </font>
         <VBox.margin>
            <Insets top="140.0" />
         </VBox.margin>
      </Label>
      <ImageView fitHeight="261.0" fitWidth="210.0" pickOnBounds="true" preserveRatio="true">
         <VBox.margin>
            <Insets top="100.0" />
         </VBox.margin>
      </ImageView>
      <GridPane hgap="5.0" vgap="10.0">
        <columnConstraints>
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            <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>
         <children>
            <Label alignment="CENTER_RIGHT" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" text="Username" GridPane.columnSpan="2">
               <font>
                  <Font size="24.0" />
               </font>
            </Label>
            <Label alignment="CENTER_RIGHT" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" text="Password" GridPane.columnSpan="2" GridPane.rowIndex="1">
               <font>
                  <Font size="24.0" />
               </font>
            </Label>
            <TextField maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="25.0" prefWidth="224.0" GridPane.columnIndex="2" GridPane.columnSpan="4" />
            <TextField maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="25.0" prefWidth="224.0" GridPane.columnIndex="2" GridPane.columnSpan="4" GridPane.rowIndex="1" />
            <Button fx:id="loginButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Sign In" GridPane.columnIndex="6" GridPane.columnSpan="2" GridPane.rowSpan="2">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
         </children>
         <padding>
            <Insets left="20.0" right="20.0" />
         </padding>
         <VBox.margin>
            <Insets top="140.0" />
         </VBox.margin>
      </GridPane>
      <Button fx:id="createAccountButton" mnemonicParsing="false" text="Create an Account">
         <font>
            <Font size="18.0" />
         </font>
         <VBox.margin>
            <Insets top="100.0" />
         </VBox.margin>
      </Button>
   </children>
</VBox>

Normal

enter image description here

Maximized

enter image description here

Leave a Comment