第23週目演習

下準備

テストドライバの導入

第23回目 (秋学期10回目) 分のテストドライバを導入する。以下の手順で行う。

  1. ダウンロード のページを開く (ここをクリック)
  2. プロジェクト「java2007」にある「test」の左側の「+」をクリック
  3. ツリーが展開されるので「install-libraries.xml」を右クリック
  4. 「実行(R)」にマウスカーソルを合わせる
  5. 「1 Ant ビルド」をクリック
  6. 「コンソール」タブに"BUILD SUCCESSFUL"と表示されれば成功
  7. eclipseの画面でプロジェクト「java2007」を右クリック
  8. メニューが表示されるので、「更新」をクリック
  9. "week23.zip" をデスクトップなどにダウンロード
  10. eclipseの画面でプロジェクト「java2007」を右クリック
  11. メニューから「インポート(I)」を選択
  12. 「インポート」ウィンドウが表示されるので、「Zip ファイル」を選択
  13. 「次へ(N)」をクリック
  14. 宛先フォルダー(L): が「java2007」になっていることを確認
  15. From zip file: の右側にある 「参照(R)...」をクリック
  16. ファイルダイアログが表示されるので、ダイアログ内に表示されたダウンロードしたファイルをダブルクリック
  17. 前の画面に戻るので、From zip file: のエリアに正しいパスが入力されていることを確認
  18. フォルダ「/」の左にチェックがついていることを確認 (ついていなければチェックボックスをクリック)
  19. 「警告を出さずに既存リソースを上書き」にチェックがついていることを確認 (上書きしたくないファイルがある場合はチェックを外す)
  20. 「終了 (F)」をクリック

第23週目テストドライバの導入に成功すると、java2007 プロジェクトの test フォルダにj2.lesson10.xml というファイルが作成される。

GUI のテスト技法はあまりよいものが確立されていない。今回のテストドライバは試験的に作成したものなので、自動テストに頼らずに各自でテストを行うこと

パッケージの作成

過去の演習を参考にして、「j2.lesson10」というパッケージを作成する。

コンピュータ内の画像を表示するプログラム

今回の演習では、コンピュータ内に保存されている画像を表示するプログラムを作成する。

このプログラムは、実行すると次のようなウィンドウを表示する。


ウィンドウの左下にあるテキストフィールドに画像が保存されているファイルのパスを入力し、右下のボタンをクリックすると、画面中央部に画像が表示される。


GUI を持つプログラムは、今までのように擬似コードを書いてそれに対するコードを書く、というスタイルだけではプログラムを書ききれない。2つのステップに分割してプログラムを作成する。

  1. 画面 (ウィンドウ表示) の設計
  2. アクション (ロジック) の設計

一言で言うと、プログラムは「入力されたデータを加工して出力する」というだけの機能を持つ。一般的な入力はキーボードやマウス、ファイルなどが考えられ、出力はディスプレイやファイル、プリンタなどが考えられる。

これまでの演習や課題で作成したプログラムでは、入出力はすべてコンソールやファイルであった。このようなプログラムは、ユーザが入力を行うタイミングを全てプログラム側で決定できる。

今回からは GUI を使用するため、ユーザは好きなタイミングで、好きな入力を行うことができる (テキストフィールドへの入力やボタンの押下など)。これらを全て考慮したプログラムを書くことは困難であるため、問題を分割してプログラムを作成することにする。

画面設計

まずは、GUI 部分を表示するクラス ImageViewer を作成する。このクラスはいくつかのコンポーネントを持つ、下記のようなウィンドウを表示するだけのクラスである。


レイアウトの決定

これまでは最初に行うべきステップが「擬似コードの作成」であったが、デザイン部分を設計するにはそれ以外に「レイアウトの決定」というステップを踏む必要がある。

本来ならば、どのようなレイアウトを持つウィンドウを表示するかといった部分から始める必要があるが、今回は作成する GUI が決定されているため、どのような方法でウィンドウにコンポーネントを配置するかということだけを決めればよい。


