如何从ObservableValue获得呼叫者舞台?扩展Boolean>在焦点更改听众

发布于 2025-02-11 17:10:11 字数 10411 浏览 0 评论 0原文

我想将焦点侦听器作为传递静态方法的静态变量,其功能是关闭一个丢失该阶段的阶段。

我有代码:

主类

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
        Scene scene = new Scene(loader.load(), 565, 551);
        primaryStage.setScene(scene);
        Controller controller = loader.getController();
        primaryStage.show();
        controller.setStage1InitOwner();
    }

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

控制器类

 public class Controller implements Initializable {
    @FXML
    private AnchorPane anchorPane;

    // stage1 suppose to be a small dialogs
    private final Stage stage1 = new Stage();

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        stage1.setScene(new Scene(new Pane(new Label("a Dialog")), 200, 200));

        // giving the listener to stage1 from static method
        stage1.focusedProperty().addListener(Prepare.getFocusListener());
        stage1.initStyle(StageStyle.TRANSPARENT);
    }

    public void setStage1InitOwner() {
        stage1.initOwner(anchorPane.getScene().getWindow());
        stage1.show();
    }
}

准备类(内部的静态方法和变量)

  public class Prepare  {
    public static ChangeListener<Boolean> focusListener = new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean t1) {
            if (!t1) ;
            // get the stage from observableValue and close it when focus is lost.
        }
    };
    public static ChangeListener<Boolean> getFocusListener()
    {
        return focusListener;
    }
}

FXML文件

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

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

<AnchorPane fx:id="anchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="551.0" prefWidth="565.0" style="-fx-background-color: #fffe00;" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
   </children>
</AnchorPane>

现在我想从ChangElistener内的可观察值获得呼叫者阶段。 我试图将其施放到布尔普洛蒂(BooleanProperty),然后使用getBean()

 if (!t1) {
            BooleanProperty booleanProperty = (BooleanProperty) observableValue;
            Stage stage = (Stage) booleanProperty.getBean();
            stage.close();
        }

,但是我得到了以下错误:

    Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl cannot be cast to class javafx.beans.property.BooleanProperty (javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl and javafx.beans.property.BooleanProperty are in module javafx.base of loader 'app')
    at sample.Prepare$1.changed(Prepare.java:13)
    at sample.Prepare$1.changed(Prepare.java:9)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.base/javafx.beans.property.ReadOnlyBooleanPropertyBase.fireValueChangedEvent(ReadOnlyBooleanPropertyBase.java:78)
    at javafx.base/javafx.beans.property.ReadOnlyBooleanWrapper.fireValueChangedEvent(ReadOnlyBooleanWrapper.java:103)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:111)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145)
    at javafx.graphics/javafx.stage.Window.setFocused(Window.java:678)
    at javafx.graphics/javafx.stage.Window$1.setFocused(Window.java:150)
    at javafx.graphics/com.sun.javafx.stage.WindowHelper.setFocused(WindowHelper.java:112)
    at javafx.graphics/com.sun.javafx.stage.WindowPeerListener.changedFocused(WindowPeerListener.java:64)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:126)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$4(GlassWindowEventHandler.java:178)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:176)
    at javafx.graphics/com.sun.glass.ui.Window.handleWindowEvent(Window.java:1336)
    at javafx.graphics/com.sun.glass.ui.Window.notifyFocus(Window.java:1315)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:833)
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl cannot be cast to class javafx.beans.property.BooleanProperty (javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl and javafx.beans.property.BooleanProperty are in module javafx.base of loader 'app')
    at sample.Prepare$1.changed(Prepare.java:13)
    at sample.Prepare$1.changed(Prepare.java:9)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.base/javafx.beans.property.ReadOnlyBooleanPropertyBase.fireValueChangedEvent(ReadOnlyBooleanPropertyBase.java:78)
    at javafx.base/javafx.beans.property.ReadOnlyBooleanWrapper.fireValueChangedEvent(ReadOnlyBooleanWrapper.java:103)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:111)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145)
    at javafx.graphics/javafx.stage.Window.setFocused(Window.java:678)
    at javafx.graphics/javafx.stage.Window$1.setFocused(Window.java:150)
    at javafx.graphics/com.sun.javafx.stage.WindowHelper.setFocused(WindowHelper.java:112)
    at javafx.graphics/com.sun.javafx.stage.WindowPeerListener.changedFocused(WindowPeerListener.java:64)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:126)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$4(GlassWindowEventHandler.java:178)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:176)
    at javafx.graphics/com.sun.glass.ui.Window.handleWindowEvent(Window.java:1336)
    at javafx.graphics/com.sun.glass.ui.Window.notifyFocus(Window.java:1315)
    at javafx.graphics/com.sun.glass.ui.win.WinWindow._close(Native Method)
    at javafx.graphics/com.sun.glass.ui.Window.close(Window.java:352)
    at javafx.graphics/com.sun.glass.ui.win.WinWindow.close(WinWindow.java:316)
    at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.lambda$close$4(WindowStage.java:824)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithRenderLock(QuantumToolkit.java:442)
    at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.close(WindowStage.java:817)
    at javafx.graphics/javafx.stage.Window$12.invalidated(Window.java:1157)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145)
    at javafx.graphics/javafx.stage.Window.setShowing(Window.java:1190)
    at javafx.graphics/javafx.stage.Window.hide(Window.java:1215)
    at javafx.graphics/com.sun.javafx.stage.WindowCloseRequestHandler.dispatchBubblingEvent(WindowCloseRequestHandler.java:45)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/com.sun.javafx.stage.WindowPeerListener.closing(WindowPeerListener.java:93)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:147)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$4(GlassWindowEventHandler.java:178)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:176)
    at javafx.graphics/com.sun.glass.ui.Window.handleWindowEvent(Window.java:1336)
    at javafx.graphics/com.sun.glass.ui.Window.notifyClose(Window.java:1241)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:833)

