教育サーバーのページ
オンラインテキスト目次
ソフトウエア基礎演習

課題の模範解答

GUIアプリケーション (AWT)

課題へ

Javaでは、ウィンドウやボタン、スクロールバーなどのGUI(Graphical User Interface)を作成することは可能である。そのために、AWT(Abstract Window Toolkit)というパッケージを利用してプログラミングする。このAWTを利用して記述したプログラムは、どのコンピュータ上で実行しても同じように動作する。 AWTではフレームというウィンドウがベースとなって、そのフレームにさまざまなウィンドウやボタンなどの部品を配置する。

パッケージ
Java2には、たくさんのクラスがあり、それぞれの機能ごとにグループに分けて提供されている。ある機能を実現するためにグループ化されたクラスの集まりをパッケージという。
イベントドリブン(イベント駆動)
一般に、ウィンドウをもったアプリケーションを作る場合には、イベントドリブン(イベント駆動)という考え方を理解する必要がある。ウィンドウシステムでは、ユーザはマウスをクリックしたり、ドラッグしたりする。また、他のアプリケーションが起動されて、ウィンドウが重なったりする。このような「何事かが起きる」こと、あるいはその「何事か」をイベントとよぶ。

イベントドリブン(イベント駆動)型アプリケーション
ユーザがマウスをクリックした場合、それはイベントとなる。イベントドリブン型アプリケーションでは、そのイベントに対して、自分のアプリケーションがどう対応・処理するのかを記述する。ボタンやメニューなどのあるアプリケーションをGUIアプリケーションと呼ぶが、イベントドリブン型でもある。

イベント処理の仕組み
Javaでは、イベントの発生源をイベントソースと呼ぶ。ボタンやラベルなどほとんどのコンポーネントがイベントソースとなる。一方,イベントを受け取る側をイベントリスナと呼ぶ。イベントリスナは、イベントソースからイベントを受け取り、イベントに対する処理をおこなう。Javaでは、イベントソースでイベントが発生すると、自分自身に登録されているイベントリスナにそのことを知らせ、予め定義されている処理を実行する。登録するイベントリスナは、イベントソースと、そこで発生するイベントにより異なるので、それぞれの状況に応じたイベントリスナを登録する必要がある。

AWTによるGUIアプリケーション作成の流れは以下のようになる。

  1. java.awtとjava.awt.eventパッケージのimport
  2. フレームクラスのインスタンスの作成
  3. レイアウト方式の指定
  4. GUI部品(コンポーネント)のインスタンス(オブジェクト)の生成
  5. 生成されたGUI部品(コンポーネント)オブジェクトの配置
  6. 表示

AWTによるGUIアプリケーション作成では、フレームというウィンドウがベースとなり、そのウィンドウの上にウィンドウやボタンなどのGUI部品を配置する。 特に、このGUI部品をコンポーネントと呼び、AWTプログラムはこのコンポーネントを組み合わせて1つのウィンドウを構成していく。 複数のコンポーネントをまとめたものをコンテナと呼び、新たなコンポーネントとしてみなしたり、コンテナの中にコンテナを包含できる。フレームは汎用的なコンテナであり、Frameクラスを使って作成できる。 コンポーネントをどのようにフレームに配置するかを指示するためには、レイアウトマネージャにレイアウト方式を指定する必要がある。実際には、AWTではレイアウトマネージャがコンポーネントのレイアウトを自動的に実行する。

Frameクラスを使ってGUIアプリケーションを作成する

1. ウインドウを表示する最も簡単なプログラム

次のプログラム MyWinApp.java はフレームというウィンドウをベースとした普通のウィンドウを表示するプログラムである。 このプログラムを実行することで表示されるウィンドウは、見た目は普通のウィンドウであるので、その右上の終了ボタンで終了できそうだが、実際には終了することはできない。これを終了するためには、Ctrl+Cなどで強制終了させる必要がある。

import java.awt.*;

