【发布时间】:2010-06-01 16:44:43
【问题描述】:
嘿,我很难理解自定义 GWT 事件处理程序的工作方式。我已经阅读了很多有关该主题的内容,但仍然有些模糊。我在 Stackoverflow 上读过类似GWT Custom Event Handler 的线程。有人可以用如下的应用方式来解释它。
我有 2 个班级,一个街区和一个人班。当人与块碰撞时,人会触发一个事件( onCollision() ),然后块类会监听该事件。
谢谢
【问题讨论】:
嘿,我很难理解自定义 GWT 事件处理程序的工作方式。我已经阅读了很多有关该主题的内容,但仍然有些模糊。我在 Stackoverflow 上读过类似GWT Custom Event Handler 的线程。有人可以用如下的应用方式来解释它。
我有 2 个班级,一个街区和一个人班。当人与块碰撞时,人会触发一个事件( onCollision() ),然后块类会监听该事件。
谢谢
【问题讨论】:
总是发送事件以通知某些事情(例如状态更改)。让我们以一个人和一堵墙为例。在这里我们可以想象有一个游戏,用户可以像人一样在迷宫中行走。每次用户撞到墙时,它都应该被告知碰撞,以便它可以对它做出反应(例如,墙可以将自己渲染为被破坏的墙)。这可以通过在每次检测到与墙壁的碰撞时发送碰撞事件来实现。这个事件是由一个人发送的,系统中对事件感兴趣的每个对象都会接收到它,并可以对它做出相应的反应。想要接收事件的对象必须将自己注册为对事件感兴趣。
这就是事件在每个系统或框架中的一般工作方式(不仅在 GWT 中)。为了在此类系统中发送和接收事件,您必须定义:
那么你可以:
这里我将展示一个在 GWT 中使用自定义事件的示例。我将使用一个负责检查邮箱并通知用户是否有新邮件的系统示例。假设系统中至少有 2 个组件:
当收到新邮件并且消息显示器接收到这些事件时,消息检查器会发送事件。
有关新邮件的信息将作为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 方法获取它。
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
// ...
}
}
在示例中,唯一的事件发送者是负责检查邮件的组件 - 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 被事件接收者用来将自己注册为对接收事件感兴趣。再次使用处理程序管理器。
当一切都定义好后,事件接收者必须在事件发送者中注册自己。这通常在创建对象期间完成:
MessageChecker checker = new MessageChecker();
MessageDisplayer displayer = new MessageDisplayer();
checker.addMessageReceivedEventHandler(displayer);
现在checker 发送的所有事件都将由displayer 接收。
要发送事件,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);
}
}
我希望它很清楚并且会有所帮助:)
【讨论】:
由于这个问题和 Piotr GWT 的回答增加了对创建自定义事件的稍微不同的方式的支持。此事件实现是特定构建,可与包 com.google.web.bindery.event.shared 中的 GWT 的 EventBus 一起使用。关于如何为 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();
}
});
现在使用新构造的事件在 eventbus 调用 fireEvent 上触发事件:
eventBus.fireEvent(new MessageReceivedEvent("my message"));
另一个实现可以在 GWT 自己的EntityProxyChange 事件类中找到。该实现使用 EventBus 的替代选项。它使用通过addHandlerToSource 添加绑定到特定源的处理程序的能力,并且可以通过eventBus.fireEventFromSource 触发。
这里给出的事件实现也更适合使用 GWT 的Activities。
【讨论】:
我通过扩展 GWT 的 Composite 类创建了自己的小部件。我想在这个类中创建我自己的自定义事件。我希望 GWT 的 WindowBuilder 编辑器可以访问这些事件。
我从这个页面上的答案中学到了很多,但我不得不做出一些改变。
我想从 Hilbrand Bouwkamp 的答案开始,因为它比较新。但我遇到了几个问题。 1)那个答案提到了事件总线。偶数总线是主程序拥有的全局变量。目前尚不清楚小部件库如何访问它。 2)我不是从头开始的。我正在扩展 GWT 库代码。为了完成这项工作,我必须从 GwtEvent 类开始,而不是 Event 类。
Piotr 的回答基本上是正确的,但是很长。我的类(间接)扩展了 GWT 的 Widget 类。 Widget 负责许多细节,例如创建 HandlerManager 对象。 (我查看了源代码,这正是标准 Widget 的工作方式,而不是使用 EventBus。)
我只需要向我的小部件类添加两件事来添加自定义事件处理程序。这些显示在这里:
public class TrackBar extends Composite {
public HandlerRegistration addValueChangedHandler(TrackBarEvent.Handler handler)
{
return addHandler(handler, TrackBarEvent.TYPE);
}
private void fireValueChangedEvent(int value)
{
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);
}
}
【讨论】:
public class MyWidget extends Composite implements HasValueChangeHandlers<T>, HasValue<T> { ... }
如果您碰巧在 GWT 之上使用 GWTP 框架,请参阅 this Stack。
GWTP 是“一个完整的模型-视图-展示器框架,用于简化您的下一个 GWT 项目。”
【讨论】: