首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从ObservableValue<获得呼叫阶段?在焦点更改侦听器中扩展Boolean>

如何从ObservableValue<获得呼叫阶段?在焦点更改侦听器中扩展Boolean>
EN

Stack Overflow用户
提问于 2022-06-30 04:51:29
回答 2查看 78关注 0票数 -1

我希望有一个焦点侦听器作为一个静态变量来传递抛出静态方法,它的功能是在这个阶段上的焦点丢失时关闭一个阶段。

我有密码:

主班

代码语言:javascript
复制
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);
    }
}

控制器类

代码语言:javascript
复制
 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();
    }
}

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

代码语言:javascript
复制
  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文件

代码语言:javascript
复制
<?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>

现在,我想从ObservableValue获得ChangeListener中的调用方级。我尝试将其转换为BooleanProperty,然后使用getBean()

代码语言:javascript
复制
 if (!t1) {
            BooleanProperty booleanProperty = (BooleanProperty) observableValue;
            Stage stage = (Stage) booleanProperty.getBean();
            stage.close();
        }

,但我得到了以下错误:

代码语言:javascript
复制
    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团队。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-06-30 14:56:31

错误告诉您出了什么问题:

javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl类不能转换为类javafx.beans.property.BooleanProperty (javafx.beans.property.ReadOnlyBooleanWrapper$ReadOnlyPropertyImpl和javafx.beans.property.BooleanProperty在加载程序的模块javafx.base中)

您正在尝试将ReadOnlyBooleanProperty实例转换为BooleanProperty,这显然是不可能的。但是,您实际上并不需要处理这种“狭窄”的实现。您所关心的只是获取"bean",这只需要类型为ReadOnlyProperty (它是属性类层次结构的顶部)。

代码语言:javascript
复制
ChangeListener<Boolean> listener = (obs, wasFocused, isFocused) -> {
  if (!isFocused) {
    var property = (ReadOnlyProperty<?>) obs;
    var stage = (Stage) property.getBean();
    stage.close();
  }
};

然而,你根本不需要处理演员的问题。如果您的目标是简单地避免代码重复,那么只需创建一个接受Stage并向其focused属性添加侦听器的实用方法。

例如:

代码语言:javascript
复制
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() {}
}

与您的代码相比,上面有一些优点:

  • 更安全的类型。
    • 您的代码容易受到第二个ClassCastException的攻击,因为侦听器可以添加到不属于Stage的属性中。

  • 您不必处理强制转换,因为您可以直接访问Stage实例。
  • 它具有防止向同一个focused属性添加多个侦听器的安全措施。

如果您不关心最后一点,也不需要删除侦听器,那么可以将代码简化为以下内容:

代码语言:javascript
复制
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() {}
}

在任何一种情况下,您都可以将您的近焦点丢失侦听器添加到:

代码语言:javascript
复制
CloseOnFocusLost.installListener(theStageInstance);
票数 2
EN

Stack Overflow用户

发布于 2022-06-30 19:21:25

一个简单的可执行应用程序,它演示了来自斯劳的回答的一些技术,再加上一两个技术。我不会对这些技术添加任何额外的解释,因为这已经在Slaw的回答中得到了很好的介绍。

只有本示例中的最后一个选项以编程方式演示如何确保侦听器不会多次添加到同一阶段。

代码语言:javascript
复制
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的回答有多大帮助,但它可能是有用的,所以我暂时保留这个帖子。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72810442

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档