Пользовательские события GWT

Привет, у меня есть проблема с тем, как работают пользовательские обработчики событий GWT. Я прочитал довольно много о теме, и это все еще какое-то туманное. Я прочитал темы здесь, в Stackoverflow, как этот GWT Custom Event Handler. Может ли кто-нибудь объяснить это в прикладном маннаре, например, следующее.

У меня есть 2 класса блока и класс man. Когда человек сталкивается с блоком, человек запускает событие (onCollision()), а затем класс блока прослушивает это событие.

Спасибо

Ответ 1

События в целом:

События всегда отправляются для информирования о чем-то (например, об изменении состояния). Позвольте взять ваш пример с мужчиной и стеной. Здесь мы можем представить себе, что есть игра, в которой пользователь может ходить как человек в лабиринте. Каждый раз, когда пользователь попадает в стену, он должен быть проинформирован о столкновении, чтобы он мог реагировать на него (например, стена может выступать как разрушенная стена). Это может быть достигнуто путем отправки события столкновения каждый раз при обнаружении столкновения со стеной. Это событие отправляется человеком, и каждый объект в системе, заинтересованный в событии, получает его и может реагировать на него соответствующим образом. Объекты, которые хотят получать события, должны регистрироваться как заинтересованные в событии.

Это то, как события работают вообще в каждой системе или структуре (не только в GWT). Чтобы отправлять и получать события в таких системах, вы должны определить:

  • Что отправляется (как выглядят события)
  • Кто получает события (приемники событий)
  • Кто отправляет события (отправители событий)

Затем вы можете:

  1. Зарегистрировать приемники событий, которые хотят получать события.
  2. Отправить события

События в GWT:

Здесь я покажу пример использования пользовательских событий в GWT. Я буду использовать пример системы, которая отвечает за проверку почтового ящика и информирует пользователя о появлении новых писем. Предположим, что в системе имеется не менее двух компонент:

  • контролер сообщений, ответственный за проверку почтового ящика и
  • Дисплей сообщений, отвечающий за отображение новых писем

Контроллер сообщений отправляет события, когда полученная новая почта получена, и дисплей сообщений принимает эти события.

Шаг 1: Определение событий

Информация о новой почте будет отправлена ​​как экземпляр класса MessageReceivedEvent. Класс содержит новое письмо (для простоты пусть предположим, что это всего лишь String).

Полный исходный код этого класса представлен ниже (комментарий для него ниже исходного кода).

public class MessageReceivedEvent extends GwtEvent<MessageReceivedEventHandler> {

    public static Type<MessageReceivedEventHandler> TYPE = new Type<MessageReceivedEventHandler>();

    private final String message;

    public MessageReceivedEvent(String message) {
        this.message = message;
    }

    @Override
    public Type<MessageReceivedEventHandler> getAssociatedType() {
        return TYPE;
    }

    @Override
    protected void dispatch(MessageReceivedEventHandler handler) {
        handler.onMessageReceived(this);
    }

    public String getMessage() {
        return message;
    }
}

MessageReceivedEventHandler - это интерфейс, который представляет приемники событий. Не беспокойтесь об этом в данный момент, это будет обсуждаться позже.

Каждый класс, представляющий событие GWT, должен расширять класс GwtEvent. Этот класс содержит два абстрактных метода, которые должны быть реализованы: getAssociatedType и dispatch. Однако в каждом классе событий они обычно реализуются очень похожим образом.

Класс хранит информацию о полученном сообщении (см. конструктор). Каждый приемник событий может получить его с помощью метода getMessage.

Шаг 2: Определение приемников событий

Каждый тип события в GWT связан с интерфейсом, представляющим приемники этого типа события. В GWT приемники называются обработчиками. В примере интерфейс приемника событий для MessageReceivedEvent будет называться MessageReceivedEventHandler. Исходный код ниже:

public interface MessageReceivedEventHandler extends EventHandler {
    void onMessageReceived(MessageReceivedEvent event);
}

Каждый обработчик должен расширять интерфейс EventHandler. Он также должен определить метод, который будет вызываться при возникновении события (он должен принимать по крайней мере один параметр - событие). Здесь метод называется onMessageReceived. Каждый приемник может реагировать на событие, реализуя этот метод.