紙に上記のようなレイアウトを書いて、コンポーネントを配置する方法を決めればよい。また、ここで書いた紙は後ほどアクションを書く部分でも使用するため、捨てずにとっておくとよい。

Java でコンポーネントを配置する方法の一つとして、LayoutManager を使用する方法を紹介した。今回は、LayoutManager を駆使して上記ウィンドウのようにクライアントエリアにコンポーネントを配置する。


上記のようなレイアウトの設計図に従い、擬似コードを書く。

擬似コードの作成

擬似コードとは、本来 Java などの特定の言語に依存しないでプログラムの流れを書くためのものであるが、GUI のレイアウトをプログラムする部分ではどうしても Java に依存したコードになってしまう。

それでも擬似コードを書かないでプログラムを直接書くよりは分かりやすいので、簡単に擬似コードを書くことにする。


上記のようなレイアウトの設計図を見ながら、このレイアウトを実現するために必要な擬似コードを書く。

プログラム全体
    window = "image viewer" というタイトルのウィンドウを作成
    label = "ここにイメージを表示" と書かれたラベルを作成
    input = "ファイル名を入力" と書かれたテキストフィールドを作成
    button = "表示" と書かれたボタンを作成
    window に label, input, button を貼り付ける
    # panel = BorderLayout のコンテナを作成
    # panel に input を追加 (CENTER)
    # panel に button を追加 (EAST)
    # label を window に貼り付け (CENTER)
    # (input, button を貼り付けた) panel をwindow に貼り付け (SOUTH)
    window のサイズを 400 x 400 に設定
    window を表示

骨格の作成

クラスの作成

以下の手順で、パッケージ「j2.lesson10」に「ImageViewer」クラスを作成する。

  1. 先ほど作成したパッケージ 「j2.lesson10」の上で右クリック
  2. マウスカーソルを「新規」に合わせる
  3. 「クラス」をクリック
  4. クラス名は ImageViewer とする

擬似コードの貼り付け (骨格のみ)

作成したクラスに、各擬似コードの名前を貼り付ける。

このプログラムでは GUI を使用するための API をいくつか使用しているため、次の 2 つの import 文を書いておく。

package j2.lesson10;

import java.awt.*;
import javax.swing.*;

public class ImageViewer {
    // プログラム全体
}

mainメソッドの作成 (骨格のみ)

擬似コード「プログラム全体」に合わせて、クラス「ImageViewer」内にpublic static void main(String[] args) から始まるメソッドを作成する。

全体の骨格

ここまでのプログラムの骨格は以下のようになる。

package j2.lesson10;

import java.awt.*;
import javax.swing.*;

public class ImageViewer {
    // プログラム全体
    public static void main(String[] args) {
    }
}

骨格テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「ImageViewerに対する骨格テスト」を実行する。

骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。

メッセージ 詳細
(クラス名), existence j2.lesson10 に対象のクラスが存在していない。パッケージやクラス名を確認
(メソッド名), existence 指定されたメソッドが存在しない
(メソッド名), public メソッドを作る際に public が抜けている
(メソッド名), public メソッドを作る際に private が抜けている
(メソッド名), static メソッドを作る際に static が抜けている
(メソッド名), type <T> メソッドを作る際に戻り値の型を間違えている (正しくは <T>)

プログラムへの変換

先ほど作成した ImageViewer クラスの main メソッドの中身を記述する。

まずは擬似コードをコメントとして貼り付ける。

// プログラム全体
public static void main(String[] args) {
    // window = "image viewer" というタイトルのウィンドウを作成
    // label = "ここにイメージを表示" と書かれたラベルを作成
    // input = "ファイル名を入力" と書かれたテキストフィールドを作成
    // button = "表示" と書かれたボタンを作成
    // window に label, input, button を貼り付ける
    // # panel = BorderLayout のコンテナを作成
    // # panel に input を追加 (CENTER)
    // # panel に button を追加 (EAST)
    // # label を window に貼り付け (CENTER)
    // # window そのものではなく、クライアントエリアに配置する
    // # (input, button を貼り付けた) panel をwindow に貼り付け (SOUTH)
    // window のサイズを 400 x 400 に設定
    // window を表示
}

