JavaFX - разница между EventDispatcher и EventFilter - PullRequest
0 голосов
/ 25 июня 2018

Я пытаюсь понять, когда использовать EventDispatcher в JavaFX.Я в значительной степени осведомлен обо всех механизмах событий захвата и пузыри.Но я все еще путаюсь с целью EventDispatcher.Потому что я могу выполнить большую часть своей работы, используя фильтры и обработчики.

Может кто-нибудь объяснить, какова реальная цель и как использовать этот EventDispatcher?

Спасибо.

1 Ответ

0 голосов
/ 25 июня 2018

Цель EventDispatcher

Как следует из названия, цель EventDispatcher - отправить Event.То, как это происходит, зависит от реализации.Однако стандартная реализация (внутренняя для JavaFX) делает EventDispatcher своего рода «коллекцией» EventHandler s.EventDispatcher обязан вызвать подходящего EventHandler в нужное время.То, что делает EventHandler «подходящим», зависит от:

  • Если EventHandler было зарегистрировано для текущей фазы цикла отправки событий (захвата или передачи)
  • ЕслиEventHandler зарегистрирован для текущего Event EventType или одного из супертипов (т. Е. EventType.getSuperType())

Путь события (или «Цепочка»)

Если мы сфокусируемся на графе сцены, то при срабатывании Event он начинается в верхней части графа сцены (Window) и проходит по иерархии к цели (обычно Node,но, по крайней мере, реализация EventTarget).Это фаза «захвата» цикла отправки событий.После достижения цели он перемещается обратно вверх по графу сцены, пока не достигнет Window снова.Это «пузырящаяся» фаза цикла.

Фаза захвата

  • Window -> Scene -> Root Node -> Middle Node -> EventTarget

Фаза пузыря

  • Window <- <code>Scene <- <code>Root Node <- <code>Middle Node <- <code>EventTarget

Если на каком-либо шаге используется Event (через Event.consume()), то он не будет переадресован на следующий шаг.Это фактически останавливает обработку Event на нужном этапе в цепочке.

Способ вычисления этого пути осуществляется реализацией EventDispatchChain и EventTarget.EventTarget должен реализовывать следующий метод:

EventDispatchChain buildEventDispatchChain(EventDispatchChain tail);

Когда срабатывает Event, он имеет обозначенное EventTarget.Поскольку EventTarget будет в нижней части графа сцены, цепочка построена снизу вверх.Это достигается путем добавления EventDispatcher к EventDispatchChain на каждом уровне иерархии (используя EventDispatchChain.prepend(EventDispatcher)).Именно здесь начинает приходить EventDispatcher.

Каждый EventTarget обычно имеет свой собственный EventDispatcher, связанный с ним.Реализации EventTarget в стандартном JavaFX (Window, Scene, Node, MenuItem и т. Д.) Предоставляют свои собственные реализации EventDispatcher.В связи с этим вам не нужно беспокоиться о том, как использовать EventDispatcher.Вы даже не используете это напрямую.Скорее, вы добавляете EventHandler s с помощью методов [add|remove]EventHandler и [add|remove]EventFilter, а также различные свойства onXXX.

Когда вызывается buildEventDispatchChain, скажем, Button, Button добавляет его EventDispatcher к данному EventDispatchChain.Затем он вызывает buildEventDispatchChain на своем Parent, если он есть.Это продолжается до корня Node из Scene.Корень Node вызывает buildEventDispatchChain на указанном Scene, который после добавления EventDispatcher делает то же самое на Window, к которому он присоединен.

На этом этапе EventDispatchChainполностью построен и готов к обработке Event.Если еще не очевидно, EventDispatchChain является просто "стеком" EventDispatcher с.Другими словами, это узкоспециализированный java.util.Deque, но фактически не расширяющий этот интерфейс.

Примечание: EventDispatchChain также предоставляет метод append(EventDispatcher) для ситуаций, в которых добавление является неправильной операцией .


Отправка события

Как только EventDispatchChain будет полностью построен, пришло время фактически отправить Event.Это можно сделать, вызвав этот метод для EventDispatchChain:

Event dispatchEvent(Event event);

. В нем EventDispatchChain get (pop) первый EventDispatcher в стеке и вызов * его метода.:

Event dispatchEvent(Event event, EventDispatchChain tail);

Side Note: К сожалению, метод EventDispatcher имеет аналогичную сигнатуру EventDispatchChain.dispatchEvent(Event), что может вызвать путаницу .

Side Note# 2: tail будет одинаковым EventDispatchChain на протяжении всего процесса .