Единственным приемником событий в этом примере является компонент MessageDisplayer:

public class MessageDisplayer implements MessageReceivedEventHandler {

    @Override
    public void onMessageReceived(MessageReceivedEvent event) {
        String newMessage = event.getMessage();
        // display a new message
        // ...
    }

}

Шаг 3. Определение отправителей событий

В этом примере единственным отправителем события является компонент, ответственный за проверку почты - EventChecker:

public class MessageChecker implements HasHandlers {

    private HandlerManager handlerManager;

    public MessageChecker() {
        handlerManager = new HandlerManager(this);
    }

    @Override
    public void fireEvent(GwtEvent<?> event) {
        handlerManager.fireEvent(event);
    }

    public HandlerRegistration addMessageReceivedEventHandler(
            MessageReceivedEventHandler handler) {
        return handlerManager.addHandler(MessageReceivedEvent.TYPE, handler);
    }

}

Каждый отправитель события должен реализовать интерфейс HasHandlers.

Наиболее важным элементом здесь является поле HandlerManager. В GWT HandlerManager, поскольку имя предлагает управлять обработчиками событий (приемниками событий). Как было сказано в начале, каждый приемник событий, который хочет получать события, должен зарегистрироваться как заинтересованный. Для этого нужны менеджеры-обработчики. Они позволяют регистрировать обработчики событий, и они могут отправлять конкретное событие каждому зарегистрированному обработчику событий.

Когда создается HanlderManager, он принимает один аргумент в своем конструкторе. Каждое событие имеет источник происхождения, и этот параметр будет использоваться в качестве источника для всех событий, отправляемых этим менеджером обработчика. В примере это this, поскольку источником событий является MessageChecker.

Метод fireEvent определяется в интерфейсе HasHandlers и отвечает за отправку событий. Как вы видите, он просто использует диспетчер обработчика для отправки (пожара) и события.

addMessageReceivedEventHandler используется приемниками событий для регистрации в качестве заинтересованных в получении событий. Для этого используется диспетчер обработчиков.

Шаг 4: Принять приемники событий с отправителями событий

Когда все определено, приемники событий должны регистрироваться в отправителе событий. Обычно это делается при создании объектов:

MessageChecker checker = new MessageChecker();
MessageDisplayer displayer = new MessageDisplayer();
checker.addMessageReceivedEventHandler(displayer);

Теперь все события, отправленные checker, будут получены displayer.

Шаг 5: Отправить события

Чтобы отправить событие, MessageChecker должен создать экземпляр события и отправить его с помощью метода fireEvent. Это можно сделать в методе newMailReceived:

public class MessageChecker implements HasHandlers {

    // ... not important stuff omitted

    public void newMailReceived() {
        String mail = ""; // get a new mail from mailbox
        MessageReceivedEvent event = new MessageReceivedEvent(mail);
        fireEvent(event);
    }

}

Я надеюсь, что это понятно и поможет:)

Ответ 2

Поскольку этот вопрос и ответ от Piotr GWT добавили поддержку немного другого способа создания пользовательских событий. Реализация этого события представляет собой конкретную сборку, которая будет использоваться с GWT EventBus в пакете com.google.web.bindery.event.shared. Пример создания пользовательского события для GWT 2.4:

import com.google.web.bindery.event.shared.Event;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;

/**
 * Here is a custom event. For comparison this is also a MessageReceivedEvent.
 * This event extends the Event from the web.bindery package.
 */
public class MessageReceivedEvent extends Event<MessageReceivedEvent.Handler> {

    /**
     * Implemented by methods that handle MessageReceivedEvent events.
     */
    public interface Handler {
        /**
         * Called when an {@link MessageReceivedEvent} event is fired.
         * The name of this method is whatever you want it.
         *
         * @param event an {@link MessageReceivedEvent} instance
         */
        void onMessageReceived(MessageReceivedEvent event);
    }

    private static final Type<MessageReceivedEvent.Handler> TYPE =
        new Type<MessageReceivedEvent.Handler>();

    /**
     * Register a handler for MessageReceivedEvent events on the eventbus.
     * 
     * @param eventBus the {@link EventBus}
     * @param handler an {@link MessageReceivedEvent.Handler} instance
     * @return an {@link HandlerRegistration} instance
     */
    public static HandlerRegistration register(EventBus eventBus,
        MessageReceivedEvent.Handler handler) {
      return eventBus.addHandler(TYPE, handler);
    }    

