I like to set up the preferred width/height of some components in my JavaFX UI, as some of the dimensions that it calculates by default are not to my liking. For example, I have some tables where currently all columns have the same width, but some should be larger than others. Also, I have some split panes that currently all have a 50-50 division which I'd like to change, and so on.
However I don't like to hard-code the values in the fxml files, because the "best" layout depends on the screen resolution, and also different users might have a different idea what is "best", it may even depend on their data how big or small they want a component to be.
My idea was to let the user layout the UI to his liking, and save his layout, then re-load it on startup. However from my research it seems there is no out of the box support to persist a UI layout, so it seems I have to manually define fx:id
's for all relevant components and save their widths/heights to e.g. a file, and read that on startup and set the values likewise.
But I wonder, is there really not a better way to do this? Is it maybe be possible to iterate over all nodes in a scene and collect their dimensions, without having to know what speficially is contained in a scene? And also, when the scene is composed of multiple different fxml's? If yes, then I could collect that into a Map<fx:id,Dimensions>
and apply this map on startup, what do you think of this approach?
Anyway, as I wouldn't know how to do this, I first started with an attempt to do it manually for a single component, but even that didn't work. See the sample application below, where I try to set up the preferred width of the left split pane. The setPrefWidth
has no effect, the splitpane is still divided in the middle.
package com.example.javafxsample;
import javafx.application.Application;
import javafx.collections.ObservableMap;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.stage.Stage;
public class SampleApp extends Application {
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(SampleApp.class.getResource("view.fxml"));
Scene scene = new Scene(loader.load());
stage.setScene(scene);
stage.setMaximized(true);
stage.show();
initLayout(loader);
}
private void initLayout(FXMLLoader loader) {
ObservableMap<String, Object> namespace = loader.getNamespace();
SplitPane leftSplitPane = (SplitPane) namespace.get("leftSplitPane");
leftSplitPane.setPrefWidth(500); // doesn't work
// maxWidth however works: leftSplitPane.setMaxWidth(500);
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<SplitPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
orientation="HORIZONTAL">
<SplitPane fx:id="leftSplitPane" orientation="VERTICAL">
<Label text="left-top" maxHeight="Infinity"/>
<Label text="left-bottom" maxHeight="Infinity"/>
</SplitPane>
<Label text="right" maxWidth="Infinity"/>
</SplitPane>