next up previous contents
Next: Listener の導入 Up: JDK1.1のイベント・モデル Previous: JDK1.1のイベント・モデル

これまでのモデル

上位のContainerでまとめて処理

最初のサンプルは、従来の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();
  }
}
=============================================================================

個々のComponentでイベント処理

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



maruyama@wakhok.ac.jp