    private final String message;

    public MessageReceivedEvent(String message) {
        this.message = message;
    }

    @Override
    public Type<MessageReceivedEvent.Handler> getAssociatedType() {
        return TYPE;
    }

    public String getMessage() {
        return message;
    }

    @Override
    protected void dispatch(Handler handler) {
        handler.onMessageReceived(this);
    }
}

Событие используется следующим образом:

Чтобы зарегистрировать обработчик для этого события с вызовом eventbus, вызовите метод статического регистра в классе MessageReceivedEvent:

MessageReceivedEvent.register(eventbus, new MessageReceivedEvent.Handler() {
   public void onMessageReceived(MessageReceivedEvent event) {
     //...do something usefull with the message: event.getMessage();
   }
});

Теперь, чтобы запустить событие в вызове eventbus fireEvent с помощью нового события:

eventBus.fireEvent(new MessageReceivedEvent("my message"));

Другая реализация может быть найдена в собственном классе событий EntityProxyChange GWT. Эта реализация использует альтернативный вариант EventBus. Он использует возможность добавления обработчиков, привязанных к определенному источнику, через addHandlerToSource и может быть запущен через eventBus.fireEventFromSource.

Реализация события, приведенная здесь, также более подходит при работе с GWT Activities.

Ответ 3

Я создал свой собственный виджет, расширив класс GWT Composite. Я хотел создать собственное собственное событие в этом классе. Я хотел, чтобы события были доступны для редактора GWT WindowBuilder.

Я узнал много из ответов на этой странице, но мне пришлось внести некоторые изменения.

Я хотел начать с ответа Хилбринда Бувокпама, потому что он был новее. Но у меня возникло несколько проблем. 1) Этот ответ сделал ссылку на автобус событий. Четный автобус - это глобальная переменная, принадлежащая основной программе. Неясно, как библиотека виджета может получить доступ к этому. 2) Я не начинал с нуля. Я расширил библиотечный код GWT. Чтобы выполнить эту работу, мне пришлось начинать с класса GwtEvent, а не из класса Event.

Ответ Петра, по сути, правильный, но он был очень длинным. Мой класс (косвенно) расширяет класс GWT Widget. Виджет заботится о многих деталях, таких как создание объекта HandlerManager. (Я просмотрел исходный код и точно, как работают стандартные виджеты, а не с помощью EventBus.)

Мне нужно было добавить две вещи к моему классу виджетов, чтобы добавить настраиваемый обработчик событий. Они показаны здесь:

public class TrackBar extends Composite {

    public HandlerRegistration addValueChangedHandler(TrackBarEvent.Handler handler)
    {
        return addHandler(handler, TrackBarEvent.TYPE); 
    }   

    private void fireValueChangedEvent()
    {
        final TrackBarEvent e = new TrackBarEvent(value);
        fireEvent(e);
    }

Мое новое событие почти точно совпадает с классом событий Piotr, показанным выше. Стоит отметить одно. Я начал с getValue(), основываясь на этом примере. Позже я добавил getTrackBar(), чтобы получить гораздо больше информации. Если бы я начинал с нуля, я бы сосредоточился на последнем, а не на первом. Полный класс событий показан ниже.

import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;

public class TrackBarEvent extends GwtEvent< TrackBarEvent.Handler >
{
    public interface Handler extends EventHandler {
        void onTrackBarValueChanged(TrackBarEvent event);
    }

    static final Type<TrackBarEvent.Handler> TYPE =
            new Type<TrackBarEvent.Handler>();

    private final int value;

    public TrackBarEvent(int value) {
        this.value = value;
    }

    @Override
    public Type<TrackBarEvent.Handler> getAssociatedType() {
        return TYPE;
    }

    public int getValue() {
        return value;
    }

    public TrackBar getTrackBar()
    {
        return (TrackBar)getSource();
    }

    @Override
    protected void dispatch(Handler handler) {
        handler.onTrackBarValueChanged(this);
    }
}

Ответ 4

Если вы используете GWTP framework поверх GWT, обратитесь к этот стек.

GWTP - это "Полная модель-представление-презентатор для упрощения вашего следующего проекта GWT".