This is my first FXML application. Previously I've gotten pretty comfortable with Swing. I simply want to save several values from the controls on the stage, so that user-input values reload across runs. The ReliableOneShotCloseHandler
is a JavaFX version of something I posted a while ago here. However, by the time the windowClosing function is called, it seems that the controls have already been disposed of ... ? I get a NullPointerException at the indicated line:
package testfxmlloader;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.control.Label;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
/**
*
* @author maskedcoder
*/
public class FXMLDocumentController implements Initializable {
@FXML
private Label label;
@FXML
private AnchorPane apMain;
@FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
@Override
public void initialize(URL url, ResourceBundle rb) {
ReliableOneShotCloseHandler roscSaveSettings = new ReliableOneShotCloseHandler(apMain.getScene().getWindow(), new ReliableOneShotCloseHandler.CloseDuties() {
@Override
public boolean confirmClose(WindowEvent e) {
return true;
}
@Override
public void windowClosing(WindowEvent e) {
saveSettings();
}
});
}
public void saveSettings() {
System.out.println("save setting: " + label.getText()); // gets NullPointerException because label is null.
}
}
class ReliableOneShotCloseHandler {
public interface CloseDuties {
public boolean confirmClose(WindowEvent e);
public void windowClosing(WindowEvent e);
}
private final CloseDuties closeDuties;
private boolean windowClosingFired = false;
private final EventHandler<WindowEvent> openEvent;
private final EventHandler<WindowEvent> closeEvent;
public ReliableOneShotCloseHandler(Window thisWindow, CloseDuties iniCloseDuties) {
super();
closeDuties = iniCloseDuties;
openEvent = (WindowEvent event) -> {
windowClosingFired = false;
};
closeEvent = (WindowEvent event) -> {
if(!windowClosingFired) {
if(closeDuties.confirmClose(event)) {
closeDuties.windowClosing(event);
windowClosingFired = true;
}
else
event.consume();
}
};
thisWindow.setOnShowing(openEvent);
thisWindow.setOnShown(openEvent);
thisWindow.setOnCloseRequest(closeEvent);
thisWindow.setOnHiding(closeEvent);
thisWindow.setOnHidden(closeEvent);
}
}
public class TestFXMLLoader extends Application {
FXMLLoader fxmll;
FXMLDocumentController fdc;
@Override
public void start(Stage stage) throws Exception {
fdc = new FXMLDocumentController();
fxmll = new FXMLLoader();
fxmll.setBuilderFactory(new JavaFXBuilderFactory());
Parent root = fxmll.load(getClass().getResource("FXMLDocument.fxml"));
fxmll.setController(fdc);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
And here's the FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" fx:id="apMain" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="testfxmlloader.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="126" layoutY="90" onAction="#handleButtonAction" text="Click Me!" />
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
</children>
</AnchorPane>
The windowClosing
function should be called before there's even permission to close, so the controls should all be functioning. The test code above is based off of the NetBeans' default JavaFXML Application, with just the addition of the ReliableOneShotCloseHandler and attendant code. Is there a flaw in my logic? Is there another way to do this?
Update: Some lines of code were included that shouldn't have been. They've been removed.