ウィンドウを作成する

擬似コードの最初の部分では、ウィンドウを作成している。

window = "image viewer" というタイトルのウィンドウを作成

ウィンドウを作成するには、javax.swing.JFrame を使用する。また、ウィンドウにタイトルを設定するには、JFrame のコンストラクタを呼び出す際に、引数としてタイトルの文字列を指定すればよい。

ウィンドウを作成する際、必ずやらなければならないことは終了ボタンが押された際の動作を決めるということである。これは「JFrame.setDefaultCloseOperation」メソッドの引数に、終了時の動作を表す定数を指定すればよい (ここでは DISPOSE_ON_CLOSE)。

下記のようなプログラムに変換できる。

// window = "image viewer" というタイトルのウィンドウを作成
JFrame window = new JFrame("image viewer");
window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

コンポーネントを作成する

擬似コードの次の部分では、必要なコンポーネントを作成している。

label = "ここにイメージを表示" と書かれたラベルを作成
input = "ファイル名を入力" と書かれたテキストフィールドを作成
button = "表示" と書かれたボタンを作成

ラベル、テキストフィールド、ボタンはそれぞれ javax.swing.JLabeljavax.swing.JTextFieldjavax.swing.JButton を使う。

また、JLabel はそのまま使うとテキストが左詰めで表示されてしまう。レイアウトではラベルに表示する文字列は中央に配置されていたため、JLabel.setHorizontalAlignmnet メソッドに JLabel.CENTER という定数を渡すことによって、文字を中央に配置するように設定する。


下記のようなプログラムに変換できる。

// label = "ここにイメージを表示" と書かれたラベルを作成
JLabel label = new JLabel("ここにイメージを表示");
label.setHorizontalAlignment(JLabel.CENTER);

// input = "ファイル名を入力" と書かれたテキストフィールドを作成
JTextField input = new JTextField("ファイル名を入力");

// button = "表示" と書かれたボタンを作成
JButton button = new JButton("表示");

ボタンにアクションを設定するという作業は後ほど行うことにする。

クライアントエリアにコンポーネントを貼り付ける

擬似コードの次の部分では、ウィンドウのクライアントエリアに各コンポーネントを配置している。

window に label, input, button を貼り付ける
# panel = BorderLayout のコンテナを作成
# panel に input を追加 (CENTER)
# panel に button を追加 (EAST)
# label を window に貼り付け (CENTER)
# (input, button を貼り付けた) panel をwindow に貼り付け (SOUTH)

下記のようなレイアウトを行うため、BorderLayout をそのまま使うだけでは実現できない。そのため、クライアント下部のエリアに別のコンテナ (javax.swing.JPanel) を置いて、そのコンテナ内でさらに領域を分割してコンポーネントを配置する必要がある。


そして、レイアウトマネージャには java.awt.BorderLayout を使用する。

次のようなプログラムに変換できる。

// window に label, input, button を貼り付ける
// # panel = BorderLayout のコンテナを作成
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
// # panel に input を追加 (CENTER)
panel.add(input, BorderLayout.CENTER);
// # panel に button を追加 (EAST)
panel.add(button, BorderLayout.EAST);

// # label を window に貼り付け (CENTER)
// # window そのものではなく、クライアントエリアに配置する
window.getContentPane().add(label, BorderLayout.CENTER);
// # (input, button を貼り付けた) panel をwindow に貼り付け (SOUTH)
window.getContentPane().add(panel, BorderLayout.SOUTH);

クライアントエリアのレイアウトマネージャを設定していないが、JFrame の content pane はもともと BorderLayout が設定されている。このような初期設定を覚えるのが面倒であれば、わざわざ BorderLayout を指定するのもよい。

ウィンドウを表示する

最後に、ウィンドウを表示する。

window のサイズを 400 x 400 に設定
window を表示

これはそのまま、setSize メソッドと setVisible メソッドによって表示の設定を行えばよい。

// window のサイズを 400 x 400 に設定
window.setSize(400, 400);
// window を表示
window.setVisible(true);

プログラムの実行

