イベント処理

イベント委譲モデル

ボタンにアクションを記述する際、前回の講義では次のように javax.swing.AbstractAction を継承したクラスを使って行っていた。

package j2.lesson10.example;

import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JLabel;
import javax.swing.JTextField;

public class TextFieldAction extends AbstractAction {

    private final JTextField textField;
    private final JLabel label;
    
    public TextFieldAction(String name, JTextField field, JLabel label) {
        super(name);
        this.textField = field;
        this.label = label;
    }

    // ボタンが押されたらテキストフィールドの内容をラベルに貼り付ける
    public void actionPerformed(ActionEvent e) {
        // テキストフィールドからラベルへ
        String text = this.textField.getText();
        this.label.setText(text);
        
        // テキストフィールドの内容を変更する
        this.textField.setText("clicked");
    }
}

AbstractAction は様々な機能を持っていて、単純なアクションの記述には向いていない。例えば、AbstractAction を使用した場合には次のようなことができる。

このうち、必要な機能は最後の「ボタンなどが押された際に実行する処理を記述できる」という部分だけである。

AbstractAction を継承したクラスを使うことによって、こちらが意識することなくボタンが押された際の処理を記述できたが、AbstractAction を用いない場合には「イベントリスナ (event listener) の登録」という方法でイベント処理を行う必要がある (AbstractAction を使った場合には、イベントリスナを自動的に登録してくれている)。

イベントとは、「ボタンがクリックされた」などのユーザがコンポーネントに対して何らかの動作を行った際に発生するもので、イベントリスナはこのイベントを受信 (listen) するためのインターフェースである。イベントリスナには通常いくつかのメソッドが宣言されていて、イベント受信時に自動的にこれらのメソッドが呼び出される。

イベント受信時に自動的に呼び出されるメソッドを イベントハンドラ と呼ぶ。イベントハンドラ内に適切な処理を行うプログラムを書くことによって、イベントに対する処理を記述することができる。


上図のように、ボタンが押された際にイベントが発生して、そのイベントの処理を他のプログラムに委譲 (他人に任せて譲ること) することによって、ボタンにアクションを付加している。

このような処理の方法をイベント委譲モデル (delegation event model)と呼び、Java の AWT や Swing でコンポーネントにアクションを記述する際にはこの方法がとられる。

イベント委譲モデルの利点は、GUI コンポーネントとそのイベント処理を別々に作成できるという点である。この利点のおかげで、「イベント処理を行うボタンを一から作る」のではなく「もともと用意されたボタンにイベント処理機能を付加する」だけでクリックされた際にアクションを行うボタンを作成することができる。

ActionListener

最もよく使用されるイベントリスナに java.awt.event.ActionListener インターフェースという「ボタンが押された場合」や「テキストフィールドでEnterキーが押された場合」などに発生するイベントを受け取ることができるイベントリスナがある。

イベント処理を行うには、まずこの ActionListener インターフェースを実装したクラスを作成する。

package j2.lesson11.example;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JLabel;
import javax.swing.JTextField;

public class TextCopyAction implements ActionListener {

    private JTextField input;
    private JLabel label;

    // 入力用のテキストフィールドと表示用のラベル
    public TextCopyAction(JTextField input, JLabel label) {
        this.input = input;
        this.label = label;
    }

    // イベントハンドラ (イベント発生時に自動的に呼び出される)
    public void actionPerformed(ActionEvent e) {
        // テキストフィールドの内容をラベルにコピー
        String text = this.input.getText();
        this.label.setText(text);
    }
}

ActionListener インターフェースは actionPerformed(java.awt.event.ActionEvent) というイベントハンドラ用のメソッドを持つ。イベントが発生された際にこの actionPerformed メソッドが呼び出されるため、このメソッド内にイベントを処理するプログラムを書けばよい。また、actionPerformed の引数である java.awt.event.ActionEvent は、イベント発生時の様々な情報が格納されているため、必要ならばそこからいくつかの情報を取得することができる。

