获取除Stage-JavaFX之外的去焦点事件

时间:2015-01-09 17:02:59

标签: java javafx

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

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");
    }
}}

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

任何帮助表示赞赏!

1 个答案:

答案 0 :(得分:1)

我不确定我真的明白为什么你需要观察舞台的焦点属性。你不能只用文本字段注册一个监听器并留在那里吗?除非文本发生变化,否则不会调用它。

如果您真的需要您描述的功能,您可以这样做。以下是对正在发生的事情的描述:

文本字段的focusedProperty会跟踪当前场景图中具有焦点的Node是否为文本字段。它是场景图中的本地图#34;这意味着它与窗口是活动窗口还是聚焦窗口无关。

窗口的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 ... */ });