java - 如何在 FXML 控件状态被丢弃之前保存它们?

标签 java fxml

这是我的第一个 FXML 应用程序。以前我对 Swing 已经很熟悉了。我只是想保存舞台上控件中的多个值,以便用户输入值在运行期间重新加载。 ReliableOneShotCloseHandler 是我 posted a while ago here 的 JavaFX 版本。 。然而,当调用 windowClosing 函数时,控件似乎已经被释放了......?我在指定的行收到 NullPointerException:

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);
    }

}

这是 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>

应该在获得关闭权限之前调用 windowClosing 函数,因此控件应该全部正常工作。上面的测试代码基于 NetBeans 的默认 JavaFXML 应用程序,仅添加了 ReliableOneShotCloseHandler 和附带代码。我的逻辑有问题吗?还有其他方法可以做到这一点吗?

更新:包含了一些不应该包含的代码行。它们已被删除。

最佳答案

从架构的角度来看,这段代码相当困惑。一般来说,我会明确区分 Controller 和应用程序类。因此,应用程序类仅加载 FXML 文件(从该文件引用 Controller 类),然后将其分配给场景或更准确地说是舞台。因此, Controller 类仅负责管理来自控制元素的任何事件作为 onCloseRequest-Event。

另一个问题可能是同时使用 EventFilterEventHandler。如果您在舞台上使用 EventFilter,则其所有节点和舞台本身将永远不会被调用其 EventHandler

因此,最简单的解决方案是直接在 Controller 类的舞台上添加一个 EventHandler,您可以从其中直接访问所有其他控件(例如标签)。

Main.java

public class Main extends Application {

@Override
public void start(Stage primaryStage) throws Exception{
    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Parent root = (Parent) loader.load();
    primaryStage.setTitle("Hello World");
    primaryStage.setScene(new Scene(root, 300, 275));
    primaryStage.show();

    Controller controller = (Controller)loader.getController();
    controller.setStage(primaryStage);
}


public static void main(String[] args) {
    launch(args);
}

} 

样本.fxml

<AnchorPane fx:id="apMain" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <Button fx:id="button" layoutX="39.0" layoutY="35.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Button" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0" />
      <Label fx:id="label" layoutX="52.0" layoutY="102.0" text="Label" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="50.0" />
   </children>
</AnchorPane>

Controller .java

public class Controller implements Initializable {

@FXML
public AnchorPane apMain;

@FXML
public Button button;

@FXML
public Label label;

@Override
public void initialize(URL location, ResourceBundle resources) {

}

public void handleButtonAction() {
    System.out.println("You clicked me!");
    label.setText("Hello World!");
}

public void setStage(Stage stage){
    stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
        @Override
        public void handle(WindowEvent event) {
            System.out.println(label.getText());
        }
    });
}

}

关于java - 如何在 FXML 控件状态被丢弃之前保存它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32486570/

相关文章:

image - JavaFX:在 .FXML 文件中嵌入编码图像

java - 从 fxml 实例化的 ObservableArrayList 返回值不符合预期

java - 创建和显示布局时如何将焦点设置在 View 上?

java - Tomcat 5.5 无法在 eclipse 中启动 - 5 分钟后超时

java - 批处理在 Spring+Hibernate+JPA 中内存不足

java - 如何在 Javafx 中为 XML 文件创建常量变量

java - 如何使用 Java 8 Stream API 减少组并返回排序列表

java - 如何使 Maven Javadoc 插件适用于任何 Java 版本

java - 为什么此 FXML 应用程序中不显示 TreeView 根?

java - 如何从 Controller 文件中的 .fxml 文件调用对象?