ActionListener を実装したクラスを作成しただけでは、イベント処理を行うことはできない。作成したイベントリスナのインスタンスをコンポーネントに登録する必要がある

package j2.lesson11.example;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

public class EventSample1 {

    public static void main(String[] args) {
        JFrame frame = new JFrame("イベントのサンプル");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        // コンポーネントを作成
        JLabel label = new JLabel("ラベル");
        JTextField textField = new JTextField("テキストフィールド");
        JButton button = new JButton("ボタン");
        
        // ボタンにはイベントリスナを登録
        TextCopyAction action = new TextCopyAction(textField, label);
        button.addActionListener(action);
        
        // コンポーネントを登録
        frame.getContentPane().setLayout(new GridLayout(3, 1));
        frame.getContentPane().add(label);
        frame.getContentPane().add(textField);
        frame.getContentPane().add(button);
        
        frame.setSize(300, 150);
        frame.setVisible(true);
    }
}

JButton.addActionListener というメソッドによって、対象のボタンにイベント処理機能を追加することができる。これによって、ボタンが押された際に発生したイベント (ボタンによって自動的に発生する) が、登録したリスナーの actionPerformed メソッドに自動的に渡されてイベント処理が行われる。


ActionListener を登録できるのはボタンに限った話ではない。他にも JTextField にも登録することができる。

package j2.lesson11.example;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

public class EventSample2 {

    public static void main(String[] args) {
        JFrame frame = new JFrame("イベントのサンプル");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        // コンポーネントを作成
        JLabel label = new JLabel("ラベル");
        JTextField textField = new JTextField("テキストフィールド");
        JButton button = new JButton("ボタン");
        
        // ボタンとテキストフィールドにイベントリスナを登録
        TextCopyAction action = new TextCopyAction(textField, label);
        button.addActionListener(action);
        textField.addActionListener(action);
        
        // コンポーネントを登録
        frame.getContentPane().setLayout(new GridLayout(3, 1));
        frame.getContentPane().add(label);
        frame.getContentPane().add(textField);
        frame.getContentPane().add(button);
        
        frame.setSize(300, 150);
        frame.setVisible(true);
    }
}

JTextField も addActionListener メソッドを持っているので、JButton の際と同じようにアクション (イベント処理) を登録することができる。JTextField の場合はEnterキーが押された際にイベントが発生するので、上記プログラムでは「ボタンを押す」か「テキストフィールドでEnterキーを押す」かどちらかの操作をすることによってアクションを実行することができる。

匿名クラス (optional)

アクションに非常に簡単なもの、例えば「コンソールに文字列を表示する」などのプログラムを書く場合、実際のアクションは次の一行である。

System.out.println("Hello, world!");

それに対し、イベントハンドラを作成するには ActionListener を実装するクラスをわざわざ作る必要がある。

package j2.lesson11.example;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class HelloActionListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {
        System.out.println("Hello, world!");
    }
}

このような場合、わざわざ ActionListener を実装するクラスを別のファイルに作らないで、簡単にイベントハンドラを書く方法がある。

package j2.lesson11.example;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class AnonymousEvent {

    public static void main(String[] args) {
        JFrame frame = new JFrame("匿名クラスのテスト");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(400, 300);
        
        JButton button = new JButton("click");
        
        // ActionListener の追加時にクラスを作る
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.out.println("Hello, world!");
            }
        });
        
        frame.getContentPane().add(button);
        frame.setVisible(true);
    }
}

上記プログラムの new ActionListener() { ... } は、「ActionListener を実装するクラスを作り、同時にインスタンスを作成する」という意味を持つ。このクラスは名前を持たないため 匿名クラス と呼ばれ、下記のように書くことができる。

new インターフェース名() {
    インスタンスメンバ
}

これによって、「インターフェース名」で指定したインターフェースを実装した無名のクラスを作成し、同時にそのインスタンスを作成する。

また、インターフェースに限らず、通常のクラスや抽象クラスを指定して匿名クラスを作成することができる。

new クラス名(コンストラクタ引数) {
    インスタンスメンバ
}

