Reputation: 942
I have a piece of code much like this:
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");
}
}}
The purpose of this code is to fire a listener each time the user types something in the textfield. The other necessary function of this code, is that upon dialog defocus, the textfield listeners should be removed.
Any help appreciated!
Upvotes: 0
Views: 1660
Reputation: 209408
I'm not sure I really understand why you need to observe the focused property of the stage. Can't you just register the listener with the text field once and leave it there? It won't be invoked unless the text changes.
If you really need the functionality you describe, you can do it. Here's a description of what's going on:
The focusedProperty
of the text field tracks whether or not the Node
with focus within the current scene graph is the text field. It is "local to the scene graph", which means it is independent of whether or not the window is the active or focused window.
The focusedProperty
of the window tracks whether or not the window has focus. This changes if you move the application to the background, etc.
Obviously, at the point where you create the text field, is hasn't been added to a scene or window, so just doing
textFieldForWork.getScene().getWindow().focusedProperty().addListener(...)
won't work, because getScene()
will return null
at this point. Even if the scene is non-null, it might not yet belong to a window, so you may have getScene()
non-null but getScene().getWindow()
null at some point.
So what you actually want to do is observe the sequence of properties. Start with textFieldForWork.sceneProperty()
and observe it; if it changes and is non-null, then observe textFieldForInput.getScene().windowProperty()
; when that changes and is non-null, observe textFieldForInput.getScene().getWindow().focusedProperty()
.
You could handle this yourself, creating listeners for each step in the chain and adding and removing them as necessary, but the EasyBind framework has API that manages exactly this use case. Using EasyBind you can do
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...
}
});
If you want to check the condition that the text field has focus and the window containing it has focus, you can do
BooleanBinding stageAndTextFieldFocused = Bindings.createBooleanBinding(() ->
stageFocused.get() && tf.isFocused(),
stageFocused, tf.focusedProperty());
with stageFocused
as above. Then just do
stageAndTextFieldFocused.addListener((obs, wasFocused, isNowFocused) ->
{ /* etc ... */ });
Upvotes: 1