那是什么问题,什么是获得舞台的最佳方法。

预先感谢Stackoverflow团队。

I want to have a focus listener as a static variable that is passed throw static method, and its function is to close a stage when focus on that stage is lost.

i have the code:

Main class

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
        Scene scene = new Scene(loader.load(), 565, 551);
        primaryStage.setScene(scene);
        Controller controller = loader.getController();
        primaryStage.show();
        controller.setStage1InitOwner();
    }

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

Controller class

 public class Controller implements Initializable {
    @FXML
    private AnchorPane anchorPane;

    // stage1 suppose to be a small dialogs
    private final Stage stage1 = new Stage();

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        stage1.setScene(new Scene(new Pane(new Label("a Dialog")), 200, 200));

        // giving the listener to stage1 from static method
        stage1.focusedProperty().addListener(Prepare.getFocusListener());
        stage1.initStyle(StageStyle.TRANSPARENT);
    }

    public void setStage1InitOwner() {
        stage1.initOwner(anchorPane.getScene().getWindow());
        stage1.show();
    }
}

Prepare class (static methods and variables inside)

  public class Prepare  {
    public static ChangeListener<Boolean> focusListener = new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean t1) {
            if (!t1) ;
            // get the stage from observableValue and close it when focus is lost.
        }
    };
    public static ChangeListener<Boolean> getFocusListener()
    {
        return focusListener;
    }
}

fxml file

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

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

<AnchorPane fx:id="anchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="551.0" prefWidth="565.0" style="-fx-background-color: #fffe00;" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
   </children>
</AnchorPane>

now i want to get the caller stage from the ObservableValue inside the ChangeListener.
i tried to cast it to BooleanProperty and then use getBean()

 if (!t1) {
            BooleanProperty booleanProperty = (BooleanProperty) observableValue;
            Stage stage = (Stage) booleanProperty.getBean();
            stage.close();
        }