上記では、「クラス名」で指定したクラスを継承した無名のクラスを作成し、同時に指定した引数をコンストラクタに渡してインスタンスを作成する。

先ほどの、

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        System.out.println("Hello, world!");
    }
});

では、ActionListener を実装する匿名クラスを作り、同時にインスタンスを作成し、それを button.addActionListener メソッドの引数としている。このように書くと、button が表すボタンがクリックされた際に「Hello, world!」とだけ表示する。

匿名クラスを用いると別のファイルを作成するよりは短いプログラムが書けるため便利であるが、不用意に使うと読みにくいプログラムになるので注意が必要である。

匿名クラスの特徴

匿名クラスは、次のような特徴を持つ。

コンストラクタの名前はそのクラス名と同じものにする必要があるが、匿名クラスはクラス名がないためにコンストラクタを宣言することができない。インスタンス生成時に外部の情報を渡す場合には、他の方法をとる必要がある。

その代わり、外側にあるクラスの this 参照を匿名クラスのメソッド内で使用することができる。

package j2.lesson11.example;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

public class OuterThis {
    
    private JTextField input;
    private JLabel label;

    // ウィンドウを表示する
    public void init() {
        JFrame frame = new JFrame("outer.thisのテスト");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(400, 300);
        
        this.label = new JLabel("label");
        this.input = new JTextField("text field");
        
        // テキストフィールドにアクションを設定
        this.input.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // 外側クラスの this を使ってインスタンスメソッドを呼び出す
                OuterThis.this.changeLabelText();
            }
        });
        
        frame.getContentPane().add(this.label, BorderLayout.CENTER);
        frame.getContentPane().add(this.input, BorderLayout.SOUTH);
        frame.setVisible(true);
    }
    
    // ラベルに表示されるテキストを変更
    void changeLabelText() {
        String text = this.input.getText();
        this.label.setText(text);
    }

    // main は init を呼び出すだけ
    public static void main(String[] args) {
        OuterThis main = new OuterThis();
        main.init();
    }
}

「外側にあるクラスの名前.this」で参照できる。ただし、外側にあるクラスで private で宣言されているインスタンスメンバにはアクセスできない。public で宣言しない場合にはパッケージアクセスなどで宣言するとよい。

また、次のように final を指定したローカル変数にアクセスすることができる。

package j2.lesson11.example;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

public class FinalLocal {
    
    // ウィンドウを表示する
    public void init() {
        JFrame frame = new JFrame("final のテスト");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(400, 300);
        
        // イベントハンドラから使用するインスタンスを final で指定
        final JLabel label = new JLabel("label");
        final JTextField input = new JTextField("text field");
        
        // テキストフィールドにアクションを設定
        input.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // final が付いているものにはアクセスできる
                String text = input.getText();
                label.setText(text);
            }
        });
        
        frame.getContentPane().add(label, BorderLayout.CENTER);
        frame.getContentPane().add(input, BorderLayout.SOUTH);
        frame.setVisible(true);
    }
    
    // main は init を呼び出すだけ
    public static void main(String[] args) {
        FinalLocal main = new FinalLocal();
        main.init();
    }
}

ローカル変数に final を指定すると、そのローカル変数は宣言と同時に値を代入する必要があり、それ以外では値を変更できなくなる。これを使うと、コンストラクタに label, input の引数を渡したときと同じことができる。

グラフィカルユーザインターフェース (2)

これまでに、Swing の GUI コンポーネントとして次の4つを紹介した。

他にも Swing の GUI コンポーネントは多数あり、そのうちいくつかを紹介する。

テキストエリア

javax.swing.JTextField はテキストを書き込める GUI コンポーネントであるが、このコンポーネントは一行のテキストしか扱うことはできなかった。

Windows に付属しているメモ帳などのアプリケーションは、複数行のテキストを扱うことができる。それに似たコンポーネントとして、Swing には javax.swing.JTextArea というクラスが用意されている。

package j2.lesson11.example;

import javax.swing.JFrame;
import javax.swing.JTextArea;

public class TextAreaSample {

