获取 Stage 以外的散焦事件 - JavaFX
Obtaining a de-focus event other than from Stage - 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");
}
}}
此代码的目的是每次用户在文本字段中键入内容时触发侦听器。此代码的另一个必要功能是,在对话框散焦时,应删除文本字段侦听器。
感谢任何帮助!
我不确定我是否真的理解为什么你需要观察舞台的焦点属性。您不能只在文本字段中注册一次侦听器并将其留在那里吗?除非文本更改,否则不会调用它。
如果你真的需要你描述的功能,你可以做到。以下是对正在发生的事情的描述:
文本域的focusedProperty
跟踪当前场景图中具有焦点的Node
是否是文本域。它是 "local to the scene graph",这意味着它与 window 是活动的还是聚焦的 window.
无关
window 的 focusedProperty
跟踪 window 是否有焦点。如果您将应用程序移至后台等,这会发生变化。
显然,在您创建文本字段时,还没有添加到场景或 window,所以只需这样做
textFieldForWork.getScene().getWindow().focusedProperty().addListener(...)
不会起作用,因为此时 getScene()
会 return null
。即使场景是非空的,它可能还不属于 window,因此您可能有 getScene()
非空但在某些时候 getScene().getWindow()
为空。
所以你真正想做的是观察属性的顺序。从textFieldForWork.sceneProperty()
开始观察;如果它发生变化并且不为空,则观察 textFieldForInput.getScene().windowProperty()
;当它发生变化并且不为空时,观察 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...
}
});
如果你想检查文本字段有焦点的条件 和 包含它的 window 有焦点,你可以做
BooleanBinding stageAndTextFieldFocused = Bindings.createBooleanBinding(() ->
stageFocused.get() && tf.isFocused(),
stageFocused, tf.focusedProperty());
和stageFocused
同上。然后做
stageAndTextFieldFocused.addListener((obs, wasFocused, isNowFocused) ->
{ /* etc ... */ });
我有一段代码很像这样:
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
是否是文本域。它是 "local to the scene graph",这意味着它与 window 是活动的还是聚焦的 window.
window 的 focusedProperty
跟踪 window 是否有焦点。如果您将应用程序移至后台等,这会发生变化。
显然,在您创建文本字段时,还没有添加到场景或 window,所以只需这样做
textFieldForWork.getScene().getWindow().focusedProperty().addListener(...)
不会起作用,因为此时 getScene()
会 return null
。即使场景是非空的,它可能还不属于 window,因此您可能有 getScene()
非空但在某些时候 getScene().getWindow()
为空。
所以你真正想做的是观察属性的顺序。从textFieldForWork.sceneProperty()
开始观察;如果它发生变化并且不为空,则观察 textFieldForInput.getScene().windowProperty()
;当它发生变化并且不为空时,观察 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...
}
});
如果你想检查文本字段有焦点的条件 和 包含它的 window 有焦点,你可以做
BooleanBinding stageAndTextFieldFocused = Bindings.createBooleanBinding(() ->
stageFocused.get() && tf.isFocused(),
stageFocused, tf.focusedProperty());
和stageFocused
同上。然后做
stageAndTextFieldFocused.addListener((obs, wasFocused, isNowFocused) ->
{ /* etc ... */ });