检测舞台何时再次聚焦并加载场景

Detect when stage again in focus and scene is loaded

我有一个父级舞台,可以在其上显示弹出窗口。 这是代码:

private static Stage chooseBreedStage;
    static {
        chooseBreedStage = new Stage();
        chooseBreedStage.setTitle("Choose breed");
        chooseBreedStage.initOwner(AppKitty.getStage());
        chooseBreedStage.initModality(Modality.WINDOW_MODAL);
        chooseBreedStage.setScene(SceneManager.getInstance().getScene(SceneEnum.CHOOSE_BREED_SCREEN));
    }

    public void showChooseBreedPopup() {
        chooseBreedStage.showAndWait();
    }

在显示弹出窗口之前,我通过 setEffect() 方法模糊了父节点。 弹出窗口关闭时如何去除模糊效果?

在我的尝试中,尽管弹出窗口已经消失,但我总是面临问题,父屏幕控制器中@FXML 注释的元素为空,但它们已经可见并且不会被弹出窗口隐藏

是否有任何侦听器在舞台未被阻塞且场景已加载时处理事件,以便我可以与@FXML 对象交互?

这是初级阶段设置的部分场景控制器:

        public class MainScreenController {
        private GaussianBlur blur = new GaussianBlur();
            @FXML
            public StackPane mainScreen;
        
            public void blurOn() {
                mainScreen.setEffect(blur);
            }
            public void blurOff() {
                mainScreen.setEffect(null);
            }
        public void makeTestClick(ActionEvent event) {
            blurOn();
           SceneManager.getInstance().getController(ChooseBreedScreenController.class)
                    .showChooseBreedPopup(); 
        }
    }

SceneManagerclass代码(@Data为lombok注解,可替换为getters ad setters):

public class SceneManager {
    private final Map<SceneEnum, SceneData> sceneData = new HashMap<>();

    @Data
    private class SceneData {
        private Scene scene;
        private FXMLLoader loader;
        private Controller controller;

        SceneData(Scene scene, FXMLLoader loader) {
            setScene(scene);
            setLoader(loader);
            setController(controller);
        }
    }

    private static SceneManager instance = new SceneManager();

    public static SceneManager getInstance() {
        return instance;
    }

    private SceneManager() {
    }

    public <T extends Controller> T getController(Class<T> controllerClass) {
        SceneEnum sceneEnum = getCorrespondingEnum(controllerClass);
        if (sceneData.get(sceneEnum) == null || sceneData.get(sceneEnum).getController() == null) {
            initSceneController(controllerClass);
        }
        return (T) sceneData.get(sceneEnum).getController();
    }

    public Controller getController(SceneEnum sceneEnum) {
        return getController(sceneEnum.getControllerClass());
    }

    private <T extends Controller> void initSceneController(Class<T> controllerClass) {
        try {
            SceneEnum sceneEnum = getCorrespondingEnum(controllerClass);
            if (sceneData.get(sceneEnum) == null) {
                initScene(sceneEnum);
            }
            sceneData.get(sceneEnum).setController(controllerClass.getConstructor().newInstance());
        } catch (Exception e) {
            //todo here should be popup with error
        }
    }

    private <T extends Controller> SceneEnum getCorrespondingEnum(Class<T> controllerClass) {
        Optional<SceneEnum> desiredSceneEnum = Arrays.stream(SceneEnum.values())
                .filter(sceneEnum -> controllerClass.isAssignableFrom(sceneEnum.getControllerClass()))
                .findFirst();
        return desiredSceneEnum.orElseGet(() -> {
            //todo here should be shown popup with error
            return null;
        });
    }

    public Scene getScene(SceneEnum sceneEnum) {
        if (sceneData.get(sceneEnum) == null) {
            initScene(sceneEnum);
        }
        return sceneData.get(sceneEnum).getScene();
    }

    private void initScene(SceneEnum sceneEnum) {
        ConsumerWithException<SceneEnum, IOException> loadingScene = sceneType -> {
            FXMLLoader loader = getFxmlLoader(sceneEnum);
            Scene scene = new Scene(loader.load());
            sceneData.put(sceneEnum, new SceneData(scene, loader));
        };
        try {
            loadingScene.accept(sceneEnum);
        } catch (IOException initializeScreenException) {
            try {
                //Loading app error popup scene, before calling getController() method for
                //this popup, to avoid recursion if loading app error popup scene producing exception as well
                //(it may be in initScene method inside getController method)
                loadingScene.accept(SceneEnum.APP_ERROR_POPUP);
                getController(AppErrorPopupController.class)
                        .showAppErrorPopup(sceneEnum);
            } catch (IOException initializeErrorPopupException) {
                //todo make a better logging
                initializeErrorPopupException.printStackTrace();
            }
        }
    }

    private FXMLLoader getFxmlLoader(SceneEnum scene){
        return new FXMLLoader(this.getClass().getResource(scene.getScenePath()));
    }
}

AppKitty class:

public class AppKitty extends Application {
    private static Stage stage;

    public static Stage getStage() {
        return stage;
    }

    public static void setScene(SceneEnum sceneEnum) {
        getStage().setScene(SceneManager.getInstance()
                        .getScene(sceneEnum));
    }

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

    @Override
    public void start(Stage primaryStage) {
        stage = primaryStage;
        primaryStage.setTitle("AppKitty");
        primaryStage.setScene(SceneManager.getInstance().getScene(SceneEnum.MAIN_SCREEN));
        primaryStage.show();
    }
}

mainScreen.fxml :

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<?import javafx.geometry.Insets?>
<StackPane xmlns="http://javafx.com/javafx"
           xmlns:fx="http://javafx.com/fxml"
           fx:controller="applicationinterface.controllers.MainScreenController"
           prefHeight="400.0" prefWidth="600.0"
           fx:id="mainScreen"
           stylesheets="@../styles/mainScreenBackground.css"
           id="background_image">

    <Label fx:id="title" text="What do you want?" alignment="TOP_CENTER"
           stylesheets="@../styles/outline.css" styleClass="outline" StackPane.alignment="TOP_CENTER">

    </Label>
    <HBox
            fx:id="bottomBar" id="outline.label" alignment="BOTTOM_CENTER"
            stylesheets="@../styles/outline.css" spacing="20" StackPane.alignment="BOTTOM_CENTER">
        <StackPane.margin>
            <Insets bottom="10"/>
        </StackPane.margin>
        <Button fx:id="randomKittyButton" text="Random Kitty" onAction="#randomKittyClick" focusTraversable="false"
                stylesheets="@../styles/buttonBorder.css" styleClass="button"/>
        <Button fx:id="makeTestButton" text="Take the test" onAction="#makeTestClick" focusTraversable="false"
                stylesheets="@../styles/buttonBorder.css" styleClass="button"/>
    </HBox>
</StackPane>

根据您的代码,您需要在静态块中声明时包含弹出阶段的关闭处理程序。您可以包括以下任何处理程序..(取决于您的需要)

chooseBreedStage.setOnCloseRequest(e1->{
    // Remove blur of AppKitty.getStage()
});

chooseBreedStage.addEventHandler(WindowEvent.WINDOW_HIDDEN,e1->{
    // Remove blur of AppKitty.getStage()
});

chooseBreedStage.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST,e1->{
    // Remove blur of AppKitty.getStage()
});