最初のサンプルは、従来の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」型では、ボタンは、イベント発生源にして、 その自明な受け取り手であり、かつ、イベントを処理する中心的な舞台として 意識されます。