, but i got the following error:

    Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl cannot be cast to class javafx.beans.property.BooleanProperty (javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl and javafx.beans.property.BooleanProperty are in module javafx.base of loader 'app')
    at sample.Prepare$1.changed(Prepare.java:13)
    at sample.Prepare$1.changed(Prepare.java:9)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.base/javafx.beans.property.ReadOnlyBooleanPropertyBase.fireValueChangedEvent(ReadOnlyBooleanPropertyBase.java:78)
    at javafx.base/javafx.beans.property.ReadOnlyBooleanWrapper.fireValueChangedEvent(ReadOnlyBooleanWrapper.java:103)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:111)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145)
    at javafx.graphics/javafx.stage.Window.setFocused(Window.java:678)
    at javafx.graphics/javafx.stage.Window$1.setFocused(Window.java:150)
    at javafx.graphics/com.sun.javafx.stage.WindowHelper.setFocused(WindowHelper.java:112)
    at javafx.graphics/com.sun.javafx.stage.WindowPeerListener.changedFocused(WindowPeerListener.java:64)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:126)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$4(GlassWindowEventHandler.java:178)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:176)
    at javafx.graphics/com.sun.glass.ui.Window.handleWindowEvent(Window.java:1336)
    at javafx.graphics/com.sun.glass.ui.Window.notifyFocus(Window.java:1315)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:833)
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl cannot be cast to class javafx.beans.property.BooleanProperty (javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl and javafx.beans.property.BooleanProperty are in module javafx.base of loader 'app')
    at sample.Prepare$1.changed(Prepare.java:13)
    at sample.Prepare$1.changed(Prepare.java:9)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.base/javafx.beans.property.ReadOnlyBooleanPropertyBase.fireValueChangedEvent(ReadOnlyBooleanPropertyBase.java:78)
    at javafx.base/javafx.beans.property.ReadOnlyBooleanWrapper.fireValueChangedEvent(ReadOnlyBooleanWrapper.java:103)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:111)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145)
    at javafx.graphics/javafx.stage.Window.setFocused(Window.java:678)
    at javafx.graphics/javafx.stage.Window$1.setFocused(Window.java:150)
    at javafx.graphics/com.sun.javafx.stage.WindowHelper.setFocused(WindowHelper.java:112)
    at javafx.graphics/com.sun.javafx.stage.WindowPeerListener.changedFocused(WindowPeerListener.java:64)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:126)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$4(GlassWindowEventHandler.java:178)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:176)
    at javafx.graphics/com.sun.glass.ui.Window.handleWindowEvent(Window.java:1336)
    at javafx.graphics/com.sun.glass.ui.Window.notifyFocus(Window.java:1315)
    at javafx.graphics/com.sun.glass.ui.win.WinWindow._close(Native Method)
    at javafx.graphics/com.sun.glass.ui.Window.close(Window.java:352)
    at javafx.graphics/com.sun.glass.ui.win.WinWindow.close(WinWindow.java:316)
    at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.lambda$close$4(WindowStage.java:824)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithRenderLock(QuantumToolkit.java:442)
    at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.close(WindowStage.java:817)
    at javafx.graphics/javafx.stage.Window$12.invalidated(Window.java:1157)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145)
    at javafx.graphics/javafx.stage.Window.setShowing(Window.java:1190)
    at javafx.graphics/javafx.stage.Window.hide(Window.java:1215)
    at javafx.graphics/com.sun.javafx.stage.WindowCloseRequestHandler.dispatchBubblingEvent(WindowCloseRequestHandler.java:45)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/com.sun.javafx.stage.WindowPeerListener.closing(WindowPeerListener.java:93)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:147)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$4(GlassWindowEventHandler.java:178)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:176)
    at javafx.graphics/com.sun.glass.ui.Window.handleWindowEvent(Window.java:1336)
    at javafx.graphics/com.sun.glass.ui.Window.notifyClose(Window.java:1241)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:833)

so what's the problem, and what is the best way to get the stage.

Big Thanks in advance for StackOverflow team.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

反差帅 2025-02-18 17:10:11

错误告诉您有什么问题:

  java.lang.classcastException:javafx.beans.beans.property.readonlybooleanwrapper $ readonlypropertyimpl不能被施加到类中。
 

您正在尝试将readonlybooleanproperty实例施加到booleanproperty,这显然是不可能的。但是您实际上不需要处理这种“狭窄”的实现。您所关心的只是获得“ bean”,只需要类型为a readonlyproperty&lt;?&gt; (哪个是属性类层次结构的顶部)。

ChangeListener<Boolean> listener = (obs, wasFocused, isFocused) -> {
  if (!isFocused) {
    var property = (ReadOnlyProperty<?>) obs;
    var stage = (Stage) property.getBean();
    stage.close();
  }
};

但是,您根本不需要处理铸造。如果您的目标是简单地避免代码重复,则只需创建一个接受阶段的实用程序方法,然后将侦听器添加到其fosited属性中即可。

例如:

import javafx.beans.value.ChangeListener;
import javafx.stage.Stage;

public final class CloseOnFocusLost {

  private static final Object KEY = new Object();

  public static void installListener(Stage stage) {
    @SuppressWarnings("unchecked")
    var listener = (ChangeListener<Boolean>) stage.getProperties().get(KEY);
    if (listener == null) {
      listener = (obs, wasFocused, isFocused) -> {
        if (!isFocused) {
          stage.close();
        }
      };
      stage.getProperties().put(KEY, listener);
      stage.focusedProperty().addListener(listener):
    }
  }

  public static void uninstallListener(Stage stage) {
    @SuppressWarnings("unchecked")
    var listener = (ChangeListener<Boolean>) stage.getProperties().remove(KEY);
    if (listener != null) {
      stage.focusedProperty().removeListener(listener):
    }
  }

