最初のサンプルは、従来のJDK1.0のスタイルです。この方法では、イベント処理は、 ボタンやチョイスといったComponentではなく、それらの入れ物であるContainerの中 で行われます。この例ではFrameを継承したSample1の中でイベント処理が行われて います。 この方法は、原理的には、JDK1.0のイベントが、GUIを構成するComponent/Containerの 階層構造を、下方から上方に、伝播する(正確に言うと伝播「出来る」)ことを 利用しています。 典型的なスタイルでは、Container中の全てのイベントが一か所に 集められますので、メソッドhandleEvent()の処理の基本は、どのComponentから どのようなイベントが来たかを判定することです。
こうしたイベント処理を自分で書く時には、特徴的なif文の ネストも、意外と苦にならないかもしれません。書いている時には、「自明」の 処理に思えるでしょう。所が、他人が書いたこの種のif文のカタマリを、 解読するのはなかなか面倒なものです。また、GUIの設計を変更して、パネルを 一枚追加しようとするような場合、このパネルで発生するイベントを、この パネル内で処理するか、それとも、これまでの上位のContainerでのイベント 処理に合流させるか悩むことになるでしょう。こんな時には、イベント処理メソッド の返値をtrueにするかfalseにするかにも神経を使う必要が出て来ます。
上位のContainerでまとめて処理するタイプのJDK1.0でのイベント処理は、 簡単なプログラムの場合には、うまく動くし、わかりやすくもあるのですが、 プログラムが大きくなるにつれて、読みにくく間違いも生まれやすくなります。 また、既にできあがっているパネルを、GUIのモジュールとして再利用しようと するような場合には、このタイプのイベント処理は、なかなかうまく使えません。
== サンプル1 =============================================================== import java.awt.*; class Sample1 extends Frame { Button button ; Checkbox checkbox ; List list ; Choice choice ; Panel panel ; TextField textfield ; Sample1(){ button = new Button("ボタン"); checkbox = new Checkbox("チェック"); list = new List(1); list.add("リスト1"); list.add("リスト2"); list.add("リスト3"); choice = new Choice(); choice.add("チョイス1"); choice.add("チョイス2"); choice.add("チョイス3"); panel = new Panel(); panel.add(button); panel.add(checkbox); panel.add(list); panel.add(choice); add(panel,"North"); textfield = new TextField(); add(textfield,"South"); pack(); setVisible(true); } public boolean handleEvent(Event evt){ if ( evt.target.equals(button) && evt.id == Event.ACTION_EVENT ){ textfield.setText("ボタンが押されました。" ); return true ; } if ( evt.target.equals(checkbox) && evt.id == Event.ACTION_EVENT ){ if ( checkbox.getState() ){ textfield.setText("チェックされました。" ); } else { textfield.setText("チェックが外されました。" ); } return true ; } if ( evt.target.equals(list) && evt.id == Event.LIST_SELECT){ textfield.setText( list.getSelectedItem() + "が選ばれました。" ); return true ; } if ( evt.target.equals(choice) && evt.id == Event.ACTION_EVENT){ textfield.setText( choice.getSelectedItem() + "が選ばれました。" ); return true ; } if ( evt.target.equals(textfield) ){ if ( evt.id == Event.LOST_FOCUS){ textfield.setText("フォーカスが無くなりました。"); return true; } if ( evt.id == Event.GOT_FOCUS ){ textfield.setText("フォーカスがあります。"); return true; } } if ( evt.id == Event.WINDOW_DESTROY ){ System.exit(0); } return false ; } public static void main(String argv[]){ new Sample1(); } } =============================================================================
JDK1.0では、もう一つのスタイルで、イベント処理が可能です。 それは、Componentの継承関係を土台としたイベント処理です。 サンプル2を見てください。ここでは、イベントを受け取るComponentが、 新しいクラスに拡大されていて、イベント処理用のメソッドが再定義されています。 ButtonやCheckboxではactionメソッドが、ListやTextFieldでは、handleEvent メソッドが書き換えられています。
実際には、このスタイルでイベント処理をすることに、皆さんは、あまり経験が ないかもしれません。この例では、たまたま、Componentは、それぞれの種類について 一個だけですが、もしもボタンが4個あったら、このスタイルでは、新しく 4個のクラスを作らなくてはなりません。イベント処理の為だけに、 外見も機能も変わらないのに、ボタンの数だけ新しいボタン・クラスを作るのは、 すこし不自然なことです。
この例を見ると、サンプルの為のサンプルのように感じるかもしれませんが そうではありません。 例えば、Canvasクラスを拡大して、ボタンの 上にイメージを張り付けたImageButtonを作るような場合に、新しいクラスを 作ってイベント処理を行うというこの方法は、本質的な役割を果たします。 JDK1.0で使われた方法と言っても、この方法は、ある意味では、オブジェクト 指向言語としてのJavaに固有の方法であって、JDK1.1では使われなくなる ということはありません。
== サンプル2 =============================================================== import java.awt.*; class S2Button extends Button { Sample2 s2; public S2Button(String label, Sample2 s2 ){ super(label); this.s2 = s2 ; } public boolean action(Event evt,Object what){ s2.textfield.setText("ボタンが押されました。" ); return(true); } } class S2Checkbox extends Checkbox { Sample2 s2; public S2Checkbox(String label, Sample2 s2 ){ super(label); this.s2 = s2 ; } public boolean action(Event evt,Object what){ if ( s2.checkbox.getState() ){ s2.textfield.setText("チェックされました。" ); return(true); } else { s2.textfield.setText("チェックが外されました。" ); return(true); } } } class S2List extends List { Sample2 s2; public S2List(int i, Sample2 s2 ){ super(i); this.s2 = s2 ; } public boolean handleEvent(Event evt){ if ( evt.id == Event.LIST_SELECT){ s2.textfield.setText( s2.list.getSelectedItem() + "が選ばれました。" ); return(true); } return(false); } } class S2Choice extends Choice { Sample2 s2; public S2Choice( Sample2 s2 ){ super(); this.s2 = s2 ; } public boolean action(Event evt,Object what){ s2.textfield.setText( s2.choice.getSelectedItem() + "が選ばれました。" ); return(true); } } class S2TextField extends TextField{ Sample2 s2; public S2TextField( Sample2 s2 ){ super(); this.s2 = s2 ; } public boolean handleEvent(Event evt){ if ( evt.id == Event.LOST_FOCUS){ s2.textfield.setText("フォーカスが無くなりました。"); return(true); } if ( evt.id == Event.GOT_FOCUS ){ s2.textfield.setText("フォーカスがあります。"); return(true); } return(true); } } class Sample2 extends Frame { S2Button button ; S2Checkbox checkbox ; S2List list ; S2Choice choice ; Panel panel ; S2TextField textfield ; Sample2(){ button = new S2Button("ボタン",this); checkbox = new S2Checkbox("チェック",this); list = new S2List(1,this); list.add("リスト1"); list.add("リスト2"); list.add("リスト3"); choice = new S2Choice(this); choice.add("チョイス1"); choice.add("チョイス2"); choice.add("チョイス3"); panel = new Panel(); panel.add(button); panel.add(checkbox); panel.add(list); panel.add(choice); add(panel,"North"); textfield = new S2TextField(this); add(textfield,"South"); pack(); setVisible(true); } public boolean handleEvent(Event evt){ if ( evt.id == Event.WINDOW_DESTROY ){ System.exit(0); } return false ; } public static void main(String argv[]){ new Sa外されました。" ); } } } =============================================================================
JDK1.0でのイベント処理は、このように、Containerで一括して行うスタイルと、 Component毎に個別に行うスタイルの二つに大きく分かれます。 前者は、GUIの階層構造とそこでのイベントの伝達を、後者は、クラスの 拡大・継承とイベント処理メソッドの再定義を基礎としています。
少し視点を変えると、次のように考えることが出来ます。 前者の「上位Container一括」型では、例えばボタンは、イベントの発生源として、 はっきりと意識されていますが、イベントの受け取り手は、何故か、そのボタンを含む 手近のContainerでなければなりません。 逆に、後者の「個別Component」型では、ボタンは、イベント発生源にして、 その自明な受け取り手であり、かつ、イベントを処理する中心的な舞台として 意識されます。