GWT自定义事件


67

嘿,我无法确定自定义GWT事件处理程序的工作原理。我已经阅读了很多有关该主题的文章,但仍然有些模糊。我在这里读过Stackoverflow上的线程,例如一个GWT自定义事件处理程序。有人可以在下面的应用手册中解释它。

我有2个班级,每个班级和一个男人班。当该人与该块碰撞时,该人触发一个事件(onCollision()),然后该块类侦听该事件。

谢谢

Answers:


195

一般事件:

事件总是被发送以通知某些事情(例如状态改变)。让我们以一个男人和一堵墙为例。在这里,我们可以想象有一个游戏,用户可以像迷宫中的男人一样行走。每次用户撞墙时,都应告知碰撞,以便对其做出反应(例如,一堵墙可能使自己成为被破坏的墙)。这可以通过在每次检测到与墙壁的碰撞时发送碰撞事件来实现。该事件由一个人发送,并且系统中对该事件感兴趣的每个对象都可以接收该事件并可以做出相应的反应。想要接收事件的对象必须将自己注册为对事件感兴趣。

这就是事件通常在每个系统或框架中(不仅在GWT中)工作的方式。为了在此类系统中发送和接收事件,您必须定义:

  1. 发送什么(事件是什么样的)
  2. 谁接收事件(事件接收者)
  3. 谁发送事件(事件发送者)

那么你也能:

  1. 注册想要接收事件的事件接收者
  2. 发送事件

GWT中的事件:

在这里,我将展示一个在GWT中使用自定义事件的示例。我将以一个系统示例为例,该系统负责检查邮箱并通知用户是否有新邮件。假设系统中至少有2个组件:

  • 邮件检查器,负责检查邮箱和
  • 消息显示器,负责显示新邮件

收到新邮件时,邮件检查器发送事件,而邮件显示器接收这些事件。

步骤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类。此类包含两个必须实现的抽象方法:getAssociatedTypedispatch。但是,在每个事件类中,它们通常都以非常相似的方式实现。

该类存储有关已接收消息的信息(请参阅构造函数)。每个事件接收者都可以使用getMessage方法获取它。

步骤2:定义事件接收者

GWT中的每个事件类型都与一个表示该事件类型的接收者的接口相关联。在GWT中,接收者称为处理程序。在示例中,事件接收器接口MessageReceivedEvent将被命名为MessageReceivedEventHandler。源代码如下:

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

每个处理程序都必须扩展EventHandler接口。它还应定义一种方法,该方法将在事件发生时调用(它应至少包含一个参数-事件)。该方法在此命名为onMessageReceived。每个接收者都可以通过实现此方法来对事件做出反应。

该示例中唯一的事件接收者是MessageDisplayercomponent:

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字段。在GWTHandlerManager中,顾名思义,它管理事件处理程序(事件接收器)。就像一开始所说的,每个想要接收事件的事件接收者都必须将自己注册为感兴趣的对象。这就是处理程序管理器的用途。它们使注册事件处理程序成为可能,并且它们可以将特定事件发送到每个注册的事件处理程序。

HanlderManager创建a时,它将在其构造函数中使用一个参数。每个事件都有一个来源,此参数将用作此处理程序管理器发送的所有事件的来源。在示例中this,事件源是MessageChecker

该方法fireEventHasHandlers接口中定义,并负责发送事件。如您所见,它仅使用处理程序管理器发送(触发)和事件。

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

}

我希望这很清楚,并会有所帮助:)


7
顺便说一句,当前将HandlerManager类标记为已弃用,建议改用SimpleEventBus。
凯尔

4
是的,在GWT 2.1.1上,最好用EventBus替换HandlerManager
den bardadym 2011年

看来您的事件应该扩展com.google.web.bindery.event.shared.Event <H>而不是GwtEvent。来自GwtEvent javadoc:“无需应用程序的自定义事件类型来扩展GwtEvent。请改用Event。”
cellepo

哇,好答案。不过,我很好奇的是,如果在“发送者”(例如组件)停止运行(例如与屏幕分离)的同时事件监听器没有注销,这是否容易导致内存泄漏
geert3 '16

@cellepo我尝试使用事件而不是GwtEvent,但是所有handlerManager都希望得到一个GwtEvent。这里有没有错过的把戏?
WORMSS

35

自从这个问题和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);
    }
}

该事件的用法如下:

要使用事件总线为该事件注册处理程序,请在MessageReceivedEvent类上调用静态注册方法:

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

现在fireEvent使用新构造的事件在eventbus调用上触发事件:

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

可以在GWT自己的EntityProxyChange事件类中找到另一个实现。该实现使用EventBus的替代选项。它具有通过addHandlerToSource和添加可以绑定到特定源的处理程序的功能eventBus.fireEventFromSource

与GWT的Activity配合使用时,此处给出的事件实现也更适合。


3
感谢您更新的GWT 2.4代码。您可以显示在注册处理程序并触发事件时如何获取对eventBus的引用吗?
贾斯汀

1
@ Hilbrand-Bouwkamp也对有关活动的信息感兴趣
吉恩·米歇尔·加西亚

3

我通过扩展GWT的Composite类创建了自己的小部件。我想在此类中创建自己的自定义事件。我希望GWT的WindowBuilder编辑器可以访问这些事件。

我从本页的答案中学到了很多东西,但是我必须进行一些更改。

我想从Hilbrand Bouwkamp的答案开始,因为它是新的。但是我遇到了两个问题。1)该答案引用了事件总线。偶数总线是主程序拥有的全局变量。尚不清楚小部件库如何获得对此的访问。2)我不是从零开始。我当时正在扩展GWT库代码。为了使这项工作有效,我必须从GwtEvent类而不是Event类开始。

皮奥特(Piotr)的回答本质上是正确的,但时间很长。我的类(间接地)扩展了GWT的Widget类。小部件负责许多细节,例如创建HandlerManager对象。(我查看了源代码,这就是标准Widget的工作原理,而不是通过使用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);
    }
}

对于用户定义的Composite小部件,要添加报告类型T值更改的事件,我发现以下答案可以解决问题:stackoverflow.com/a/15477048/685715。该设计可以总结如下:public class MyWidget extends Composite implements HasValueChangeHandlers<T>, HasValue<T> { ... }
Matt Wallis 2014年

1

如果您碰巧在GWT之上使用GWTP框架,请参考此Stack

GWTP是“一个完整的模型视图呈现器框架,可简化您的下一个GWT项目。”

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.