Gillius's Programming

JavaFX Window Scaling like a PowerPoint Slide on Resize

With JavaFX billed as the preferred new front-end system for Java, and Java 7 becoming prevalent and bundling JavaFX as of 7u6, I've started to take a very hard look at this technology, especially compared to Swing. And from everything I've seen so far, I'm very impressed.

The first application I've had the chance to use JavaFX with is a presentation application on a custom piece of kiosk hardware that has 4 separate screens on it. For my skillset, the choices were either HTML5 or Java. I wasn't keen on trying to figure out how to get 4 separate monitors to work with Chrome browsers or something similar, and I know Java better, so I went the route of JavaFX.

For this application, I wanted to create screens that worked basically like PowerPoint slides. The resolution on the kiosk screens is high and the user is far away, so the "normal size" of the GUI interface would not be sufficient. I wanted something where as I expand the window the content gets larger (like in PowerPoint) rather than stay the same size and reflow, like in a standard GUI application. Fortunately, the transform capabilities in JavaFX make this very easy.

There was only one trick, and that is getting the content to resize normally to some "standard" resolution like 960x540 (half of a 1920x1080 TV) to use as the "100%" size of the application. To do that in JavaFX, you can use a resizable component such as a Pane, placed in a Group, which is a non-resizable component. Then you can place the Group into a container such as StackPane, which keeps it centered, and use the scalex/y settings to resize the content. As the resize is done on the vector drawing and not at the image level, detail is entirely preserved.

Here is the code that I used. In this example I get the actual GUI content from an FXML file, but it could be created by hand. This is also an excellent example of the new binding technique in JavaFX where I am able to bind the window's size to the scale property without creating an explicit listener like I would need with traditional Swing.

	public void start( Stage stage ) throws Exception {
		FXMLLoader loader = new FXMLLoader( getClass().getResource( "Content.fxml" ) );
		Region contentRootRegion = (Region) loader.load();

		//Set a default "standard" or "100%" resolution
		double origW = 960;
		double origH = 540;

		//If the Region containing the GUI does not already have a preferred width and height, set it.
		//But, if it does, we can use that setting as the "standard" resolution.
		if ( contentRootRegion.getPrefWidth() == Region.USE_COMPUTED_SIZE )
			contentRootRegion.setPrefWidth( origW );
		else
			origW = contentRootRegion.getPrefWidth();

		if ( contentRootRegion.getPrefHeight() == Region.USE_COMPUTED_SIZE )
			contentRootRegion.setPrefHeight( origH );
		else
			origH = contentRootRegion.getPrefHeight();

		//Wrap the resizable content in a non-resizable container (Group)
		Group group = new Group( contentRootRegion );
		//Place the Group in a StackPane, which will keep it centered
		StackPane rootPane = new StackPane();
		rootPane.getChildren().add( group );

		stage.setTitle( "My Slide" );
		//Create the scene initally at the "100%" size
		Scene scene = new Scene( rootPane, origW, origH );
		//Bind the scene's width and height to the scaling parameters on the group
		group.scaleXProperty().bind( scene.widthProperty().divide( origW ) );
		group.scaleYProperty().bind( scene.heightProperty().divide( origH ) );
		//Set the scene to the window (stage) and show it
		stage.setScene( scene );
		stage.show();
	}