与JavaFx中的FocusEvent.getOppositeComponent等效

时间:2019-05-16 16:05:08

标签: java javafx focus

在我的JavaFx应用程序中,我想在主框架获得焦点时调用一个方法。但是,我只想在焦点位于应用程序外部并返回时才做出反应(例如,在对话框关闭时不希望发生这种情况)。

当应用程序在Swing中时,我可以使用方法

FocusEvent.getOppositeComponent

(对应于失去焦点的元素),如果它为null,则我知道焦点先前不在我的应用程序之外。

我在JavaFX中找不到任何等效项。

我尝试通过在窗口上添加事件过滤器来查看窗口事件:

primaryStage.addEventFilter(Event.ANY, e -> System.out.println("event " + e));

但它不会跟踪焦点事件。

3 个答案:

答案 0 :(得分:5)

JavaFX中没有等效项。每个窗口的焦点更改都以boolean property的形式处理,因此您只能知道窗口是否获得焦点或失去焦点。如果您为应用程序中的所有窗口注册了一个侦听器,则可以确定其中一个窗口是否在另一个窗口获得焦点时失去焦点。

JavaFX中没有“ FocusEvent”,您可以在Event中找到所有事件类型。

您可以请求功能here

答案 1 :(得分:1)

JavaFX 层次结构基于:Stage-> Scene-> Nodes-> ...-> Nodes

enter image description here

如果您想收听Stage(窗口)的焦点,则可以将监听器添加到Stage重点关注的属性

Stage stage = ...
stage.focusedProperty()
        .addListener((observable, oldValue, newValue) -> {
                    if (!stage.isFocused()) { 
                       //action
                    }
                }
        );
  

这不能解决问题。您无法在此处告诉您关注哪个组件。 oldValuenewValue是布尔值,因此您的if很琐碎

您可以检查是否丢失了所有Stage个 重点(实施自定义ChangeListener):

class AllStageUnfocusedListener implements ChangeListener<Boolean>{
    //IdentitySet and Runnable use only as example
    private final Set<Stage> stageSet;
    private final Runnable runnable;

    public AllStageUnfocusedListener(Runnable runnable) {
        this.stageSet =  Collections.newSetFromMap(new IdentityHashMap<>());
        this.runnable =  runnable;
    }

    public ChangeListener<Boolean> add(Stage stage){
        stageSet.add(stage);
        return this;
    }

    @Override
    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
        if(isAllStageLostFocus()){
            runnable.run();
        }
    }

    private boolean isAllStageLostFocus() {
        for (Stage stage : stageSet) {
            if (stage.isFocused()) {
                return false;
            }
        }
        return true;
    }
} 

并将Listener添加到Focused Property

AllStageUnfocusedListener changeListener = new AllStageUnfocusedListener(() -> { /* action */ });
Stage stage = ...
stage.focusedProperty()
            .addListener(changeListener.add(stage))

答案 2 :(得分:1)

我终于找到了一种使用JavaFX中事件的顺序来处理问题的半满意方法,因此我将其发布为答案,以防它可以帮助其他人。

当关闭窗口w1并将焦点放在窗口w2上时,事件顺序如下:

  
      
  1. w1收到事件WINDOW_HIDING
  2.   
  3. w2 focusProperty更改为true
  4.   
  5. w1收到事件WINDOW_HIDDEN
  6.   

所以我写了以下代码,让我知道焦点是否来自内部窗口:

public class MainStage {
    private Stage primaryStage;
    private AtomicBoolean triggerEventOnFocusGain = new AtomicBoolean(true);

    ...

    primaryStage.focusedProperty.addListener((prop, oldVal, newVal) -> {
        if(newVal.booleanValue() && triggerEventOnFocusGain.get()) {
            doStuff();
        }
    });
}

public class SomeDialog {
    private MainStage mainStage;
    private Window dialogWindow;

    ...

    dialogWindow.addEventHandler(WindowEvent.WINDOW_HIDING, event ->
        mainStage.setTriggerEventOnFocusGain(false));
    dialogWindow.addEventHandler(WindowEvent.WINDOW_HIDDEN, event ->
        mainStage.setTriggerEventOnFocusGain(true));
}

唯一的问题是我必须对所有内部Windows /对话框执行此操作。

以我为例,我最终决定仅通过少数几个对话框就可以避免这样做,因为触发这些对话框会引发问题,而忽略其他对话框。

另一种方法当然是为执行上述代码的所有视图类引入一个通用的抽象父类。