プログラムを実行すると、下記のようなウィンドウが表示される。


このウィンドウは表示だけを行っていて特に機能を持たない。演習の後半でこのウィンドウに機能を持たせていく。

画面設計の実際

画面表示部分を実装する場合、LayoutManager を駆使して設計することは通常あまりせず、画面設計を行うためのツールを使用して作成することが多い。

本演習では特定のツールに依存した内容を紹介することはしないため、興味があればそのようなツールを探して使用してみるのもよい (Eclipse のプラグインとしてもそのようなツールが公開されている)。

どちらにしろ、一度紙にレイアウトを描いて設計することはよい習慣である。

プログラム全体

ImageViewerクラス全体を掲載しておく。

package j2.lesson10;

import java.awt.*;
import javax.swing.*;

public class ImageViewer {

    // プログラム全体
    public static void main(String[] args) {
        // window = "image viewer" というタイトルのウィンドウを作成
        JFrame window = new JFrame("image viewer");
        window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        // label = "ここにイメージを表示" と書かれたラベルを作成
        JLabel label = new JLabel("ここにイメージを表示");
        label.setHorizontalAlignment(JLabel.CENTER);
        
        // input = "ファイル名を入力" と書かれたテキストフィールドを作成
        JTextField input = new JTextField("ファイル名を入力");
        
        // button = "表示" と書かれたボタンを作成
        JButton button = new JButton("表示");
        
        // window に label, input, button を貼り付ける
        // # panel = BorderLayout のコンテナを作成
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        // # panel に input を追加 (CENTER)
        panel.add(input, BorderLayout.CENTER);
        // # panel に button を追加 (EAST)
        panel.add(button, BorderLayout.EAST);
        
        // # label を window に貼り付け (CENTER)
        // # window そのものではなく、クライアントエリアに配置する
        window.getContentPane().add(label, BorderLayout.CENTER);
        // # (input, button を貼り付けた) panel をwindow に貼り付け (SOUTH)
        window.getContentPane().add(panel, BorderLayout.SOUTH);
        
        // window のサイズを 400 x 400 に設定
        window.setSize(400, 400);
        // window を表示
        window.setVisible(true);
    }
}

アクションの設計

画面設計の部分が終わったので、次はこの画面にアクションを追加する。

このプログラムのアクションを表すクラスとして ImageViewerAction を作成する。

アクションの確認

このプログラムでは、「ボタンが押された際にテキストフィールドに書かれている画像ファイルを読み込み、ラベルに表示する」といった処理を行う。

このアクションを実現するため、先ほど紙に書いたレイアウトのうち、貼り付けられているコンポーネントに識別のための記号 (a, b, c など) を割り当てる。


そして、「ボタンが押された際にテキストフィールドに書かれている画像ファイルを読み込み、ラベルに表示する」という表現を、先ほど割り当てた記号を使って正確に言葉で表現する

親クラスの抽出

アクションを記述する際には、javax.swing.AbstractAction を継承すると便利であるため、これを利用する。

フィールドの抽出

アクションを記述するクラスに必要なインスタンスフィールドを抽出する。

  1. テキストフィールド (b) に書かれた文字列を取得
  2. 1 で取得した文字列をファイルパスとみなし、そこから画像を読み込む
  3. 2 で読み込んだ画像をラベル (a) に貼り付ける

このアクションで、使用するデータ (コンポーネント) は (a) と (b)、つまりラベルとテキストフィールドである。それ以外のデータは、(a) (b) を使えば全て用意できる。そこで、これら二つをインスタンスフィールドとして作成する。

コンストラクタの抽出

コンストラクタを作成する際、javax.swing.AbstractAction を継承することを考慮しなければならない。親クラスの AbstractAction は String を一つだけとるコンストラクタ AbstractAction(String) が存在するため、それを使用することにする。

また、インスタンスフィールドを初期化するため、JLabel と JTextField をコンストラクタの引数で受け取ることにする。

上記を踏まえて、次のようなコンストラクタを用意する。

