LG3D のイベントモデルは、AWT や Swing のイベントモデルとちょっと異なります。
まず AWT (Swing) のイベントモデルについて。AWT のイベントモデルはイベントを発生するオブジェクトに対してリスナを登録することでイベントを受けられるようにします。
実際にはリスナが自分で登録しないで、他のオブジェクトが登録することもありますが、まぁ大目に見てください。
そして、イベントが発生した時は、イベントは一度イベントキューに入れられます。そして、イベントキューから 1 つづつ取りだされて、イベントが発生したコンポーネント
(下図ではとりあえず EventPublisher としています) に渡されます。この EventPublisher がリスナにイベントを配信します。
このとき、イベントをキューに入れるスレッドと、イベントを配信するスレッドは別になります。
イベント処理はリスナで行います。よく使われるのが無名クラスを使ったリスナですね。
このモデルだとイベントの種類とリスナはかなり強く結びついています。
次に LG3D のイベントモデルです。
LG3D のイベントモデルは、AWT のリスナに相当する部分が 2 つのインタフェースで表されます。イベントを受ける部分と、イベントの処理をおこなう部分です。
クラス図で書くと次のようになります。
org.jdesktop.lg3d.utils.eventadapter.EventAdapter インタフェースはイベントを受け取って、イベントから必要な情報を抽出します。そして、イベントの処理をおこなう org.jdesktop.lg3d.utils.action.Action インタフェースを実装したクラスを呼び出します。
このような構成にすることで Action インタフェースはイベントの種類に依存せずに、処理だけをおこなうことが可能になります。 実際には EventAdapter インタフェースが Action インタフェースへの関連を持つわけはないので、実装クラスが関連を持つようになります。
また、AWT/Swing と違うのが EventAdapter を実装したクラスが提供されていることです。
AWT/Swing では MouseListener など、EventListener の派生インタフェースは提供されています。しかし、これはあくまでもインタフェースでしかありません。MouseAdapter
クラスなどの実装クラスも提供されていますが、単に定義してあるメソッドに対して空の実装をしただけのクラスでしかありません。
それに比べて LG3D では EventAdapter インタフェースを実装したクラスとして、org.jdesktop.lg3d.utils.eventadapter.MousePressedEventAdapter
クラスなどが提供されています。
これらのコンクリートクラスは AWT のリスナに比べると、粒度が小さいのが特徴です。
たとえば、AWT では MouseListener インタフェースに相当するのが、MousePressedEventAdapter, MouseClickedEventAdapter, MouseEnteredEventAdapter の 3 つのクラスで表されます。
粒度を小さくすることでイベントから取り出す情報を決めうちすることができます。
さて、コードではどう書くのでしょう。
コンポーネントにリスナ登録をおこなうのはおなじなのですが、コンストラクタに Action インタフェースを実装したクラスを指定します。
comp3d.addListener(new MousePressedEventAdapter(
new ScaleActionBoolean(comp3d, 1.1f))); |
comp3d が Component3D オブジェクトと考えてください。
MousePressedEventAdapter クラスが EventAdapter インタフェースを実装したクラス。そして、ScaleActionBoolean クラスが ActionBoolean インタフェースを実装したクラスです。
イベント発生時はどのような動きになるかというと、次の図のようになります。
このシーケンス図に出てくる EventProcessor オブジェクトは org.jdesktop.lg3d.displayserver パッケージで定義されています。しかし、AWT と同じようにあまり意識する必要はないです。
配信するときは EventProcessor
オブジェクトから直接 EventAdapter オブジェクト (この場合は MousePressedEventAdapter オブジェクト)
に配信されます。AWT がコンポーネントからイベントが配信されていたのとは異なりますね。
processEvent メソッドの引数は LgEvent オブジェクトです。
おぉ、ここではじめてイベントクラスが出てきました。
LgEventListener オブジェクトは Action オブジェクト (この場合は ScaleAction オブジェクト)
の performAction メソッドをコールします。
AWT
ではイベントにくるまれている情報が、LG3D では performAction の引数になります。つまり、EventAdapter がイベントから情報を抽出してくれるのです。
そして、扱う情報に応じて Action
の派生クラスが定義されています。
主なところだと
- org.jdesktop.lg3d.utils.action.ActionNoArg インタフェース
- org.jdesktop.lg3d.utils.action.ActionBoolean インタフェース
- org.jdesktop.lg3d.utils.action.ActionFloat インタフェース
- org.jdesktop.lg3d.utils.action.ActionFloat3 インタフェース
などがあります。
これらのインタフェースの performAction メソッドの第 1 引数はすべて LgEventSource オブジェクトです。LgEventSource インタフェースはイベントが発生したコンポーネントなどを表すためのインタフェースです。
ActionNoArg インタフェースは引数が LgEventSource オブジェクトだけ。ActionBoolean インタフェースは
boolean の引数、ActionFloat は float の引数、ActionFloat3 は float の引数が 3 つあります。
たとえば、ScaleActionBoolean クラスは ActionBoolean インタフェースを実装しています。
他の Action インタフェースを実装したクラスには
- org.jdesktop.lg3d.utils.action.ScaleActionFloat
- org.jdesktop.lg3d.utils.TranslateActionBoolean
- org.jdesktop.lg3d.utils.TranslateActionFloat
- org.jdesktop.lg3d.utils.TransparencyActionBoolean
- org.jdesktop.lg3d.utils.TransparencyActionFloat
などがあります。
クラス名の Boolean とか Float がインタフェース名に対応しています。
しかし、こうしているとちょっと困ったことがあります。
イベントアダプタは扱う情報が決まっているので、コールできるアクションのインタフェースが決まっているということです。
MousePressedEventAdapter であれば、マウスが押されているかどうかという情報とマウスの位置情報を扱うので、ActionBoolean, ActionBooleanFloat2, ActionBooleanFloat3 の 3 つのインタフェースを扱うことができます。
困るのは、たとえば、ScaleActionBoolean を使いたいのだけれども、MouseClickedEventAdapter クラスのようにイベントアダプタが ActionBoolean インタフェースを扱えない場合です。
そんなときにはイベントアダプタとアクションの間にアクションアダプタを挟むようにします。
たとえば、トグル処理にするには org.jdesktop.lg3d.utils.actionadapter.ToggleAdapter
クラスを使用します。org.jdesktop.lg3d.utils.actionadapter パッケージには他にもこのようなクラスが提供されています。
例として MouseClickedEventAdapter クラスで ActionBoolean インタフェースを使う場合を示しました。
comp3d.addListener(new MouseClickedEventAdapter(
new ToggleAdapter(
new ScaleActionBoolean(comp3d, 1.1f)))); |
このようにデリゲートするクラスをコンストラクタで示すようにします。
|