public class MyWinApp {
    public static void main(String[] args) {
       Frame f = new Frame();
       f.setSize(300, 200);
       f.setVisible(true);
    }
}
実行例は、たとえば次のようである。
% java MyWinApp
...
....
ウィンドウシステムを強制終了させる場合には、コマンドプロンプトウィンドウ上でCtrl+Cを入力する。
プログラムの説明

プログラム前半の

Frame f = new Frame();
では、新しいフレーム(基本的なウィンドウ)オブジェクトを作成している。
f.setSize(300, 200);
では、ウィンドウの横幅を300、高さを200に設定している。
f.setVisible(true);
は、ウィンドウの表示命令を示している。

2. 普通に終了できる機能をもつウィンドウを表示するプログラムの説明

次のプログラム MyWinApp1.java はフレームというウィンドウをベースとした普通のウィンドウを表示するプログラムである。 このプログラムを実行することで表示されるウィンドウは、その右上にある終了ボタンで終了できる。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame {
    public MyFrame() {
       setSize(300, 200);
       addWindowListener(new MyWindowAdapter());
    }
}


class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
       System.exit(0);
    }
}

public class MyWinApp1 {
    public static void main(String[] args) {
       MyFrame f = new MyFrame();
       f.setVisible(true);
    }
}
実行例は、たとえば次のようである。
% java MyWinApp1
...
....
ウィンドウのクローズボタンをクリックすることで、ウィンドウシステムを正常に終了させる ことができる

プログラムの説明

プログラム前半の

class MyFrame extends Frame
以下では、新しいフレームクラスMyFrameをFrameクラスを継承して定義している。
addWindowListner(new MyWindowAdapter());
では、イベントソースであるウィンドウシステムとイベントリスナであるMyWindowAdapterとを関連付けている。これによって、WindowEventを受け取るイベントリスナを指定している。つまり、イベントソースであるウィンドウのクローズボタンをクリックしたときには、ウィンドウクローズを通知するイベントがイベントリスナに伝えられる。アプリケーションはそのイベントリスナで定義されているメソッドwindowClosing(WindowEvent e)を起動し、終了処理を実行する。
class MyWindowAdapter extends WindowAdapter
では、イベントリスナーである MyWindowAdapterクラスはWindowAdapterクラスを継承して作成している。
public void windowClosing(WindowEvent e){
       System.exit(0);} 
では、プログラムを正常終了するための処理が記述されている。

WindowAdapterクラスとは何か?

Windowを閉じるイベントを処理するイベントリスナーはWindowListenerの はずであるが、ここではWindowAdapterというクラスを使っている。これは なぜであろうか。

比較としてWindowListenerを使って上記と同様の動作をするプログラムを書いてみる。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame {
    public MyFrame() {
	setSize(300,200);
	addWindowListener(new MyWindowListener());
    }
}

class MyWindowListener implements WindowListener {
    public void windowClosing(WindowEvent e) {
	System.exit(0);
    }
    public void windowOpened(WindowEvent e) {}
    public void windowClosed(WindowEvent e) {}
    public void windowIconified(WindowEvent e) {}
    public void windowDeiconified(WindowEvent e) {}
    public void windowActivated(WindowEvent e) {}
    public void windowDeactivated(WindowEvent e) {}
}

public class MyWinApp2 {
    public static void main(String[] args) {
        MyFrame f = new MyFrame();

	f.setVisible(true);
    }
}

WindowListenerはインタフェースなので、extendsでなくimplements を使う。インタフェースとは、メソッドしかないクラスのようなものである。 インタフェースではどういうメソッドを使うかといった宣言だけがされていて、 メソッド自体は定義されていない。 そしてインタフェースを使う時(実装するという)には、 このメソッドを全て定義する必要がある(定義しないとエラーが出る)。 このため、上記では、windowOpened(), windowClosed()... といった、 WindowListenerインタフェースで定義されている抽象メソッドを全て定義している。 これを省略することはできない。

