I'm creating a board game in JavaFx, and it works fine in my desktop PC, with a 1920x1080 screen. When i try to open it in my Laptop (which is 1920x1080 as well, but with the default screen size at 125%), it doesn't show the elements properly anymore (their size overflows the bounds of the screen). The only way to make the app work properly is to run it on a 1920x1080 screen with default scree size at 100%
I need a way to scale the whole stage to the right resolution
This is my Main.java:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws IOException {
final double WIDTH = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
final double HEIGHT = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
Parent board = FXMLLoader.load(getClass().getResource("/board.fxml"));
Pane innerBoard = ((Pane)board.getChildrenUnmodifiable().get(0));
Assert.assertCmp(innerBoard.getId(), "BOARD");
Scene scene = new Scene(board, WIDTH, HEIGHT);
scene.getStylesheets().add(getClass().getResource("/style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.setResizable(true);
innerBoard.setVisible(false);
primaryStage.setFullScreenExitHint("");
primaryStage.show();
innerBoard.setVisible(true);
}
}
This is the FXML:
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Pane id="BOARD" fx:id="innerBoard" layoutX="356.0" layoutY="-7.0" style="-fx-background-color: #cfffd1;">
<children>
<!-- Lots of children -->
</children>
</Pane>
</children>
</Pane>
I recommend you forego Pane and numeric positions and sizes. You want your board to be capable of displaying in any resolution; explicit positions and sizes directly contradict that.
Layouts are the best way to make your board fit into any size.
(Of course, layouts only work with rectangular board regions; I will assume you aren’t going for something complex like a circular or hexagon-based board, or a board with winding paths. In those cases, a Canvas may be a better choice than a Pane.)
GridPane allows setting RowConstraints and ColumnConstraints, each of which can be percentage-based. If rows and columns have a size that is a percentage of the width of their parent, you get scaling for free. Here is a very primitive rendition of a Monopoly board:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.Priority;
public class MonopolyBoard
extends Application {
// Source:
// https://commons.wikimedia.org/wiki/File:Store_Building_Flat_Icon_Vector.svg
private static final String SIDE_SPACE_IMAGE_URI =
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/53"
+ "/Store_Building_Flat_Icon_Vector.svg"
+ "/240px-Store_Building_Flat_Icon_Vector.svg.png";
// Source:
// https://commons.wikimedia.org/wiki/File:Car_icon_transparent.png
private static final String CORNER_SPACE_IMAGE_URI =
"https://upload.wikimedia.org/wikipedia/commons/7/7e"
+ "/Car_icon_transparent.png";
@Override
public void start(Stage stage) {
GridPane grid = new GridPane();
Node[] row = new Node[11];
row[0] = createSpace(CORNER_SPACE_IMAGE_URI);
row[10] = createSpace(CORNER_SPACE_IMAGE_URI);
for (int i = 1; i <= 9; i++) {
row[i] = createSpace(SIDE_SPACE_IMAGE_URI);
}
grid.addRow(0, row);
for (int i = 1; i <= 9; i++) {
grid.add(createSpace(SIDE_SPACE_IMAGE_URI), 0, i);
grid.add(createSpace(SIDE_SPACE_IMAGE_URI), 10, i);
}
row[0] = createSpace(CORNER_SPACE_IMAGE_URI);
row[10] = createSpace(CORNER_SPACE_IMAGE_URI);
for (int i = 1; i <= 9; i++) {
row[i] = createSpace(SIDE_SPACE_IMAGE_URI);
}
grid.addRow(10, row);
grid.getColumnConstraints().add(createColumnConstraints(2 / 11.0));
for (int i = 1; i <= 9; i++) {
grid.getColumnConstraints().add(createColumnConstraints(1 / 11.0));
}
grid.getColumnConstraints().add(createColumnConstraints(2 / 11.0));
grid.getRowConstraints().add(createRowConstraints(2 / 11.0));
for (int i = 1; i <= 9; i++) {
grid.getRowConstraints().add(createRowConstraints(1 / 11.0));
}
grid.getRowConstraints().add(createRowConstraints(2 / 11.0));
stage.setScene(new Scene(grid, 50 * 11, 50 * 11));
stage.setTitle("Monopoly");
stage.show();
}
private ColumnConstraints createColumnConstraints(double percentage) {
ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(percentage * 100);
constraints.setHgrow(Priority.ALWAYS);
return constraints;
}
private RowConstraints createRowConstraints(double percentage) {
RowConstraints constraints = new RowConstraints();
constraints.setPercentHeight(percentage * 100);
constraints.setVgrow(Priority.ALWAYS);
return constraints;
}
private Node createSpace(String imageURI) {
BorderPane container = new BorderPane();
container.setStyle(
"-fx-border-width: 1px;"
+ " -fx-border-style: solid;"
+ " -fx-background-image: url('" + imageURI + "');"
+ " -fx-background-position: center;"
+ " -fx-background-repeat: no-repeat;"
+ " -fx-background-size: contain;");
return container;
}
public static void main(String[] args) {
launch(args);
}
}
In addition to using the percentage properties of RowConstraints and ColumnConstraints, the above code takes advantage of CSS image scaling using -fx-background-size: contain.
Resize (or maximize) the window to see the scaling at work. Obviously, if it were fullscreen, the same scaling would apply.