Здесь действительно используются EventDispatcher s.Вот алгоритм, используемый каждым EventDispatcher как определено внутренним классом com.sun.javafx.event.BasicEventDispatcher (исходный код Java 10):

@Override
public Event dispatchEvent(Event event, final EventDispatchChain tail) {
    event = dispatchCapturingEvent(event);
    if (event.isConsumed()) {
        return null;
    }
    event = tail.dispatchEvent(event);
    if (event != null) {
        event = dispatchBubblingEvent(event);
        if (event.isConsumed()) {
            return null;
        }
    }

    return event;
}

Шаги:

  1. ОтправкаEvent для фазы захвата
    • Это вызывает все EventHandler с, которые были добавлены как фильтр
    • Использование Event здесь не остановит отправку Event in этот шаг ; но остановит обработку Event на последующих шагах
  2. Если Event используется, верните null, иначе перешлите Event на tail и дождаться его возвращения
  3. Если tail.dispatchEvent не возвращает null, отправьте Event для пузырчатая фаза
    • Если возвращенное Event было null, это означает, что Event было израсходовано где-то вниз по цепочке и больше не нужно делать обработку
    • Это вызывает все EventHandler s, которые были добавлены как обработчик (который включает те, которые добавлены через свойства onXXX)
    • Как и на шаге 1, использование Event здесь не останавливает обработку этот шаг ; но остановит обработку Event на последующих шагах
  4. Если Event потребляется, вернуть null, в противном случае вернуть Event
    • Как и в случае EventDispatchChain, возврат null означает, что Event был потребляется и обработка Event должна прекратиться

Это делается для каждого EventDispatcher в EventDispatchChain. Вызов tail.dispatchEvent является в основном "рекурсивной" операцией (без «реальная» рекурсия). По сути, этот код идет вниз по стеку (вызовы tail.dispatchEvent) и затем возвращается обратно в стек (когда tail.dispatchEvent возвращается). И каждое «звено в цепочке» выполняет обработку перед «рекурсивным» вызов (фаза захвата) и после «рекурсивного» вызова возвращается (пузырение фаза).

Заметьте здесь, однако, что на каждом этапе это EventDispatcher, который является на самом деле вызывая каждый из соответствующих EventHandler с. Вот как EventDispatcher используется .


Реализация своего собственного EventDispatcher

При расширении класса, который уже реализует EventTarget, вам следует только создайте свой собственный EventDispatcher, когда абсолютно необходим . Если ваша цель заключается в контролировать, если Event достигает определенного EventTarget, тогда ваш первый выбор должен потреблять Event в соответствующем месте (упомянутое Джай в Комментарии). Если вы хотите что-то изменить в пути Event, то вы может потребоваться предоставить свой EventDispatcher. Однако из-за закрытых характер внутренних EventDispatcher реализаций в сочетании с тем, что интерфейс EventDispatcher ограничен, вы, вероятно, будете ограничены оборачивать оригинальную EventDispatcher в вашу собственную реализацию и делегировать когда необходимо. Я видел это сделано в коде других людей (возможно, даже видел это в самой JavaFX) но я не могу вспомнить код достаточно хорошо, чтобы дать вам примеры этого.

Если вы создаете собственный EventTarget с нуля, тогда вам придется реализовать свой собственный EventDispatcher. Некоторые вещи, которые нужно иметь в виду, если вы делаете нужна ваша собственная реализация:

  • Он должен вызывать только EventHandler, зарегистрированные для текущей фазы (если должны быть фазы)
  • Он должен вызывать только EventHandler, зарегистрированные для Event * EventType и сказал EventType супертипы
  • Он должен направить Event к хвосту EventDispatchChain
  • Он должен возвращать null только если Event было израсходовано
  • Должно быть , способное к одновременной модификации тем же Thread
    • Причина этого в том, что EventHandler может удалить себя или добавить / удалить еще один EventHandler, пока выполняется метод handle. это будет происходить, пока EventDispatcher итерирует EventHandler s в некотором роде.
  • Он должен "исправить источник" Event. В стандартной реализации JavaFX каждый EventDispatcher обновляет источник Event до Object связанный с этим EventDispatcher (например, Node). Это сделано в подклассы вышеупомянутого com.sun.javafx.event.BasicEventDispatcher. Метод «исправить источник» - Event.copyFor(Object, EventTarget)
    • Примечание: я не уверен, является ли это на самом деле частью контракта и может быть необходимо, если ваша реализация не требует этого.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...