これでは面倒なので、全てのメソッドを定義したAdapterクラスが用意されている。 Adapterクラスを使う(継承する)と、自分が定義したいメソッドだけを定義すればよくなる。MyWinApp1.javaでは、WindowAdapterを使用しているため、定義したい windowClosing()メソッドだけを書けば良く、他のwindowOpened(),windowClosed()... といったメソッドは書かなくても良い。もちろん、書きたい場合は書けばよい。

3. タイトルをもつウィンドウを表示するプログラムの説明

次のプログラム MyWinApp1n.java は、上記のMyWinApp1n.javaに対してさらに、タイトルを表示できるようにしたプログラムである。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame {
    public MyFrame() {
       setTitle("My Window Application");
       setSize(300, 200);
       addWindowListener(new MyWindowAdapter());
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
       System.exit(0);
    }
}

public class MyWinApp1n {
    public static void main(String[] args) {
       MyFrame f = new MyFrame();
       f.setVisible(true);
    }
}

実行例は、たとえば次のようである。

% java MyWinApp1n

プログラムの説明

プログラム前半の

setTitle("My Window Application");
では、フレームにタイトルを表示している。

4. 1つのボタンが配置されたウィンドウを表示するプログラムの説明

次のプログラム MyButtonTest.java は、GUIでよく使われるコンポーネントであるボタンが配置されたウィンドウを表示できるようにしたプログラムである。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame {
    Button b1;
    public MyFrame() {
       setTitle("My Button Application");
       setSize(300, 200);
       setLayout(new FlowLayout());
       b1 = new Button("Button1");
       add(b1);
       addWindowListener(new MyWindowAdapter());
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
       System.exit(0);
    }
}

public class MyButtonTest {
    public static void main(String[] args) {
       MyFrame f = new MyFrame();
//       f.show();
       f.setVisible(true);
    }
}
実行例は、たとえば次のようである。
% java MyButtonTest

プログラムの説明

プログラム前半の

setLayout(new FlowLayout());
では、左から右方向にボタンを配置することを示す。
b1 = new Button("Button1");
では、"Button1"というラベルのボタンオブジェクトを生成することを示す。
add(b1);
では、生成されたボタンオブジェクトをフレームオブジェクトに追加することを示す。

5. 複数のボタンが配置されたウィンドウを表示するプログラムの説明

次のプログラム MyButtonTest1.java は、GUIでよく使われるコンポーネントであるボタンが配置されたウィンドウを表示できるようにしたプログラムである。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame {
    Button b1, b2, b3;
    public MyFrame() {
       setTitle("My Button Application");
       setSize(300, 200);
       setLayout(new FlowLayout());
       b1 = new Button("Button1");
       add(b1);
       b2 = new Button("Button2");
       add(b2);
       b3 = new Button("Button3");
       add(b3);
       addWindowListener(new MyWindowAdapter());
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
       System.exit(0);
    }
}

public class MyButtonTest1 {
    public static void main(String[] args) {
       MyFrame f = new MyFrame();
//       f.show();
       f.setVisible(true);
    }
}
実行例は、たとえば次のようである。
% java MyButtonTest

プログラムの説明

プログラム前半の

setLayout(new FlowLayout());
では、左から右方向にボタンを配置することを示す。
b1 = new Button("Button1");
b2 = new Button("Button2");
b3 = new Button("Button3");
では、"Button1"、"Button2"、"Button3"という3つのラベルのボタンオブジェクトを生成することを示す。
add(b1);
add(b2);
add(b3);
では、生成された3つのボタンオブジェクトをフレームオブジェクトに追加することを示す。

6. イベントに対応したボタンを伴うウィンドウを表示するプログラムの説明

