Получение события дефокусировки, отличного от Stage - JavaFX

137
14

У меня есть код очень похожий:

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

Цель этого кода - запустить прослушиватель каждый раз, когда пользователь вводит что-то в текстовое поле. Другая необходимая функция этого кода заключается в том, что при дефокусировке диалога слушатели текстового поля должны быть удалены.

Любая помощь ценится!

спросил(а) 2021-01-19T18:29:41+03:00 9 месяцев, 1 неделя назад
1
Решение
65

Я не уверен, что я действительно понимаю, почему вам нужно наблюдать сосредоточенное свойство сцены. Вы не можете просто зарегистрировать слушателя с текстовым полем один раз и оставить его там? Он не будет вызываться, если текст не изменится.

Если вам действительно нужна функциональность, которую вы описываете, вы можете это сделать. Вот описание того, что происходит:

focusedProperty текстового поля отслеживает, является ли Node с фокусом в текущем графике сцены текстовым полем. Это "локально для графа сцены", что означает, что он не зависит от того, является ли окно активным или сфокусированным окном.

focusedProperty окна отслеживает, имеет ли фокус фокус. Это изменяется, если вы перемещаете приложение на задний план и т.д.

Очевидно, что в момент, когда вы создаете текстовое поле, оно не добавлено в сцену или окно, поэтому просто выполните

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

не будет работать, потому что getScene() вернет null в этот момент. Даже если сцена не равна null, она может еще не принадлежать окну, поэтому у вас может быть getScene() не null, а getScene().getWindow() null в какой-то момент.

Итак, что вы на самом деле хотите сделать, это наблюдать за последовательностью свойств. Начните с textFieldForWork.sceneProperty() и соблюдайте его; если он изменяется и не имеет значения null, то наблюдайте 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 ... */ });

ответил(а) 2021-01-19T18:29:41+03:00 9 месяцев, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема