Это не вопрос пользовательского состояния, но это обычный случай слабых слушателей, которые в какой-то момент собирают мусор: ваш SetChangeListener
исчезнет через некоторое время.
Вы можете проверитьэто, не нажимая вообще в кнопке переключателя.Просто наведите несколько раз кнопку и нажмите на нее.Через некоторое время вы не получите никакого уведомления.
Это, например, последовательность изменений:
INFO: pseudo-changed: [focused]
INFO: pseudo-changed: [hover, focused]
INFO: pseudo-changed: [focused]
INFO: pseudo-changed: [hover, focused]
INFO: pseudo-changed: [focused]
INFO: pseudo-changed: [hover, focused]
INFO: pseudo-changed: [hover, pressed, focused]
INFO: pseudo-changed: [hover, pressed, focused, armed]
INFO: pseudo-changed: [hover, focused, armed]
INFO: pseudo-changed: [hover, focused]
INFO: pseudo-changed: [hover, pressed, focused]
INFO: pseudo-changed: [hover, pressed, focused, armed]
INFO: pseudo-changed: [hover, focused, armed]
INFO: pseudo-changed: [hover, focused]
INFO: pseudo-changed: [hover, pressed, focused]
INFO: pseudo-changed: [hover, pressed, focused, armed]
INFO: pseudo-changed: [hover, focused, armed]
INFO: pseudo-changed: [hover, focused]
INFO: pseudo-changed: [focused]
INFO: pseudo-changed: [hover, focused]
После последнего я больше не получаю уведомлений.
Чтобы избежать этого, все, что нам нужно сделать, - это сохранить надежную ссылку на наблюдаемый набор псевдоклассов:
private ObservableSet<PseudoClass> states;
private Parent createContent() {
sl = change -> LOG.info("pseudo-changed: " + change.getSet());
specialButton = new SpecialButton("custom buttom");
states = specialButton.getPseudoClassStates();
states.addListener(sl);
...
}
Теперь он будет работать с любым изменением состояния, как с кнопки, так и с переключателя.кнопка.
INFO: pseudo-changed: [hover, focused]
INFO: pseudo-changed: [focused]
INFO: pseudo-changed: []
INFO: pseudo-changed: [special]
INFO: pseudo-changed: [hover, special]
INFO: pseudo-changed: [hover, pressed, special]
INFO: pseudo-changed: [hover, pressed, focused, special]
...
INFO: pseudo-changed: [focused, special]
INFO: pseudo-changed: [special]
INFO: pseudo-changed: []
INFO: pseudo-changed: [hover]
...
РЕДАКТИРОВАТЬ
Несмотря на то, что finalize
устарел, он все еще может использоваться для проверки того, что слушатель gc'ed в первом сценарии:
specialButton.getPseudoClassStates().addListener(new SetChangeListener<PseudoClass>() {
@Override
public void onChanged(Change<? extends PseudoClass> change) {
LOG.info("pseudo-changed: " + change.getSet());
}
@Override
protected void finalize() throws Throwable {
super.finalize();
LOG.info("SetChangeListener finalized by gc");
}
});
Итак, в приведенном выше тесте вы получите:
...
INFO: pseudo-changed: [hover, focused]
INFO: pseudo-changed: [focused]
INFO: pseudo-changed: [hover, focused]
INFO: SetChangeListener finalized by gc
и больше никаких уведомлений после этого, как упоминалось ранее.
Однако, если выудерживайте сильную ссылку, даже если вы вызовете явно System.gc()
, слушатель не будет gc'ed.
Стоит также проверить реализацию FXCollection::UnmodifiableObservableSet
, где WeakSetChangeListener
равно используется , удерживая ссылку на самого слушателя:
private SetChangeListener<E> listener;
private void initListener() {
if (listener == null) {
listener = c -> {
callObservers(new SetAdapterChange<E>(UnmodifiableObservableSet.this, c));
};
this.backingSet.addListener(new WeakSetChangeListener<E>(listener));
}
}
Это означает, чтоСсылка private SetChangeListener<E> listener;
на самом деле не нужна.