    public static void main(String[] args) {
        JFrame frame = new JFrame("テキストエリアのサンプル");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        JTextArea textArea = new JTextArea("テキストエリア");
        frame.getContentPane().add(textArea);
        
        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

上記のプログラムを実行すると、次のように表示される。


このテキストエリアには、自由に文字列を書きこむことができる。


テキストエリアのスクロール

先ほどのテキストエリアに対し、はみ出すほどの文字列を書くとはみ出た分は表示することができない。


通常はテキストエリアの右端や下端にスクロールバーを表示して、はみ出た部分をスクロールして表示させる方式が普通であるのだが、Swing の JTextArea にはそのような機能は備わっていない。

Swing でスクロール処理を行うには javax.swing.JScrollPane という「スクロール可能なコンテナ」の上にスクロールさせたいコンポーネントを貼り付ければよい。

package j2.lesson11.example;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class ScrollableTextArea {

    public static void main(String[] args) {
        JFrame frame = new JFrame("テキストエリアのサンプル");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        // スクロール機能を持たないテキストエリア
        JTextArea textArea = new JTextArea("テキストエリア");
        
        // スクロール機能を持つコンテナにテキストエリアを貼り付ける
        JScrollPane scroll = new JScrollPane(textArea);
        
        // テキストエリア + スクロール機能 のコンポーネントをクライアントエリアに貼り付ける
        frame.getContentPane().add(scroll);
        
        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

上記のテキストエリアは、長い文章を貼り付けた際に自動的にスクロールバーが表示され、好きな位置を見ることができる。


また、テキストエリアには「行の折り返し機能」をつけることができる。これは、1行に長いテキストを書き込んだ際に、自動的に画面端で折り返して2行以上で表示してくれる機能である。

package j2.lesson11.example;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class WrappedTextArea {

    public static void main(String[] args) {
        JFrame frame = new JFrame("折り返しのサンプル");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        JTextArea textArea = new JTextArea("テキストエリア");
        // 行の折り返し機能を on にする
        textArea.setLineWrap(true);
        
        JScrollPane scroll = new JScrollPane(textArea);
        frame.getContentPane().add(scroll);
        
        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

上記のように、テキストエリアに対して「textArea.setLineWrap(true)」としてやることで行末で自動的に折り返すようなテキストエリアを作ることができる。


先ほどの JScrollPane は、テキストエリアにスクロール機能を付加するためだけのものではなく、Swing の GUI コンポーネントにスクロール機能を付加するためのものである。つまり、これまでに使用した JLabel などにもスクロール機能をつけることができる。

package j2.lesson11.example;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;

public class ScrollableComponents {

    public static void main(String[] args) {
        JFrame frame = new JFrame("スクロール可能なコンポーネント");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        // 大きめの画像を表示するラベル
        ImageIcon icon = new ImageIcon("U:/big.jpg");
        JLabel label = new JLabel(icon);
        
        // ラベルをスクロール可能にする
        JScrollPane scrollLabel = new JScrollPane(label);
        
        // ラベル + スクロール機能 をクライアントエリアに貼り付ける
        frame.getContentPane().add(scrollLabel);
        
        frame.setSize(250, 250);
        frame.setVisible(true);
    }
}

上記のプログラムを実行すると、スクロールバーの付いたウィンドウに画像が表示される。


メニューバー

GUI アプリケーションの多くは、「メニューバー」と呼ばれるものを持っている。Windows でタイトルバーのすぐ下にあるバーがそれである。


Swing で作成したアプリケーションにこのメニューバーをつけるには、javax.swing.JMenuBar というクラスを使えばよい。

package j2.lesson11.example;

import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JTextArea;

public class MenuBarSample1 {

    public static void main(String[] args) {
        JFrame frame = new JFrame("メニューバーのサンプル (1)");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        // メニューバーを作成して登録
        JMenuBar menuBar = new JMenuBar();
        frame.setJMenuBar(menuBar);

        JTextArea textArea = new JTextArea("テキストエリア");
        frame.getContentPane().add(textArea);
        
        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

メニューバーで注意すべき点は、frame.getContentPane().add() のようにクライアントエリアに追加するのではなく、フレームのメニューバー領域に直接追加する必要があるという点である。そのためのメソッドは、JFrame.setJMenuBar である (JFrame.setMenuBar というメソッドも存在するが、setJMenuBar とは別物なので使用しない)。

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


タイトルバーの下に、微妙にメニューバーが表示されている。これは、メニューバーに何もアイテムを追加していないため、非常に小さなメニューバーが作成されてしまったためである。

メニューバーにアイテムを追加するには、javax.swing.JMenuItem クラスのインスタンスを使う。

package j2.lesson11.example;

import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JTextArea;

public class MenuBarSample2 {

    public static void main(String[] args) {
        JFrame frame = new JFrame("メニューバーのサンプル");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        // メニューバーを作り
        JMenuBar menuBar = new JMenuBar();
        
        // そこにアイテムを追加していく
        JMenuItem item1 = new JMenuItem("item1");
        JMenuItem item2 = new JMenuItem("item2");
        menuBar.add(item1);
        menuBar.add(item2);
        
        // 作成したメニューバーを登録する
        frame.setJMenuBar(menuBar);

        JTextArea textArea = new JTextArea("テキストエリア");
        frame.getContentPane().add(textArea);

        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

上記のように、メニューバーに対して add メソッドでメニューアイテムを追加できる。このプログラムを実行すると、次のようにメニューバーにアイテムが追加されていることが確認できる。


つぎに、このアイテムがクリックされた際の動作を記述する。JMenuItem には addActionListener メソッドが用意されているため、ここに ActionListener を実装したクラスのインスタンスを渡すだけでよい。

テキストエリアに文字列を表示するだけのアクションは、次のように書ける。

package j2.lesson11.example;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JMenuItem;
import javax.swing.JTextArea;

public class MenuItemAction implements ActionListener {
    
    private final JTextArea area;

    // 表示先のテキストエリアを取得するコンストラクタ
    public MenuItemAction(JTextArea area) {
        this.area = area;
    }

    // メニューがクリックされた際のアクション
    public void actionPerformed(ActionEvent e) {
        // どのメニューがクリックされたかを調べる
        JMenuItem item = (JMenuItem) e.getSource();
        
        // メニュー文字列を取得
        String text = item.getText();
        
        // テキストエリアの先頭に追加 (\n は改行を表す)
        this.area.insert(text + "がクリックされました\n", 0);
        this.area.setCaretPosition(0);
    }
}

ActionEvent には getSource というメソッドがあり、これを呼び出すとアクションを発生させたコンポーネントを取得できる。ここではこれを利用して、アクションを発生させた JMenuItem インスタンスから文字列を取得している。

上記のアクションをメニューアイテムに登録するには、次のように addActionListener メソッドを使う。

package j2.lesson11.example;

import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class MenuBarSample3 {

    public static void main(String[] args) {
        JFrame frame = new JFrame("メニューバーのサンプル");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        JTextArea textArea = new JTextArea("テキストエリア");
        frame.getContentPane().add(new JScrollPane(textArea));

        JMenuBar menuBar = new JMenuBar();
        JMenuItem item1 = new JMenuItem("item1");
        JMenuItem item2 = new JMenuItem("item2");
        
        // メニューにアクションを登録
        MenuItemAction action = new MenuItemAction(textArea);
        item1.addActionListener(action);
        item2.addActionListener(action);
        
        // 作成したメニューバーを登録する
        menuBar.add(item1);
        menuBar.add(item2);
        frame.setJMenuBar(menuBar);

        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

上記プログラムを実行し、各メニューをクリックすると、次のようにテキストエリアに文字列が表示されていく。


メニュー

メニューバーに置かれているアイテムは、通常はクリックするとさらにメニューをポップアップして表示する。


このようなメニューは javax.swing.JMenu クラスを使うことによって実現できる。

package j2.lesson11.example;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class MenuBarSample4 {

    public static void main(String[] args) {
        JFrame frame = new JFrame("メニューのサンプル");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        JTextArea textArea = new JTextArea("テキストエリア");
        frame.getContentPane().add(new JScrollPane(textArea));

        JMenuBar menuBar = new JMenuBar();
        
        // (サブ)メニューを作成
        JMenu menu = new JMenu("menu");
        
        // (サブ)メニューにメニューアイテムを登録
        JMenuItem item1 = new JMenuItem("item1");
        JMenuItem item2 = new JMenuItem("item2");
        menu.add(item1);
        menu.add(item2);
        
        // メニューバーに(サブ)メニューを登録
        menuBar.add(menu);

        // 作成したメニューバーを登録
        frame.setJMenuBar(menuBar);

        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

上記のように、まずメニューを作成し、そこにメニューアイテムを追加する。そして、作成したメニューをメニューバーに登録することによってポップアップするようなメニューをメニューバーに配置できる。


ファイルの選択

メニューに欠かせないものが、次のようなファイル選択ダイアログである。


Swing では、javax.swing.JFileChooser を使うことによってファイル選択画面を表示することができる。

これまでに説明した Swing のコンポーネントと違い、JFileChooser はウィンドウに貼り付けたりして使用するものではない。新しいウィンドウを表示してファイルの選択をさせることが多い。そこで、次のようにアクションが起こされたらファイルを選択するというような ActionListener を作成する。

package j2.lesson11.example;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.JFileChooser;
import javax.swing.JFrame;

public class FileChooseAction implements ActionListener {

    private final JFrame frame;

    // ファイル選択画面には、ウィンドウが必要
    public FileChooseAction(JFrame frame) {
        this.frame = frame;
    }
    
    public void actionPerformed(ActionEvent e) {
        JFileChooser chooser = new JFileChooser();
        
        // ファイルを開くダイアログ (ウィンドウ) を作成する
        // この際に、元のウィンドウを引数に指定する
        chooser.showOpenDialog(this.frame);
        
        // 選択されたファイルは java.io.File 型で取得できる。
        File selected = chooser.getSelectedFile();
        
        // ファイルが選択されなかった場合、 null が返される
        if (selected == null) {
            System.out.println("ファイルが選択されませんでした");
        }
        else {
            // File.getPath() で選択されたファイルを表す文字列を取得
            String path = selected.getPath();
            System.out.println(path + "が選択されました");
        }
    }
}

まず、

JFileChooser chooser = new JFileChooser();

によってファイル選択ウィンドウを作成する。この時点ではまだ表示されないので、次の

chooser.showOpenDialog(this.frame);

によって、ファイルを開くウィンドウを表示する。この引数にウィンドウを指定すると、ファイル選択中はウィンドウを操作できなくなる。null という定数を指定してもよい。

ファイル選択が終了するまで次の行は実行されず、ファイル選択が終了したら次の行で選択されたファイルを取得できる。

File selected = chooser.getSelectedFile();

選択されたファイルは、java.io.File 型のオブジェクトとして返される。このオブジェクトはファイルを表していて、File.getPath() メソッドによって文字列に変換することができる。

ファイル選択画面がキャンセルされたりしてファイルが選択されなかった場合、getSelectedFile() は null を返す。

// ファイルが選択されなかった場合、 null が返される
if (selected == null) {
    System.out.println("ファイルが選択されませんでした");
}
else {
    // File.getPath() で選択されたファイルを表す文字列を取得
    String path = selected.getPath();
    System.out.println(path + "が選択されました");
}

このプログラムを実行すると、次のようなファイル選択画面が表示される。


このほかに、showSaveDialog というファイル保存のためのダイアログも存在する。使い方はほとんど showOpenDialog と同じなので、説明は割愛する。

その他のコンポーネント

他にも Swing には様々なコンポーネントが用意されている。一例を挙げると、以下のようなものがある。

これらのコンポーネントは、javax.swing パッケージ内に J から始まるクラスとして用意されている。Java API 仕様 や、The Java Tutorial などを参考に、いくつかプログラムを書いてみるとよい。