AWTを使ったGUIアプリケーションの作成では、イベントに対する処理を、プログラム内で以下のような手順でおこなう必要がある。

  1. イベントリスナーであるコンポーネントは、イベントを受け取る準備としてそのためのインタフェイスを実装する
  2. さらに、イベントリスナーは、ある特定のイベントを受け取った時の処理といして、そのイベントを識別し、それに対応する処理をそのインタフェースの実装の中で記述しておく
  3. イベントソースであるコンポーネントは、自身で発生させたイベントに対するイベントオブジェクトを渡すべきイベントリスナーを登録しておく

次のプログラム MyButtonTest2n.java は、ボタンが押されたことに対してイベント処理を施したプログラムである。

import java.awt.*;
import java.awt.event.*;

class ButtonTestFrame extends Frame implements ActionListener {
    Button b1, b2, b3;

    public ButtonTestFrame() {
        setTitle("My Button Application");
        setSize(300, 200);
        addWindowListener(new MyWindowAdapter());
        setLayout(new FlowLayout());

        b1 = new Button("Button1");
        b2 = new Button("Button2");
        b3 = new Button("Button3");

        b1.addActionListener(this);
        b2.addActionListener(this);
        b3.addActionListener(this);

        add(b1);
        add(b2);
        add(b3);

    }

    public void actionPerformed(ActionEvent ae) {
       if (ae.getSource() == b1) 
          System.out.println("Button1がクリックされました");
       else if (ae.getSource() == b2)
          System.out.println("Button2がクリックされました");
       else if (ae.getSource() == b3)
          System.out.println("Button3がクリックされました");
    }

}


class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
       System.exit(0);
    }
}


public class MyButtonTest2n {
    public static void main(String[] args) {
       ButtonTestFrame f = new ButtonTestFrame();
//       f.show();
       f.setVisible(true);
    }
}
実行例は、たとえば次のようである。
% java MyButtonTest2n

プログラムの説明

このプログラムのイベント処理では、イベントソースであるButtonクラスから発生したイベント を、イベントリスナであるButtonTestFrameクラスとMyWindowAdapterクラスに伝達することで 処理がおこなわれる。

 class ButtonTestFrame extends Frame implements ActionListner 
では、ボタンが押されたというイベントを受け付け、それに対する処理を実行するために、ButtonTestFrameクラスがActionListnerインタフェースを実装していることを表す。
public void actionPerformed(ActionEvt ae){ 
       if (ae.getSource() == b1) 
          System.out.println("Button1がクリックされました");
       else if (ae.getSource() == b2)
          System.out.println("Button2がクリックされました");
       else if (ae.getSource() == b3)
          System.out.println("Button3がクリックされました");
    }
は、ActionListnerインタフェースにおいて実装されねばならないメソッドであり、ボタンが押された際に実行される処理が記述されている。この場合には、ボタンが押された場合には、"Button がクリックされました"という文字列が標準出力に表示されことを示している。actionPerformed()メソッドの引数aeにはボタンがクリックされたイベント情報が入っている。このオブジェクトaeはボタンがクリックされてときに生成される。getSource()メソッドはイベントソースを見つけるために利用する。

       b1.addActionListener(this);
       b2.addActionListener(this);
       b3.addActionListener(this);
では、ボタンに対してイベントリスナーを登録している。これによって、ボタンをクリックされたことにより発生するイベントを感知してactionPerformed()メソッドを呼び出すことができるようになる。

 class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
       System.exit(0);
    }
}
では、リスナーインターフェイスを実装しないで、WindowAdaperを継承したMyWindowAdapterを定義している。これは、複数のリスナーメソッドをもつリスナーインタフェイスでは不要なリスナーメソッドを実装しなくてならないという問題を解決するために提供されているものである。アダプタークラスは、リスナーインタフェイスがあらかじめ空のリスナーメソッドを実装しており、このクラスを継承してイベントリスナーを作成すれば、余計なリスナーメソッドを実装しなくてもよいものである。

その他のコンポーネントの例

テキストフィールド