上記のように引数を並べたのは、引数の前半に「入力関係のデータ」、引数の後半に「出力関係のデータ」を並べるというルールがよく用いられるからである。自分で決めたスタイルを用いてもよいが、今回の演習では上記のような引数の順序をとること。

メソッドの抽出

javax.swing.AbstractAction を継承したクラスでアクションを記述するには、抽象メソッドとして定義された以下のメソッドをオーバーライドする必要がある。

アクションを記述するには上記のメソッドだけ公開すればよいので、必要な公開メソッドは上記のものだけである。

擬似コードの作成

「ボタンが押された際の処理」の擬似コード

「ボタンが押された際の処理」は先ほど紙に書いた以下の内容と同様である。

  1. テキストフィールド (b) に書かれた文字列を取得
  2. 1 で取得した文字列をファイルパスとみなし、そこから画像を読み込み
  3. 2 で読み込んだ画像をラベル (a) に貼り付ける

もう少しプログラムに近い形に書き直す。

ボタンが押された際の処理
    imagename = テキストフィールドに入力された内容
    image = imagename を画像として読み出したもの
    ラベルにイメージを貼り付ける

骨格の作成

クラスの作成

以下の手順で、パッケージ「j2.lesson10」に「ImageViewerAction」クラスを作成する。

  1. 先ほど作成したパッケージ 「j2.lesson10」の上で右クリック
  2. マウスカーソルを「新規」に合わせる
  3. 「クラス」をクリック
  4. クラス名は ImageViewerAction とする。

擬似コードの貼り付け

作成したクラスに、各擬似コードの名前を貼り付ける。

このプログラムでは GUI を使用するための API をいくつか使用しているため、次の 2 つの import 文を書いておく。

package j2.lesson10;

import java.awt.event.*;
import javax.swing.*;

public class ImageViewerAction {
    // ボタンが押された際の処理
}

親クラスの設定

「親クラスの抽出」の部分にもあったように、親クラスとして AbstractAction を指定する。

package j2.lesson10;

import java.awt.event.*;
import javax.swing.*;

public class ImageViewerAction extends AbstractAction {
    // ボタンが押された際の処理
}

インスタンスフィールドの作成

「フィールドの抽出」の部分で決めた内容に従って、インスタンスフィールドをクラス内に作成する。

package j2.lesson10;

import java.awt.event.*;
import javax.swing.*;

public class ImageViewerAction extends AbstractAction {
    
    private final JTextField input;
    private final JLabel label;

    // ボタンが押された際の処理
}

コンストラクタの作成

「コンストラクタの抽出」の部分で決めた内容に従って、コンストラクタをクラス内に作成する。

親クラス AbstractAction のコンストラクタに引数 name を渡し、それ以外の引数は同名のインスタンスフィールドに代入しておく。

package j2.lesson10;

import java.awt.event.*;
import javax.swing.*;

public class ImageViewerAction extends AbstractAction {
    
    private final JTextField input;
    private final JLabel label;

    public ImageViewerAction(String name, JTextField input, JLabel label) {
        super(name);
        this.input = input;
        this.label = label;
    }

    // ボタンが押された際の処理
}

メソッドの作成

「メソッドの抽出」の部分で決めた内容に従って、メソッドをクラス内に作成する。

package j2.lesson10;

import java.awt.event.*;
import javax.swing.*;

public class ImageViewerAction extends AbstractAction {
    
    private final JTextField input;
    private final JLabel label;

    public ImageViewerAction(String name, JTextField input, JLabel label) {
        super(name);
        this.input = input;
        this.label = label;
    }

    // ボタンが押された際の処理
    public void actionPerformed(ActionEvent e) {
    }
}

全体の骨格

ここまでのプログラムの骨格は以下のようになる。

package j2.lesson10;

import java.awt.event.*;
import javax.swing.*;

public class ImageViewerAction extends AbstractAction {
    
    private final JTextField input;
    private final JLabel label;

    public ImageViewerAction(String name, JTextField input, JLabel label) {
        super(name);
        this.input = input;
        this.label = label;
    }

    // ボタンが押された際の処理
    public void actionPerformed(ActionEvent e) {
    }
}

