web-dev-qa-db-ja.com

GWTカスタムイベント

ちょっと私はカスタムGWTイベントハンドラーがどのように機能するかについて頭を悩ましています。私はこのトピックについてかなり読んだことがありますが、それでもいささかぼやけています。このようなStackoverflowのスレッドをここで読みました GWT Custom Event Handler 。誰かが次のような応用マナーでそれを説明できますか。

ブロックと男性クラスの2つのクラスがあります。男がブロックと衝突すると、男はイベント(onCollision())を発生させ、ブロッククラスはそのイベントをリッスンします。

ありがとう

67
Ciarán

一般的なイベント:

イベントは常に何かについて通知するために送信されます(例:状態の変化)。男と壁の例を見てみましょう。ここでは、ユーザーが迷宮の中で男として歩くことができるゲームがあると想像できます。ユーザーが壁にぶつかるたびに、衝突に反応できるように衝突について通知する必要があります(たとえば、壁はそれ自体を破壊された壁としてレンダリングできます)。これは、壁との衝突が検出されるたびに衝突イベントを送信することで実現できます。このイベントは男性によって送信され、イベントに関心のあるシステム内のすべてのオブジェクトがそれを受信し、それに応じて対応できます。イベントを受信したいオブジェクトは、イベントに関心があるとして自身を登録する必要があります。

これは、すべてのシステムまたはフレームワークで(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の2つの抽象メソッドを実装する必要があります。ただし、すべてのイベントクラスでは、通常、非常によく似た方法で実装されます。

このクラスには、受信したメッセージに関する情報が格納されます(コンストラクターを参照)。すべてのイベントレシーバーは、getMessageメソッドを使用して取得できます。

ステップ2:イベントレシーバーを定義する

GWTの各イベントタイプは、このイベントタイプのレシーバーを表すインターフェイスに関連付けられています。 GWTでは、レシーバーはハンドラーと呼ばれます。この例では、MessageReceivedEventのイベントレシーバーインターフェイスの名前はMessageReceivedEventHandlerになります。ソースコードは次のとおりです。

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

各ハンドラーは、EventHandlerインターフェイスを拡張する必要があります。また、イベントが発生したときに呼び出されるメソッドを定義する必要があります(少なくとも1つのパラメーター(イベント)を取る必要があります)。ここでは、メソッドの名前は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が作成されると、コンストラクターで1つの引数を取ります。すべてのイベントにはOriginのソースがあり、このパラメーターはこのハンドラーマネージャーによって送信されるすべてのイベントのソースとして使用されます。例では、イベントソースが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);
    }

}

私はそれが明確で助けになることを願っています:)

195
Piotr

この質問と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();
   }
});

次に、イベントバスでイベントを起動するには、新しく構築されたイベントでfireEventを呼び出します。

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

別の実装は、GWT独自のEntityProxyChangeイベントクラスにあります。その実装は、EventBusの代替オプションを使用します。 addHandlerToSourceを介して特定のソースにバインドされ、eventBus.fireEventFromSourceを介してトリガーできるハンドラーを追加する機能を使用します。

GWTの Activities を使用する場合は、ここで指定したイベント実装もより適しています。

35

GWTのCompositeクラスを拡張して、独自のウィジェットを作成しました。このクラスで独自のカスタムイベントを作成したかった。 GWTのWindowBuilderエディターがイベントにアクセスできるようにしたかったのです。

このページの回答から多くのことを学びましたが、いくつかの変更を加える必要がありました。

ヒルブランド・ブーカンプの答えから始めたかったのは、それが新しいからです。しかし、私はいくつかの問題に遭遇しました。 1)その答えは、イベントバスに言及していました。偶数バスは、メインプログラムが所有するグローバル変数です。ウィジェットライブラリがどのようにアクセスできるかは明確ではありません。 2)私はゼロから始めていませんでした。 GWTライブラリコードを拡張していました。そのためには、EventクラスではなくGwtEventクラスから開始する必要がありました。

Piotrの答えは本質的に正しいですが、非常に長いものでした。私のクラスは(間接的に)GWTのWidgetクラスを拡張します。 Widgetは、HandlerManagerオブジェクトの作成など、多くの詳細を処理します。 (ソースコードを調べたところ、EventBusを使用するのではなく、標準のウィジェットが正確に機能します。)

カスタムイベントハンドラーを追加するには、ウィジェットクラスに2つの項目を追加するだけで済みました。それらはここに示されています:

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のイベントクラスとほぼ同じです。 1つのことは注目に値します。その例に基づいて、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);
    }
}
3

GWTの上で [〜#〜] gwtp [〜#〜] フレームワークを使用している場合は、 this Stack を参照してください。

[〜#〜] gwtp [〜#〜] は、「次のGWTプロジェクトを簡素化する完全なモデルビュープレゼンターフレームワーク」です。

1
cellepo