  // prevent instantiation of utility class
  private CloseOnFocusLost() {}
}

上面的代码具有一些优点:

  • 它更加类型安全。
    • 您的代码很容易受到 second classcastException,因为可以将侦听器添加到不属于stage> stage的属性中。<<<<<<<<<<<<<<<< /li>
  • 您无需处理铸造,因为您可以直接访问阶段实例。
  • 它具有保障措施,以防止将多个侦听器添加到相同的copited属性。

如果您不在乎最后一点,并且永远不需要删除听众,那么您可以将代码简化为以下内容:

import javafx.stage.Stage;

public final class CloseOnFocusLost {

  public static void installListener(Stage stage) {
    stage.focusedProperty().addListener((obs, wasFocused, isFocused) -> {
      if (!isFocused) {
        stage.close();
      }
    });
  }

  // prevent instantiation of utility class
  private CloseOnFocusLost() {}
}

无论哪种情况,您都会添加带有封闭式的失调的听众:

CloseOnFocusLost.installListener(theStageInstance);

The error tells you what's wrong:

java.lang.ClassCastException: class javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl cannot be cast to class javafx.beans.property.BooleanProperty (javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl and javafx.beans.property.BooleanProperty are in module javafx.base of loader 'app')

You're trying to cast a ReadOnlyBooleanProperty instance to a BooleanProperty, which obviously is not possible. But you don't actually need to deal with such "narrow" implementations. All you care about is getting the "bean", and that only requires the type to be a ReadOnlyProperty<?> (which is the top of the property class hierarchy).

ChangeListener<Boolean> listener = (obs, wasFocused, isFocused) -> {
  if (!isFocused) {
    var property = (ReadOnlyProperty<?>) obs;
    var stage = (Stage) property.getBean();
    stage.close();
  }
};

However, you shouldn't need to deal with casting at all. If your goal is to simply avoid code duplication, then just create a utility method that accepts a Stage and adds a listener to its focused property.

For example:

import javafx.beans.value.ChangeListener;
import javafx.stage.Stage;

public final class CloseOnFocusLost {

  private static final Object KEY = new Object();

  public static void installListener(Stage stage) {
    @SuppressWarnings("unchecked")
    var listener = (ChangeListener<Boolean>) stage.getProperties().get(KEY);
    if (listener == null) {
      listener = (obs, wasFocused, isFocused) -> {
        if (!isFocused) {
          stage.close();
        }
      };
      stage.getProperties().put(KEY, listener);
      stage.focusedProperty().addListener(listener):
    }
  }

  public static void uninstallListener(Stage stage) {
    @SuppressWarnings("unchecked")
    var listener = (ChangeListener<Boolean>) stage.getProperties().remove(KEY);
    if (listener != null) {
      stage.focusedProperty().removeListener(listener):
    }
  }

  // prevent instantiation of utility class
  private CloseOnFocusLost() {}
}

The above has a few advantages over your code:

  • It is more type-safe.
    • Your code is vulnerable to a second ClassCastException because the listener could be added to a property which does not belong to a Stage.
  • You don't have to deal with casting because you have direct access to the Stage instance.
  • It has safeguards to prevent adding more than one listener to the same focused property.

If you don't care about the last point, and don't ever need to remove the listener, then you can simplify the code to the following:

import javafx.stage.Stage;

public final class CloseOnFocusLost {

  public static void installListener(Stage stage) {
    stage.focusedProperty().addListener((obs, wasFocused, isFocused) -> {
      if (!isFocused) {
        stage.close();
      }
    });
  }

  // prevent instantiation of utility class
  private CloseOnFocusLost() {}
}

In either case, you'd add your close-on-focus-lost listener with:

CloseOnFocusLost.installListener(theStageInstance);
两相知 2025-02-18 17:10:11

一个简单的可执行应用程序,该应用程序从

在此示例中,只有最后一个选项可以通过编程表明确保侦听器不会多次添加到同一阶段。