骨格テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「ImageViewerActionに対する骨格テスト」を実行する。

骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。

メッセージ 詳細
(クラス名), existence j2.lesson10 に対象のクラスが存在していない。パッケージやクラス名を確認
(メソッド名), existence 指定されたメソッドが存在しない
(メソッド名), public メソッドを作る際に public が抜けている
(メソッド名), not static メソッドを作る際に static を余計につけている
(メソッド名), type <T> メソッドを作る際に戻り値の型を間違えている (正しくは <T>)

プログラムへの変換

先ほど作成した ImageViewerAction クラスの actionPerformed メソッドの中身を記述する。

まずは擬似コードをコメントとして貼り付ける。

// ボタンが押された際の処理
public void actionPerformed(ActionEvent e) {
    // imagename = テキストフィールドに入力された内容
    // image = imagename を画像として読み出したもの
    // ラベルにイメージを貼り付ける
}

テキストフィールドから文字列を取得する

擬似コードの最初の行では、テキストフィールドの内容を取得している。

imagename = テキストフィールドに入力された内容

これは、JTextField.getText() メソッドを呼び出すことにより実現できる。

// imagename = テキストフィールドに入力された内容
String imagename = this.input.getText();

画像を読み込む

擬似コードの次の行では、取得した文字列を元に画像を読み込んでいる。

image = imagename を画像として読み出したもの

Swing のコンポーネントで画像を扱うには、javax.swing.ImageIcon を使用すると便利である。このクラスは、コンストラクタに画像ファイルの名前を指定するだけで画像を読み込んでくれる。

// image = imagename を画像として読み出したもの
ImageIcon image = new ImageIcon(imagename);

ラベルに画像を貼り付ける

擬似コードの最後の行では、読み込んだ画像をラベルに貼り付けている。

ラベルにイメージを貼り付ける

ラベルに画像を貼り付けるには、JLabel.setIcon(javax.swing.Icon) というメソッドを使う。javax.swing.Icon はインターフェースで、先ほど使用した javax.swing.ImageIcon がその実装クラスの一つである。そのため、setIcon メソッドには先ほど作成した image を引数に渡せばよい。

// ラベルにイメージを貼り付ける
this.label.setIcon(image);
this.label.setText("");

ラベルには、イメージとテキストの両方を同時に表示できる。今回は、画像を貼り付けたらテキストを消去したいため、「this.label.setText("")」によって設定されているテキストを消している。

ImageViewer クラスの変更

アクションを作成したら、先ほど作成した画面に統合する必要がある。

このアクションのトリガ (発生条件) は、次のようなものであった。

ImageViewer クラス内の、該当するボタンにこのアクションを設定する。

具体的には、変更すべき箇所は下記の部分である。

// button = "表示" と書かれたボタンを作成
JButton button = new JButton("表示");

このボタンを作成する際に、表示する文字列の代わりに先ほど作成した ImageViewerAction クラスのインスタンスを渡す。

// button = "表示" と書かれたボタンを作成
// # ImageViewerAction を設定する
JButton button = new JButton(new ImageViewerAction("表示", input, label));

プログラムの実行

プログラムを実行する前に、表示する画像データをコンピュータ内に保存しておく必要がある。

下記の 2 つの画像データをコンピュータ内の好きな位置に保存すること。

それぞれ、U:\pr2302sample1.jpg, U:\pr2302sample2.jpg に保存したとして話を進める。

画像データを用意したらプログラムを実行する。今回作成したアクションは ImageViewer クラスに組み込んだため、ImageViewer クラスを起動する

すると、下記のようなウィンドウが表示される。


まず、ウィンドウ左下のテキストフィールドに U:/pr2302sample1.jpg と入力し (\ の部分は / で入力する)、ウィンドウ右下のボタンをクリックする。すると、次のように画像が表示されるはずである。


また、ウィンドウ左下のテキストフィールドに U:/pr2302sample2.jpg と入力し、ウィンドウ右下のボタンをクリックすると、次のように画像が表示されるはずである。


ボタンを押しても何も変化が起こらない場合は、アクションの設計を間違っている (actionPerformed(ActionEvent) をオーバーライドしていない) か、ImageViewer クラスを正しく変更できていない。