テキストフィールドは1行のテキストが入力できる画面上の領域である。 テキストフィールドの内容はキーボードを使って編集できる。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame implements ActionListener {
    Button b1;
    TextField t;
    public MyFrame() {
	setSize(300,200);
        addWindowListener(new MyWindowAdapter());
	setLayout(new FlowLayout());
	t = new TextField("Initial Value");
	add(t);
	b1 = new Button("Push");
	add(b1);
	b1.addActionListener(this);
    }
    public void actionPerformed(ActionEvent ae) {
	// TextFieldの現在の値を標準出力に表示する
	System.out.println(t.getText());
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e){
       System.exit(0);
    }
}

public class MyTextFieldTest {
    public static void main(String[] args) {
	MyFrame f = new MyFrame();
	f.setVisible(true);
    }
}

TextFieldオブジェクトにはgetText()メソッドがあり、現在のテキストフィールド に入っている値を得ることができる。

チェックボックス

チェックボックスは"ON"と"OFF"の2つの状態を持つ一種のボタンである。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame implements ActionListener {
    Button b1;
    Checkbox c1, c2, c3;
    public MyFrame() {
	setSize(300,200);
        addWindowListener(new MyWindowAdapter());
	setLayout(new FlowLayout());
	c1 = new Checkbox("1");
	add(c1);
	c2 = new Checkbox("2");
	add(c2);
	c3 = new Checkbox("3");
	add(c3);
	b1 = new Button("Push");
	add(b1);
	b1.addActionListener(this);
    }
    public void actionPerformed(ActionEvent ae) {
	// Checkboxの現在の値を標準出力に表示する
	System.out.println("checkbox1 is " + c1.getState());
	System.out.println("checkbox2 is " + c2.getState());
	System.out.println("checkbox3 is " + c3.getState());
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e){
       System.exit(0);
    }
}

public class MyCheckboxTest {
    public static void main(String[] args) {
	MyFrame f = new MyFrame();
	f.setVisible(true);
    }
}

CheckboxオブジェクトにはgetState()メソッドがあり、現在のチェックボックスの 状態(true=選択、false=未選択)を返す。

ラジオボタン

ラジオボタンはいくつかの選択肢の中から1つだけを選べるようなボタン の一種である。Javaでは CheckboxクラスとCheckboxGroupクラスを使ってラジオボタンを作る。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame implements ActionListener {
    Button b1;
    CheckboxGroup g;
    Checkbox c1, c2, c3;
    public MyFrame() {
	setSize(300,200);
        addWindowListener(new MyWindowAdapter());
	setLayout(new FlowLayout());

	g = new CheckboxGroup();
	c1 = new Checkbox("1", false, g);
	add(c1);
	c2 = new Checkbox("2", false, g);
	add(c2);
	c3 = new Checkbox("3", false, g);
	add(c3);
	b1 = new Button("Push");
	add(b1);
	b1.addActionListener(this);
    }
    public void actionPerformed(ActionEvent ae) {
	// Checkboxの現在の値を標準出力に表示する
	System.out.println("checkbox1 is " + c1.getState());
	System.out.println("checkbox2 is " + c2.getState());
	System.out.println("checkbox3 is " + c3.getState());

	Checkbox c = g.getSelectedCheckbox();
	if (c != null) {
	    System.out.println(c.getLabel() + " is selected.");
	} else {
	    System.out.println("No one is selected.");
	}
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
       System.exit(0);
    }
}

public class MyRadioButtonTest {
    public static void main(String[] args) {
	MyFrame f = new MyFrame();
	f.setVisible(true);
    }
}

先にCheckboxGroupのオブジェクトを作っておき、 c1 = new Checkbox("1", false, g)と いう風にCheckboxオブジェクトを生成する時に指定する。この例の "1"はラジオボタンの名前、falseは初期状態(未選択、trueなら選択)、 gがCheckboxGroupオブジェクトである。 現在選択されているラジオボタン(Checkboxオブジェクト)は CheckboxGroupオブジェクトのgetSelectedCheckbox()メソッドで得ることができる。 これで得たCheckboxオブジェクトの名前は getLabel()メソッドで得ることができる。