import javafx.application.Application;
import javafx.beans.property.ReadOnlyProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class StageClosureApp extends Application {
    private static final ChangeListener<Boolean> CLOSE_ON_FOCUS_LOSS_USING_BEAN_LISTENER =
            (o, wasFocused, isFocused) -> {
                if (!isFocused) ((Stage) ((ReadOnlyProperty<?>) o).getBean()).close();
            };

    private static ChangeListener<Boolean> closeOnFocusLossListener(Stage stage) {
        return (o, wasFocused, isFocused) -> {
            if (!isFocused) stage.close();
        };
    }

    private static void closeOnFocusLoss(Stage stage) {
        stage.focusedProperty().removeListener(CLOSE_ON_FOCUS_LOSS_USING_BEAN_LISTENER);
        stage.focusedProperty().addListener(CLOSE_ON_FOCUS_LOSS_USING_BEAN_LISTENER);
    }

    @Override
    public void start(Stage stage) {
        // all options perform the same function (close the stage on losing focus).
        // when testing, only enable one option, commenting the others out.

        // option 1: use a closure to reference the stage in a lambda function defined inline.
        stage.focusedProperty().addListener((o, wasFocused, isFocused) -> {
            if (!isFocused) stage.close();
        });

        // option 2: pass the stage instance to a static listener generation function.
        stage.focusedProperty().addListener(closeOnFocusLossListener(stage));

        // option 3: get the stage from the bean in an inline lambda function.
        stage.focusedProperty().addListener((o, wasFocused, isFocused) -> {
            if (!isFocused) ((Stage) ((ReadOnlyProperty<?>) o).getBean()).close();
        });

        // option 4: use a single instance static change listener that get the stage from the bean.
        stage.focusedProperty().addListener(CLOSE_ON_FOCUS_LOSS_USING_BEAN_LISTENER);

        // option 5: update stage to add a focus loss listener when it is not focused (can safely be called multiple time for a stage)
        closeOnFocusLoss(stage);
            
        stage.setScene(new Scene(new Label("hello, world")));
        stage.show();
    }
    
    public static void main(String[] args) {
        launch();
    }
}

如果阶段失去焦点是应用程序中显示的唯一阶段(如在这些测试用例中),则默认情况下,该应用程序将自动关闭,根据应用程序生命周期,一旦关注阶段就丢失了。

我不确定这个示例会增加Slaw的答案,但这可能很有用,因此我将暂时保留该帖子。

A simple executable application that demonstrates some of the techniques from Slaw's answer, plus one or two more. I won't add any additional explanation on these techniques as that is already covered well in Slaw's answer.

Only the last option in this example demonstrates programmatically ensuring that the listener is not added to the same stage more than once.

import javafx.application.Application;
import javafx.beans.property.ReadOnlyProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class StageClosureApp extends Application {
    private static final ChangeListener<Boolean> CLOSE_ON_FOCUS_LOSS_USING_BEAN_LISTENER =
            (o, wasFocused, isFocused) -> {
                if (!isFocused) ((Stage) ((ReadOnlyProperty<?>) o).getBean()).close();
            };

    private static ChangeListener<Boolean> closeOnFocusLossListener(Stage stage) {
        return (o, wasFocused, isFocused) -> {
            if (!isFocused) stage.close();
        };
    }

    private static void closeOnFocusLoss(Stage stage) {
        stage.focusedProperty().removeListener(CLOSE_ON_FOCUS_LOSS_USING_BEAN_LISTENER);
        stage.focusedProperty().addListener(CLOSE_ON_FOCUS_LOSS_USING_BEAN_LISTENER);
    }

    @Override
    public void start(Stage stage) {
        // all options perform the same function (close the stage on losing focus).
        // when testing, only enable one option, commenting the others out.

        // option 1: use a closure to reference the stage in a lambda function defined inline.
        stage.focusedProperty().addListener((o, wasFocused, isFocused) -> {
            if (!isFocused) stage.close();
        });

        // option 2: pass the stage instance to a static listener generation function.
        stage.focusedProperty().addListener(closeOnFocusLossListener(stage));

        // option 3: get the stage from the bean in an inline lambda function.
        stage.focusedProperty().addListener((o, wasFocused, isFocused) -> {
            if (!isFocused) ((Stage) ((ReadOnlyProperty<?>) o).getBean()).close();
        });

        // option 4: use a single instance static change listener that get the stage from the bean.
        stage.focusedProperty().addListener(CLOSE_ON_FOCUS_LOSS_USING_BEAN_LISTENER);

        // option 5: update stage to add a focus loss listener when it is not focused (can safely be called multiple time for a stage)
        closeOnFocusLoss(stage);
            
        stage.setScene(new Scene(new Label("hello, world")));
        stage.show();
    }
    
    public static void main(String[] args) {
        launch();
    }
}

If the stage losing focus is the only stage displayed in the application (as it is in these test cases), then, by default, the application will automatically shut down, according to the application lifecycle, as soon as focus on the stage is lost.

I'm not sure that this example adds much to Slaw's answer, but it might be useful, so I'll keep the post for now.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文