ボタンを押すとラベルの部分の文字列が消え、画像が何も表示されない場合は、指定した画像ファイルが読み込めなかった場合である。

機能テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「ImageViewerActionに対する機能テスト」を実行する。

すると、ImageViewer クラスが起動されて、ウィンドウが表示される。しばらく待つとウィンドウが自動で操作され、いくつかのテストケースが実行される (自動で操作されている間はキーボードやマウスを操作しないこと)。

テストケース実行後は次のような画面が出るので、「実際の表示」タブの画像と「期待された表示」タブの画像を比較し、同じならば「OK」ボタンを押す。


メッセージ 詳細
フレーム <F> が見つかりません <F> の名前を持つフレームが見つからない。JFrame のコンストラクタに指定している文字列を確認
フレーム <F> は javax.swing.JFrame のインスタンスではありません <F> の名前を持つフレームが JFrame のインスタンスでない。java.awt.Frame を誤って使用していないか確認
<機能テストの項目名> <機能テストの項目名> で、FAIL ボタンを押した

機能テストの項目

テストの概要 意味
起動直後の状態 プログラムを実行し、まだ何もしていない状態の画像
.temp/pr2302sample1.jpgを... .temp/pr2302sample1.jpg を作成後、テキストフィールドにそのファイル名を指定してボタンをクリック
.temp/pr2302sample2.jpgを... .temp/pr2302sample2.jpg を作成後、テキストフィールドにそのファイル名を指定してボタンをクリック

プログラム全体

ImageViewerAction 全体を掲載しておく。

package j2.lesson10;

import java.awt.event.*;
import javax.swing.*;

public class ImageViewerAction extends AbstractAction {
    
    private final JTextField input;
    private final JLabel label;

    // コンストラクタ
    //  name - アクションの名前
    //  input - ファイル名を入力するテキストフィールド
    //  label - 画像を表示するラベル
    public ImageViewerAction(String name, JTextField input, JLabel label) {
        super(name);
        this.input = input;
        this.label = label;
    }

    // ボタンが押された際の処理
    public void actionPerformed(ActionEvent e) {
        // imagename = テキストフィールドに入力された内容
        String imagename = this.input.getText();
        // image = imagename を画像として読み出したもの
        ImageIcon image = new ImageIcon(imagename);
        // ラベルにイメージを貼り付ける
        this.label.setIcon(image);
        this.label.setText("");
    }
}

また、変更後の ImageViewer も掲載する。

package j2.lesson10;

import java.awt.*;
import javax.swing.*;

public class ImageViewer {

    // プログラム全体
    public static void main(String[] args) {
        // window = "image viewer" というタイトルのウィンドウを作成
        JFrame window = new JFrame("image viewer");
        window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        // label = "ここにイメージを表示" と書かれたラベルを作成
        JLabel label = new JLabel("ここにイメージを表示");
        label.setHorizontalAlignment(JLabel.CENTER);
        
        // input = "ファイル名を入力" と書かれたテキストフィールドを作成
        JTextField input = new JTextField("ファイル名を入力");
        
        // button = "表示" と書かれたボタンを作成
        // # ImageViewerAction を設定する
        JButton button = new JButton(new ImageViewerAction("表示", input, label));

        // window に label, input, button を貼り付ける
        // # panel = BorderLayout のコンテナを作成
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        // # panel に input を追加 (CENTER)
        panel.add(input, BorderLayout.CENTER);
        // # panel に button を追加 (EAST)
        panel.add(button, BorderLayout.EAST);
        
        // # label を window に貼り付け (CENTER)
        // # window そのものではなく、クライアントエリアに配置する
        window.getContentPane().add(label, BorderLayout.CENTER);
        // # (input, button を貼り付けた) panel をwindow に貼り付け (SOUTH)
        window.getContentPane().add(panel, BorderLayout.SOUTH);
        
        // window のサイズを 400 x 400 に設定
        window.setSize(400, 400);
        // window を表示
        window.setVisible(true);
    }
}