例えば、1.0 のアプレットはすべて、 1.1 でコンパイルが成功するはずです。そして古い (1.0) クラスファイルを使用するか、再コンパイルして新しい (1.1) クラスファイルを使用するかに関わらず、1.0 のアプレットは 1.1 ベースのブラウザで動作します。また、アプレットソースを変更しない限り、新しい (1.1) クラスファイルは古い (1.0ベースの) ブラウザで動作するはずです。しかし、1.1 の機能を使用するためにアプレットを変更した場合、アプレットは 1.1 API への更新をしていない Netscape 3.0 のようなブラウザでは動作しないでしょう。
さて、なぜプログラムを 1.1 AWT API に更新する必要があるのでしょうか? 次の 3 つの理由があります:
このドキュメントの後半では、 AWT の変更内容 および プログラムの変換方法 について説明します。そして、プログラムを 1.1 に変換する 例 を示します。
イベント処理のために、非コンポーネント ("delegation")、データ転送 (切り取り-コピー-貼り付けなど)、デスクトップカラー (外観の一貫性を向上させるため)、マウスレス操作、およびコンポーネント固有カーソルによるアーキテクチャのサポートが追加された。しかし、これらアーキテクチャの改善に関連する機能のすべてが、完全に実装されているわけではない。例えば、データ転送アーキテクチャによってドラッグとドロップが可能にはなったが、ドラッグとドロップは 1.1 に実装はされていない。JDK1.1 - AWT の拡張には、最終の 1.1 リリースで実装された確実な詳細が記されている。
こうした変更によって、プログラムのうち、GUI ビルダや JavaBeans を使用したものがコンポーネントに問合わせをして、コンポーネントのプロパティを決定できるようになった。また、プログラマが簡単に AWT API を学習して使用できるようになった。
例えば、イメージとグラフィック機能が向上し、PopupMenu および ScrollPane クラスが追加された。PopupMenu
クラスによって、メニューバーに結び付けられていないメニューを持つことができるようになった。ScrollPane クラスによって、スクロール領域の実装がスクロール速度の向上と同様に、簡単にできるようになった。
イベント処理の変更の詳細は このドキュメントの後半にあります。AWT の変更内容の詳細については、 JDK1.1 - AWT の拡張を参照してください。
% cp MyClass.java MyClass.java.orig % cp MyClass.class MyClass.class.orig
ほとんどの変換を自動的に実行する sed スクリプトが提供されます。(スクリプトがなにを変換するかの詳細については、単純な名前変更を参照してください。) このスクリプトは UNIX sed コマンド用に設計されています。PC 上で開発をしている場合、UNIX ツールの PC バージョンを提供する MKS Toolkit などの製品を使用して、スクリプトを実行可能です。次に UNIX システム上のシェルプロンプトで sed スクリプトの使用方法を示します:
% updateAWT MyClass.java > tmp.java
変更をチェックし、明らかな異常が起こらないことを確認します。例えば、UNIX システム上で次のコマンドを実行できます:
変更が OK であることを確認後、次のようにします:% diff MyClass.java tmp.java
% mv tmp.java MyClass.java
% javac MyClass.java
コンパイルエラーまたは警告が表示される可能性があります。最も多いものは、プログラムが推奨されないメソッドを使用した、またはメソッドが見つからなかった、という警告です。
プログラムが、ある理由から使用を推奨しない 1.1 以前のもののような、推奨されないメソッドを呼び出した場合、コンパイラは警告を表示します。 例えば:
% javac MyClass.java Note: MyClass.java uses a deprecated API. Recompile with "-deprecation" for details. 1 warning %
注:
1.1.1 Java コンパイラは、プログラムが推奨されないメソッドをオーバーライドするとき警告を出しますが、1.1 コンパイラは出しません。1.1 コンパイラが警告を出すのは、プログラムが推奨されないメソッドを呼び出しするときだけです。例えば、プログラムが action メソッドをオーバーライドする場合、action の実装のほとんどがスーパークラスの実装を呼び出ししないため、1.1 コンパイラは通常警告を出しません。
各ソースファイルが推奨されないメソッドを使用しているかどうかの情報を得るためには、Java コンパイラの -deprecation オプションを使用します。例えば次のようになります:
% javac -deprecation MyClass.java
MyClass.java:18: Note: The method boolean handleEvent(java.awt.Event) in
class java.awt.Component has been deprecated, and class MyClass overrides
it.
public boolean handleEvent(Event event) {
^
MyClass.java:26: Note: The method boolean handleEvent(java.awt.Event) in
class java.awt.Component has been deprecated.
return super.handleEvent(event);
^
Note: MyClass.java uses a deprecated API. Please consult the
documentation for a better alternative.
2 warnings
推奨されないメソッドの代替については、1.1 AWT の推奨されないメソッドで調べることができます。(それは逆にイベント関連メソッドについては、このドキュメントを参照するように指示しています。)
メソッドを見つけることができなかったというコンパイラエラーを受け取った場合、sed スクリプトがメソッドの名前を不正に変更した可能性があります。この問題を修正するためのヘルプについては、単純な名前変更 を参照してください。
Component handleEvent メソッドが(action など、それが呼び出すメソッドと共に)イベント処理の中心でした。
Component オブジェクトだけがイベントを処理でき、イベントを処理するコンポーネントは、イベントが発生するコンポーネントか、またはコンポーネント包含階層の上位コンポーネントでなければなりませんでした。
1.1 では、もはやイベント処理はコンポーネント包含階層のオブジェクトに限定されず、handleEvent メソッドはイベント処理の中心ではなくなりました。代わりに、どのタイプのオブジェクトでもイベントリスナとして登録できます。イベントリスナは、関心のある登録したイベントの型についてだけ通知を受け取ります。イベントを処理するためだけに Component サブクラスを生成する必要はもうなくなりました。
バージョン 1.1 へアップグレードするとき、イベント処理コードを変換する最も簡単な方法は、同じクラスにそれを残し、そのクラスをそのイベントの型のリスナにすることです。
別の可能性としては、イベント処理コードを、 1 つまたはそれ以上の非コンポーネントリスナに集中化することです。このアプローチを取れば、プログラムの GUI を実装の詳細から分離できます。このためには、実在するコードを修正し、リスナがコンポーネントから必要とするすべての状態の情報を得られるようにする必要があります。このアプローチは、プログラムのアーキテクチャを明瞭に保っておきたい場合に価値があります。
第 3 の可能性としては、processXxx メソッドの 1 つをオーバー
ライドすることですが、Component を既に拡張している場合でない限り推奨しません。これらのイベント処理アプローチについては、設計ドキュメント
delegation イベントモデル で取り上げます。
同じプログラム内で 1.0 イベントモデルと 1.1 イベントモデルを混在させないことを推奨します。そうした場合の結果は予測不可であり、デバッグできない可能性があります。
-deprecation フラグ付でコンパイルすると、古いスタイルのイベント処理メソッドを含むリストが生成されます。(1.1.1 以前は、コンパイラが完全なリストを生成しなかったため、ソースファイルで "Event" を検索する必要がありました。) コード検索中に、どのクラスがイベント処理の目的のためだけに存在するかを注意しておく必要があります。そのようなクラスは削除ができます。
次に、1.0 コンポーネントをリスナに変換するときの手順を示します:
java.awt.event パッケージをインポートするようにする:
import java.awt.event.*
Button、
List、
MenuItem、
TextField、
Checkbox、および
Choice オブジェクトを探すように指示している。
Button によって生成されるアクションイベントを処理しようとする場合、この表では ActionListener インタフェースを実装するように指示している。
public class MyClass extends SomeComponent
implements ActionListener
代替:
インタフェースを実装する代わりに、イベントアダプタクラスを拡張する内部クラスを宣言することができる。インタフェースのメソッドを 1 つだけ実装する必要があるとき、内部クラスは有効だが、インタフェースには他の多くのメソッドが含まれる。
詳細については、イベント処理のための内部クラスの使用を参照のこと。
this を適当な型のリスナとして登録する。例えば:
代替: イベントを処理するために内部クラスを使用する場合、代わりに内部クラスのインスタンスを登録する。 詳細については、イベント処理のための内部クラスの使用を参照のこと。newComponentObject.addActionListener(this);
ActionListener は actionPerformed というただ 1 つのメソッドを持つ。そこで新しいメソッドを作成する手っ取り早い方法として、次のように action メソッドのシグネチャを this から単純に変更することができる:
public boolean action(Event event, Object arg) {
this に対して:
public void actionPerformed(ActionEvent event) {
代替:
イベントを処理するために内部クラスを使用する場合、メソッドの空の実装を作成する必要はない。
詳細については、イベント処理のための内部クラスの使用を参照のこと。
return 文を削除する。
event.target への参照を event.getSource() に変更する。
イベント処理内部クラスの実装は、クラスにイベントリスナインタフェースを実装させる一種の変形です。以前と同様に、どのインタフェースをクラスが処理する必要のあるイベントに対応させるか判別します。しかし、インタフェースを実装する代わりに、そのインタフェースに対応しているアダプタを拡張する内部クラスを作成します。例えば、WindowListener を実装する代わりに、 WindowAdapter を拡張する内部クラスを実装できます。そして、単純に内部クラスのインスタンスを作成し、例えば addWindowListener メソッドで、それをリスナの適当な種類として登録します。次に例を示します:
public class MyClass ... {
...
//Where the window is created:
newWindow.addWindowListener(new MyInnerClass());
...
class MyInnerClass extends WindowAdapter {
public void windowClosing(WindowEvent event) {
if (inAnApplet) {
dispose();
} else {
System.exit(0);
}
}
}
...
}
コンパイル後、内部クラスバイトコードは、次のような名前の別個な .class ファイルに置かれます:
MyClass$MyInnerClass.class.
内部クラスを使用する完全な例としては、innerclass/DialogWindow.java を参照してください。
AppletButtonという名のクラスの支援を受けてアプレットとしても実行できます。
(
Java チュートリアル を読んだ方なら、
"Dialog の使い方"の節にもこの例が出てくるので、このプログラムを理解しやすいかもしれません。)
次に AppletButton の助けを借り、アプレットとして動作しているバージョン 1.0 の DialogWindow( ソースコードへのリンクがあります) を示します:
% mkdir 1.0example % mv DialogWindow.class 1.0example % cp DialogWindow.java 1.0example
% updateAWT DialogWindow.java > tmp.java % diff DialogWindow.java tmp.java 33c33 < dialog.show(); --- > dialog.setVisible(true); 38c38 < textArea.appendText(text + "\n"); --- > textArea.append(text + "\n"); 47c47 < window.show(); --- > window.setVisible(true); 87c87 < hide(); --- > setVisible(false); % mv tmp.java DialogWindow.java
% which javac /usr/local/java/jdk1.1.1/solaris/bin/javac % javac DialogWindow.java Note: DialogWindow.java uses a deprecated API. Recompile with "-deprecation" for details. 1 warning %
% javac -deprecation DialogWindow.java
DialogWindow.java:18: Note: The method boolean handleEvent(java.awt.Event) in
class java.awt.Component has been deprecated, and class DialogWindow overrides
it.
public boolean handleEvent(Event event) {
^
DialogWindow.java:26: Note: The method boolean handleEvent(java.awt.Event) in
class java.awt.Component has been deprecated.
return super.handleEvent(event);
^
DialogWindow.java:29: Note: The method boolean action(java.awt.Event,
java.lang.Object) in class java.awt.Component has been deprecated, and class
DialogWindow overrides it.
public boolean action(Event event, Object arg) {
^
DialogWindow.java:81: Note: The method boolean action(java.awt.Event,
java.lang.Object) in class java.awt.Component has been deprecated, and class
SimpleDialog overrides it.
public boolean action(Event event, Object arg) {
^
Note: DialogWindow.java uses a deprecated API. Please consult the
documentation for a better alternative.
5 warnings
%
ご覧になって分かるとおり、このクラスのコンパイルは成功していますが、推奨されないメソッド (handleEvent および action) を呼び出し、またはオーバーライドしています。
コンポーネントのリスナ化で前述したように、DialogWindowプログラムにとって、この手順はイベント処理コードの変換だけからなっています。DialogWindow.java で "Event" を検索すれば、このプログラムを包む 2 つのクラス(DialogWindow および SimpleDialog)でイベント処理コードを見つけることができます。
次に DialogWindow プログラム内のイベント処理の概要を示します:
DialogWindow は、自分自身のために ウィンドウ破壊イベントを処理する。
DialogWindow は、そこに含まれるコンポーネントのためにアクショ
ンイベントを処理する。よく調べると、 アクションイベントを生成できるコンポーネントがたった 1 つだけ含まれていることが分かる: Button である。
SimpleDialog そこに含まれるコンポーネントのために、アクションイベントを処理する。よく調べると、アクションイベントを生成できるコンポーネントが 3 つ含まれていることが分かる: 2 つの Button (キャンセルと設定)および TextField である。
DialogWindow および SimpleDialog には、非イベント処理コードが入っており、イベント処理コードをよそに移動してもこれらを排除できない。
イベント処理コードの現在のロケーションは理に適ったものです。DialogWindow にすべてのイベントを処理させることもまた、それがこのアプリケーションの制御クラスであるという理由から、意味をもちます。さらに他の可能性としては、アクションイベントすべてを処理する非 Component 制御クラスを導入することも考えられます。
DialogWindow プログラムを変換するために、そのイベント処理コードを前と同じコンポーネントにおいて置き、コンポーネントにリスナインタフェースを実装させます。
次の例は、変換された DialogWindow プログラム内のイ ベント関連コードの最重要部分を示しています。顕著な変更は太字で示します。
import java.awt.event.*;
public class DialogWindow extends Frame
implements WindowListener,
ActionListener {
...
public DialogWindow() {
...
Button button = new Button("Click to bring up dialog");
button.addActionListener(this);
...
addWindowListener(this);
}
public void windowClosed(WindowEvent event) {
}
public void windowDeiconified(WindowEvent event) {
}
public void windowIconified(WindowEvent event) {
}
public void windowActivated(WindowEvent event) {
}
public void windowDeactivated(WindowEvent event) {
}
public void windowOpened(WindowEvent event) {
}
public void windowClosing(WindowEvent event) {
if (inAnApplet) {
dispose();
} else {
System.exit(0);
}
}
public void actionPerformed(ActionEvent event) {
if (dialog == null) {
dialog = new SimpleDialog(this, "A Simple Dialog");
}
dialog.setVisible();
}
...
}
class SimpleDialog extends Dialog implements ActionListener {
...
SimpleDialog(Frame dw, String title) {
...
field = new TextField(40);
field.addActionListener(this);
...
Button b = new Button("Cancel");
b.addActionListener(this);
setButton = new Button("Set");
setButton.addActionListener(this);
...
}
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if ( (source == setButton)
| (source == field)) {
parent.setText(field.getText());
}
field.selectAll();
setVisible(false);
}
}
WindowListener インタフェースを実装する代わりに、DialogWindow に、単純に WindowAdapter を拡張する内部クラスを含めることができます。次に内部クラスと共に実装された DialogWindow の最重要点を示します。1.0 バージョンの顕著な変更点は、太字になっています。
import java.awt.event.*;
public class DialogWindow extends Frame
implements ActionListener {
...
public DialogWindow() {
...
Button button = new Button("Click to bring up dialog");
button.addActionListener(this);
...
addWindowListener(new DWAdapter());
}
class DWAdapter extends WindowAdapter {
public void windowClosing(WindowEvent event) {
if (inAnApplet) {
dispose();
} else {
System.exit(0);
}
}
}
public void actionPerformed(ActionEvent event) {
if (dialog == null) {
dialog = new SimpleDialog(this, "A Simple Dialog");
}
dialog.setVisible(true);
}
...
}
DialogWindowアプリケーションを使用することによって、これがすべてのイベントを正しく処理しているかを調べることができます。% java DialogWindow
handleEvent メソッドで処理されます。第 2 列にはイベント型を生成できる 1.0 コンポーネントを示します。第 3 列には、リストアップされたイベントの 1.1 相当の処理を支援するリスナインタフェースを示します。第 4 列には各リスナインタフェースのメソッドを示します。
| 1.0 | 1.1 | ||
|---|---|---|---|
| イベント/メソッド | 生成元 | インタフェース | メソッド |
ACTION_EVENT/action
| Button,
List,
MenuItem,
TextField
| ActionListener
|
actionPerformed(ActionEvent)
|
Checkbox,
CheckboxMenuItem,
Choice
| ItemListener
| itemStateChanged(ItemEvent)
| |
WINDOW_DESTROY
WINDOW_EXPOSE
WINDOW_ICONIFY
WINDOW_DEICONIFY
| Dialog,
Frame
| WindowListener
| windowClosing(WindowEvent)
windowOpened(WindowEvent)
windowIconified(WindowEvent)
windowDeiconified(WindowEvent)
windowClosed(WindowEvent)
(1.0 に相当するものなし)
windowActivated(WindowEvent)
(1.0 に相当するものなし)
windowDeactivated(WindowEvent)
(1.0 に相当するものなし)
|
WINDOW_MOVED
| Dialog,
Frame
| ComponentListener
|
componentMoved(ComponentEvent)
|
SCROLL_LINE_UP
SCROLL_LINE_DOWN
SCROLL_PAGE_UP
SCROLL_PAGE_DOWN
SCROLL_ABSOLUTE
SCROLL_BEGIN
SCROLL_END
| Scrollbar
| AdjustmentListener
(または新しい ScrollPane
クラスを使用)
| adjustmentValueChanged(AdjustmentEvent)
|
LIST_SELECT
LIST_DESELECT
| Checkbox,
CheckboxMenuItem,
Choice,
List
| ItemListener
| itemStateChanged(ItemEvent)
|
MOUSE_DRAG/mouseDrag
MOUSE_MOVE/mouseMove
| Canvas,
Dialog,
Frame,
Panel,
Window
| MouseMotionListener
| mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
|
MOUSE_DOWN/mouseDown
MOUSE_UP/mouseUp
MOUSE_ENTER/mouseEnter
MOUSE_EXIT/mouseExit
| Canvas,
Dialog,
Frame,
Panel,
Window
| MouseListener
| mousePressed(MouseEvent)
mouseReleased(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mouseClicked(MouseEvent)
(1.0に相当するものなし)
|
KEY_PRESS/keyDown
KEY_RELEASE/keyUp
KEY_ACTION/keyDown
KEY_ACTION_RELEASE/keyUp
| Component
| KeyListener
| keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)
(1.0に相当するものなし)
|
GOT_FOCUS/gotFocus
LOST_FOCUS/lostFocus
| Component
| FocusListener
| focusGained(FocusEvent)
focusLost(FocusEvent)
|
| 1.0に相当するものなし | ContainerListener
| componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
| |
| 1.0に相当するものなし | TextListener
| textValueChanged(TextEvent)
| |