JavaFX from the Trenches – Singleton Controllers

My genealogy application, Evidentia, uses a LOT of stages. Those stages are REUSED a LOT of times. In order to reuse the stages in the most efficient way possible (memory and CPU wise), I implemented the good ole Singleton design pattern.

Now this isn’t glamorous, but then “From the Trenches” is about the day to day stuff in JavaFX, so I wanted to share how I chose to implement the pattern at the controller level.

So what were my “requirements”?

  • Needed to reuse the same stages MANY times but with different data initializing the various fields.
  • Needed to reuse the same stages from DIFFERENT parent windows.
  • If the stage had dynamic controls (controls that could appear/disappear depending on how the user utilized it), the stage had to reinitialize itself to a known state, either after use or before reuse.

Pretty typical Java stuff. Here is the boiler plate for almost ALL my controllers:

public class LicenseInstallerController implements Initializable {
 
// define various @FXML fields to be initialized
 
    private static Stage myStage = null;
    private static LicenseInstallerController instance = null;
 
    public static synchronized void show(MyDataObject myData) throws IOException {
        if (myStage == null) {
	    // things that only need to be done once
	    // notice the reference to the Stage "myStage" is saved in a static variable
            URL location = LicenseInstallerController.class
                   .getResource("/org/ed4becky/evidentia/view/license/LicenseInstaller.fxml");
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(location);
            Scene scene = new Scene((Pane) loader.load(location.openStream()));
 
	    // by now, I can count on the initiialize() method having been called
	    // and a reference to this controller being storedd in "instance" variable
            myStage = new Stage();
            myStage.setScene(scene);
            myStage.setTitle("Evidentia License Installer");
            myStage.initModality(Modality.NONE);
            myStage.getIcons().add(ImageCache.getInstance().get("Evidentia48x48.png"));
 
	    // by setting an owner (this case my MASTER" stage), two things happen
	    // 1) shutdown of app closes window, 2) toFront() works as expected
	    myStage.initOwner(EvidentiaController.myStage.getScene().getWindow());
        }
	// HERE is where I put code to return the state of the stage 
        // and its controls to a known state.
	// if you don't clear out text fields, selection indices, etc... 
        // there will be garbage left from the last time show() was called...
	...
	// anything specific to THIS call to show, like initializing text fields with data 
        // from "myData" is done here, just before myStage.show() is called
	...
        myStage.show();
        myStage.toFront();
    }
 
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        instance = this;
		// one time initializations to controls happen here
		// remember this is called during the loader.load(),
		// so its only called the FIRST time
    }
 
	public static LicenseInstallerController getInstance() {
		// yeah I know, NULL check and synchronize are missing...
		// the getInstance() method is seldom used - usually only for
		// message passing between stages
		return instance;
	}
}

A couple of my own comments:

  • Yes I know, I haven’t protected the instantiation of the instance variable – but in my app I generally know there won’t be multiple threads creating the controller. (I should still go back and clean that up!)
  • I ran into an issue where a stage that had lost focus (not on top, was blurred out) still had popup tool tips displaying and forcing the background window to the top. This was resolved by a call to initOwner(), I assume setting a proper window hierarchy.
  • getInstance() is usually only used for message passing between parent/child windows
  • Why make myStage static? Sometimes I find it useful to add a method that allows parent/child windows to have direct access to certain controls (like status message labels, or progress bars). Other times I need access to the stage in a stop() method.

There are of course, multiple ways to accomplish the stated goals. This is just my (current) way.

Comments welcome…



This entry was posted in JavaFX. Bookmark the permalink.

3 Responses to JavaFX from the Trenches – Singleton Controllers

  1. Mircea says:

    Nice info.

  2. Pingback: JavaFX links of the week, January 7 // JavaFX News, Demos and Insight // FX Experience

  3. Pingback: Java desktop links of the week, January 7 | Jonathan Giles

Add Comment Register



Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>