テキストエリア

テキストエリアは複数の行からなるテキスト領域である。

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame implements ActionListener {
    Button b1;
    TextArea t;
    public MyFrame() {
	setSize(200,200);
        addWindowListener(new MyWindowAdapter());
	setLayout(new FlowLayout());
	t = new TextArea("Initial Value", 6, 20);
	add(t);
	b1 = new Button("Push");
	add(b1);
	b1.addActionListener(this);
    }
    public void actionPerformed(ActionEvent ae) {
	// TextAreaの現在の値を標準出力に表示する
	System.out.println(t.getText());
    }
}

class MyWindowAdapter extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
       System.exit(0);
    }
}

public class MyTextAreaTest {
    public static void main(String[] args) {
	MyFrame f = new MyFrame();
	f.setVisible(true);
    }
}

上記の例では、 t = new TextArea("Initial Value", 6, 20); として、6行,20列のテキストエリアを生成している。"Initial Value" は初期のテキストエリアの内容である。

課題 4-1

上のプログラム MyWinApp.java を書いて実行してみよ。 実際にウィンドウを終了してみよ。

課題 4-2

上のプログラム MyWinApp1.java を書いて実行してみよ。 さらに、ウィンドウサイズを480 x 300に変えて実行してみよ。

課題 4-3

上のプログラム MyWinApp1n.java を書いて実行してみよ。 さらに、ウィンドウタイトルを"New Window Application"に変えて実行してみよ。

課題4-4

Component, Window, Frame, Button, TextArea クラスの継承関係をAPI仕様などを使って調べて書け。

課題 4-5

上のプログラム MyButtonTest.java を書いて実行してみよ。 さらに、GUI部品であるボタンの名前を「ボタン」に変えて実行してみよ。

課題 4-6

上のプログラム MyButtonTest1.java を書いて実行してみよ。 さらに、GUI部品である4つのボタンを2X2の格子上に配置して表示してみよ。 この場合、setLayout(new FlowLayout());の代わりにsetLayout(new GridLayout(2,2));を使うこと。

課題 4-7

上のプログラム MyButtonTest2n.java を書いて実行してみよ。 さらに、標準出力に表示される文字列を英語に直して表示せよ。

課題 4-8

上のプログラム MyButtonTest.java をボタンを押すと、ボタンに書かれた文字が"Pressed"に変わるように プログラムを修正せよ。ボタンに書かれた文字を変更するメソッドは、 API仕様などで調べよ。

課題 4-9

上のプログラム MyButtonTest.java でボタンに書かれた文字が、ボタンを押すたびに"Black","White"と交互に 変わるようにプログラムを修正せよ。現在ボタンに書かれた文字を得る メソッドが存在する。API仕様などで調べよ。

課題 4-10

上のプログラム MyTextFieldTest.java にもう一つTextFieldを追加せよ。さらにボタンを押した場合に、 2つのTextFieldの内容が同一なら"Equal"、違うなら "Not Equal"と 標準出力に表示するようにせよ。

課題 4-11

上のプログラム MyCheckboxTest.java でそれぞれのチェックボックスの名前を"RED","GREEN","BLUE"とせよ。 さらにボタンを押した場合に、 3つのチェックボックスが全て選択された場合に、"White color"と 標準出力に表示するようにせよ。

課題 4-12

上のプログラム MyTextAreaTest.java にもう一つTextAreaを追加せよ。さらにボタンを押した場合に、 2つのTextAreaの内容を標準出力に表示するようにせよ。

ソフトウエア基礎演習


nagai@rsch.tuis.ac.jp (Based upon mizutani@rsch.tuis.ac.jp's style sheet)
ohmi@rsch.tuis.ac.jp (加筆修正: 2004年4月27日〜2004年5月11日)