我有一段很像这样的代码:

package blah;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextInputControl;
import javafx.stage.Stage;

public class SimpleExample {

TextInputControl textFieldForWork;
LocalTextChangeListener localTextChangeListener;


public SimpleExample(TextInputControl textFieldForWork, Stage s) {
    this.textFieldForWork = textFieldForWork;
    localTextChangeListener = new LocalTextChangeListener();

    System.out.println("Creating new focus listener for TextField component");
    LocalFocusListener localFocusListener = new LocalFocusListener();

    s.focusedProperty().addListener(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
            if (observable.getValue().toString().equals("false")) {
                System.out.println("Removing TextField focus listener");
                textFieldForWork.focusedProperty().removeListener(localFocusListener);
            } else {
                System.out.println("Adding TextField focus listener");
                textFieldForWork.focusedProperty().addListener(localFocusListener);
            }
        }
    });
}

private class LocalFocusListener implements ChangeListener {
    @Override
    public void changed(ObservableValue observable, Object oldValue, Object newValue) {
        if (observable.getValue().toString().equals("true")) {
            System.out.println("Adding text change listener");
            textFieldForWork.textProperty().addListener(localTextChangeListener);
        } else {
            System.out.println("Removing text change listener");
            textFieldForWork.textProperty().removeListener(localTextChangeListener);
        }
    }
}

private class LocalTextChangeListener implements ChangeListener {
    @Override
    public void changed(ObservableValue observable, Object oldValue, Object newValue) {
        System.out.println("Textfield changed - do processing");
    }
}}


这段代码的目的是每次用户在文本字段中键入内容时触发一个侦听器。该代码的另一个必要功能是,在对话框散焦后,应删除文本字段侦听器。

任何帮助表示赞赏!

最佳答案

我不确定我是否真的了解您为什么需要观察舞台的重点属性。您不能只在文本字段中注册一次监听器,然后将其保留在那里吗?除非文本更改,否则不会调用它。

如果您确实需要描述的功能,则可以做到。以下是发生的情况的描述:

文本字段的focusedProperty跟踪当前场景图中焦点位于Node的文本字段。它是“场景图局部”,这意味着它与窗口是活动窗口还是聚焦窗口无关。

窗口的focusedProperty跟踪窗口是否具有焦点。如果将应用程序移至后台等,则此更改。

显然,在您创建文本字段时,尚未将其添加到场景或窗口中,因此只需执行

textFieldForWork.getScene().getWindow().focusedProperty().addListener(...)

将不起作用,因为getScene()此时将返回null。即使场景不是非空的,它也可能还不属于窗口,因此您可能有getScene()非空但在某些时候getScene().getWindow()为空。

因此,您实际要做的是观察属性序列。从textFieldForWork.sceneProperty()开始并观察它;如果它改变并且为非空,则观察textFieldForInput.getScene().windowProperty();当更改并且为非null时,请遵守textFieldForInput.getScene().getWindow().focusedProperty()

您可以自己处理,为链中的每个步骤创建侦听器,并根据需要添加和删除它们,但是EasyBind框架的API可以精确管理此用例。使用EasyBind,您可以做

    MonadicObservableValue<Boolean> stageFocused =
            EasyBind.monadic(textFieldForWork.sceneProperty())
                .flatMap(Scene::windowProperty)
                .flatMap(Window::focusedProperty)
                .orElse(false);
    stageFocused.addListener((obs, wasFocused, isNowFocused) -> {
        if (isNowFocused) {
            // stage now has focus...
        } else {
            // stage has lost focus...
        }
    });


如果要检查文本字段具有焦点并且包含它的窗口具有焦点的条件,则可以执行

BooleanBinding stageAndTextFieldFocused = Bindings.createBooleanBinding(() ->
    stageFocused.get() && tf.isFocused(),
    stageFocused, tf.focusedProperty());


stageFocused相同。然后就做

stageAndTextFieldFocused.addListener((obs, wasFocused, isNowFocused) ->
    { /* etc ... */ });

10-05 19:40