WisdomSoft - for your serial experiences.

6.2 ボタン

ボタンを表示し、入力に反応するプログラムを作成します。押すことができる単純なボタンや、選択状態を切り替えるチェックボタン、ラジオボタンを解説します。

6.2.1 プッシュボタン

クリックすることによって何らかのプログラムの機能を起動させるようなユーザーインタフェースにはボタンが最適です。ボタンにはいくつか種類がありますが、最も一般的なボタンはプッシュボタンです。プッシュボタンは通常の状態と押されたときの状態に分かれ、マウスでクリックされているときなどに押された状態に移行します。

Swing のプッシュボタンは javax.swing.JButton クラスで実装されています。

javax.swing.JButton クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.AbstractButton
                          |
                          +--javax.swing.JButton
public class JButton extends AbstractButton implements Accessible

JButton クラスはコンポーネントを押したり選択するような性質を持つ全てのボタンコンポーネントが継承する抽象スーパークラスとして定められています。AbstractButton クラスには、ボタンとして動作する全てのコンポーネントに共通する基本的な機能が公開されています。

表1 JButton クラスのコンストラクタ
コンストラクタ 解説
public JButton() テキストおよびアイコンなしのボタンを生成します。
public JButton(Icon icon) アイコン付きのボタンを生成します。
public JButton(String text) テキスト付きのボタンを生成します。
public JButton(Action a) 指定された Action からプロパティを取得してボタンを生成します。
public JButton(String text, Icon icon) 初期テキストおよびアイコン付きのボタンを生成します。

JButton クラスには特に重要なメソッドはなく、ボタン制御のために必要な基本的なメソッドは全て AbstractButton クラスで定義されています。JButton() コンストラクタからは、アイコンと文字列を指定してラベルのようにボタンの中に表示させることができます。Action という型のオブジェクトをパラメータで受け取るコンストラクタがありますが、これはコンポーネントに対して動作やデータを提供することができるメソッドを定義したインタフェースです。このインタフェースについてはすぐ後に説明します。

コード1
import javax.swing.*;

public class Test {
	public static void main(String args[]) {
		Icon icon = new ImageIcon("icon1.jpg");
		JButton button = new JButton("Hello Kitty" , icon);

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(button);
		win.show();
	}
}
実行結果
コード1 実行結果

コード1は、JButton クラスのインスタンスを生成しウィンドウに表示しています。このボタンには、ボタンに表示する文字列とアイコンを設定しています。AbstractButton クラスはボタン上の内容にアイコンとテキストを設定できるように設計されているため、事実上ラベルとほぼ同じ操作が可能です。ただし、ボタンはラベルとは異なり入力を受け付けます。ボタンをマウスでクリックすると、背景色が濃いグレーになって押されていることをアピールするでしょう。

6.2.2 抽象ボタンクラス

JButton クラスは、Swing の全てのボタン関連クラスが基底とする javax.swing.AbstractButton クラスを継承しています。このクラスは、ボタンの動作に重要な基本的なメソッドを公開しています。ボタンの制御は、ほとんどこのクラスが提供するメソッドだけで実現できるでしょう。

javax.swing.AbstractButton クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.AbstractButton
public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants

重要なのは、JButton クラスも含めて、ボタン的性質を持つ全てのコンポーネントを AbstractButton 型オブジェクトとして統一的に制御することが可能である Swing の優秀な設計です。ボタン的性質を持つコンポーネントとは、このほかにメニュー項目やチェックボタン、ラジオボタンなどが含まれます。これらのコンポーネントも AbstractButton クラスを継承しています。

表2 AbstractButton クラスの基本的なメソッド(抜粋)
メソッド 解説
public void addActionListener(ActionListener l) ボタンに ActionListener を追加する。
public void removeActionListener(ActionListener l) ボタンから ActionListener を削除する。
public ActionListener[] getActionListeners() 設定されているすべての ActionListener の配列を返す。
public void setText(String text) ボタンのテキストを設定する。
public String getText() ボタンのテキストを返す。
public void setIcon(Icon defaultIcon) ボタンのデフォルトのアイコンを設定する。
public Icon getIcon() デフォルトのアイコンを返す。
public void setDisabledIcon(Icon disabledIcon) 無効な状態のボタンのアイコンを設定する。
public Icon getDisabledIcon() 無効な状態のボタンのアイコンを返する。
public void setVerticalAlignment(int alignment) アイコンとテキストの垂直方向の配置を設定する。
public int getVerticalAlignment() アイコンとテキストの垂直方向の配置を返す。
public void setHorizontalAlignment(int alignment) アイコンとテキストの水平方向の配置を設定する。
public int getHorizontalAlignment() アイコンとテキストの水平方向の配置を返す。
public void setVerticalTextPosition(int textPosition) アイコンに対するテキストの垂直の位置を設定する。
public int getVerticalTextPosition() アイコンに対するテキストの垂直の位置を返す。
public void setHorizontalTextPosition(int textPosition) アイコンに対するテキストの水平の位置を設定する。
public int getHorizontalTextPosition() アイコンに対するテキストの水平の位置を返す。
public void setIconTextGap(int iconTextGap) アイコンとテキストの間の距離を設定する。
public int getIconTextGap() アイコンとテキストの間の距離を返す。

表2を見ても、AbstractButton が提供するこれらのメソッドは JLabel で提供されているメソッドと共通しているため、ボタンの内容は性質的にラベルとほとんど同じであることがわかります。全てのボタンはアイコンとテキストをラベルと同じように表示することができるのです。

ボタンが押されたことをプログラムが知るには、これまで同様にリスナを用います。ボタンが押されると AbstractButton は登録されている ActionListener オブジェクトの actionPerformed() メソッドを呼び出します。開発者はこのメソッドを実装してボタンが押されたときの処理を記述するのです。

コード2
import javax.swing.*;
import java.awt.event.*;

public class Test {
	public static void main(String args[]) {
		Icon icon = new ImageIcon("icon1.jpg");
		JButton button = new JButton("Hello Kitty" , icon);
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.out.println(e);
			}
		});

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(button);
		win.show();
	}
}

コード2はボタンに ActionListener オブジェクトを追加してボタンが押されたときの処理を実現しています。このプログラムを実行してボタンを押すと、標準出力にイベント情報が表示されるでしょう。ボタンを押すことによってプログラムが何らかの応答を行うには、このようにリスナを実装します。

6.2.3 トグルボタン

JButton クラスのボタンコンポーネントの動作は「押す」だけでした。マウスのボタンが押されている間はボタンが押され続け、マウスのボタンを離すとボタンは元に戻ります。しかし、照明の切り替えスイッチのように、ボタンを一度押したら選択状態を維持し、もう一度ボタンを押すことによって元に戻る 2 状態を維持できるボタンが必要になることもあります。

このような 2 つの状態を維持できるボタンはトグルボタンと呼ばれ、javax.swing.JToggleButton クラスで実装されています。

javax.swing.JToggleButton クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.AbstractButton
                          |
                          +--javax.swing.JToggleButton
public class JToggleButton extends AbstractButton implements Accessible

JToggleButton コンポーネントの外見は JButton コンポーネントと変わりませんがボタンを押したときの動作に違いがあります。JToggleButton は 2 つの状態を維持することが可能なので、マウスのボタンを離してもコンポーネントはそのまま選択された状態を維持します。そして、もう一度マウスでクリックすると元の状態に戻ります。

表3 JToggleButton クラスのコンストラクタ
コンストラクタ 解説
public JToggleButton() トグルボタンをテキストおよびイメージの指定なしで作成する。
public JToggleButton(Icon icon) トグルボタンをイメージの指定あり、テキストの指定なしで作成する。
public JToggleButton(Icon icon, boolean selected) トグルボタンをイメージおよび選択状態の指定あり、テキストの指定なしで作成する。
public JToggleButton(String text) トグルボタンを指定されたテキストで作成する。
public JToggleButton(String text, boolean selected) トグルボタンを指定されたテキストおよび選択状態で作成する。
public JToggleButton(Action a) 指定された Action からプロパティを取得するトグルボタンを作成する。
public JToggleButton(String text, Icon icon) トグルボタンを指定されたテキストおよびイメージで作成する。
public JToggleButton(String text, Icon icon, boolean selected) トグルボタンを指定されたテキスト、イメージ、および選択状態で作成する。

表3は JToggleButton クラスで宣言されているコンストラクタです。JButton クラスに比べて、コンストラクタには選択状態を初期化する boolean 型のパラメータが増えていますが、それ以外で新しいものはありません。重要なメソッドは全て AbstractButton クラスから継承しているため、コンポーネントの制御のほとんどは AbstractButton クラスのメソッドで実現することができます。

表4 AbstractButton クラスの選択に関するメソッド
メソッド 解説
public void setSelected(boolean b) ボタンの選択状態を設定する。
public boolean isSelected() ボタンの選択状態を返す。
public void setSelectedIcon(Icon selectedIcon) 選択状態のボタンのアイコンを設定する。
public Icon getSelectedIcon() 選択状態のボタンのアイコンを返す。
public void addItemListener(ItemListener l) ボタンに ItemListener を追加します。
public void removeItemListener(ItemListener l) ボタンから指定した ItemListener を削除します。
public ItemListener[] getItemListeners() このボタンに設定されているすべての ItemListener の配列を返す。

表4は AbstractButton クラスの選択に関するメソッドです。JButton コンポーネントでは大きな意味を持つものではありませんでしたが、選択可能なボタンではこれらのメソッドが必要になるでしょう。ボタンが押されることによって発生するイベントは ActionListener インタフェースで登録することができましたが、選択状態の推移に関しては java.awt.event.ItemListener インタフェースを用いてイベントを受け取る必要があります。

もちろん、ボタンが押されれば ActionListener も呼び出されるためこれを処理する形でも不可能ではありませんが、ActionEvent オブジェクトは選択状態の情報を持ちません。これに対し、ItemListener インタフェースが宣言する itemStateChanged() メソッドのパラメータからは、イベントを発生させた源泉オブジェクトの選択状態を取得することができるのです。

ItemListener インタフェース itemStateChanged() メソッド
public void itemStateChanged(ItemEvent e)

このメソッドの引数には java.awt.event.ItemEvent クラスのオブジェクトが指定されています。 選択状態が変更したオブジェクトの選択状態などは、このイベント引数から取得することができます。

java.awt.event.ItemEvent クラス
java.lang.Object
  |
  +--java.util.EventObject
        |
        +--java.awt.AWTEvent
              |
              +--java.awt.event.ItemEvent
public class ItemEvent extends AWTEvent

このクラスは、選択状態になったのか、または非選択状態になったのかを表す値を getStateChange() メソッドから取得することができます。ItemEvent クラスのフィールドで、選択状態と非選択状態を表す SELECTED 及び DESELECTED 定数が公開されているので、メソッドの戻り値と比較することでイベントが選択状態に移行したものなのか、選択が解除されたものなのかを知ることができるでしょう。

ItemEvent クラス getStateChange() メソッド
public int getStateChange()

例えば、ボタンが非選択状態のときにクリックされて選択状態に移行するイベントでは getStateChange() メソッドは ItemEvent.SELECTED を返します。

コード3
import javax.swing.*;
import java.awt.event.*;

public class Test {
	public static void main(String args[]) {
		Icon icon = new ImageIcon("icon1.jpg");
		JToggleButton button = new JToggleButton("Hello Kitty" , icon);
		button.setSelectedIcon(new ImageIcon("icon2.jpg"));
		button.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				if (e.getStateChange() == ItemEvent.SELECTED)
					System.out.println("Selected");
				else System.out.println("Deselected");
			}
		});

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(button);
		win.show();
	}
}
実行結果
コード3 実行結果

コード3は、2 状態ボタンの JToggleButton コンポーネントを表示するプログラムです。通常のボタンとは異なり、一度クリックすると選択状態のままで維持されます。選択状態を解除するにはもう一度コンポーネントをクリックします。

選択状態と非選択状態のアイコンをそれぞれ設定することもできるため、このプログラムではデフォルトのアイコンをコンストラクタから指定し、さらに選択状態のアイコンを setSelectedIcon() メソッドを使って設定しています。ボタンを選択状態にすると、アイコンが変化することを確認できるでしょう。

6.2.4 チェックボタンとラジオボタン

JToggleButton は JButton と同じ外見で、理論的に利用方法が異なるコンポーネントでした。これをより具体化したコンポーネントがチェックボタンです。

チェックボタンはボタンに矩形のチェック領域が存在し、コンポーネントが選択されている場合はチェックされていることをアピールするマークが表示されるようになります。非選択状態は何も無い矩形アイコンが表示され、選択状態になるとチェックマーク付きのアイコンに変化します。

チェックボタンを表示するには javax.swing.JCheckBox クラスを使います。このクラスは JToggleButton クラスを継承し 2 状態のアイコンを独自に定義しています。

javax.swing.JCheckBox クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.AbstractButton
                          |
                          +--javax.swing.JToggleButton
                                |
                                +--javax.swing.JCheckBox
public class JCheckBox extends JToggleButton implements Accessible

JCheckBox クラスのコンストラクタは、基本的に JToggleButton クラスのコンストラクタと同じパラメータですが、通常このクラスのアイコンを変更することはありません。なぜならば、チェック用の四角いボックスがアイコンの役割を果たしてくれているからです。

JCheckBox コンポーネントを押すことで変化するのはアイコンだけです。つまり、このコンポーネントはアイコンで選択状態と非選択状態を表すため、原則として必ずアイコンが必要です。選択状態のアイコンが設定されていない場合は選択することができません。基本的には、デフォルトのアイコンを用いるべきですが、独自のアイコンを使って選択状態を表す場合は必ず選択状態のアイコンの両方を設定するようにしましょう。

コード4
import javax.swing.*;

public class Test {
	public static void main(String args[]) {
		AbstractButton button = new JCheckBox("Kitty on your lap");
		button.setSelected(true);

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(button);
		win.show();
	}
}
実行結果
コード4 実行結果

コード4はデフォルトのアイコンを持つチェックボタンを表示しています。チェック状態をアイコンで表現するということを除いては JToggleButton クラスに比べて大きな違いはありません。

Swing にはチェックボタンと兄弟関係にあるラジオボタンを実装する javax.swing.JRadioButton クラスも存在します。 ラジオボタンとは、1 つだけをみるとチェックボックスと違いがわかりませんが、複数の関連したラジオボタンの中で常に 1 つだけしか選択できない排他的な 2 状態ボタンとして利用します。

javax.swing.JRadioButton クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.AbstractButton
                          |
                          +--javax.swing.JToggleButton
                                |
                                +--javax.swing.JRadioButton
public class JRadioButton extends JToggleButton implements Accessible

このクラスのコンストラクタの引数もまた JToggleButton クラスと同じなので省略します。このコンポーネントは、デフォルトでチェックボタンと区別するために丸い形のアイコンを表示します。

このクラスは、基本的にデフォルトのアイコンを除いて JCheckBox クラスと大きな違いはありません。しかし、一般的なアプリケーションの間ではラジオボタンは排他的なボタンとして使われているため、ユーザーが直観的に複数のボタンの中からひとつしか選択することができないボタンであることを認識できることが期待できます。

複数のラジオボタンを排他的に選択するには、関係するラジオボタンの 1 つの選択状態が変化したときにすべてのラジオボタンを繰り返し処理で調べ、新しく選択されたボタン以外を非選択状態に移行するプログラムを書かなければなりません。幸い、この作業は javax.swing.ButtonGroup クラスが代行してくれます。

javax.swing.ButtonGroup クラス
java.lang.Object
  |
  +--javax.swing.ButtonGroup
public class ButtonGroup extends Object implements Serializable

ButtonGroup クラスは複数の AbstractButton オブジェクトを 1 つのグループとして管理してくれます。このクラスのインスタンスに登録されているボタンは常に 1 つしか選択できません。他のボタンが選択されようとすると、現在選択されているボタンは非選択状態に移行します。ButtonGroup クラスのメソッドは AbstractButton として扱ってくれるため、ラジオボタン以外にチェックボタンなどでも排他的な関係を作ることができます。

表5 ButtonGroup クラスのコンストラクタとメソッド
コンストラクタ 解説
public ButtonGroup() 新しい ButtonGroup を生成します。
メソッド
public void add(AbstractButton b) グループにボタンを追加します。
public void remove(AbstractButton b) グループからボタンを削除します。
public Enumeration getElements() グループに関連するすべてのボタンを返します。
public ButtonModel getSelection() 選択されたボタンのモデルを返します。
public void setSelected(ButtonModel m, boolean b) ButtonModel に対して選択された値を設定します。
public boolean isSelected(ButtonModel m) ButtonModel が選択されているかどうかを返します。
public int getButtonCount() グループ内のボタンの数を返します。

ButtonGroup オブジェクトのグループにボタンを追加するには add() メソッドを使って AbstractButton オブジェクトを指定します。複数のラジオボタンを同じグループとして追加すれば、グループ内のボタンのどれか 1 つしか選択できなくなります。

コード5
import javax.swing.*;
import java.awt.*;

public class Test {
	public static void main(String args[]) {
		AbstractButton button1= new JRadioButton("猫が好き");
		AbstractButton button2 = new JRadioButton("犬が好き");

		ButtonGroup group = new ButtonGroup();
		group.add(button1);
		group.add(button2);

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().setLayout(new FlowLayout());
		win.getContentPane().add(button1);
		win.getContentPane().add(button2);
		win.show();
	}
}
実行結果
コード5 実行結果

コード5は、排他的な関係の 2 つのラジオボタンを生成しウィンドウに表示しています。これらのボタンは同一のグループとして配属されているため、一方を選べばもう一方の選択は自動的に解除されてしまいます。同時に選択するということはできません。

6.2.5 アクション

Swing にはアクションと呼ばれる新しい機能が javax.swing.Action インタフェースで宣言されています。このインタフェースは ActionListener インタフェースを拡張していて、リスナの提供以外にコンポーネントの機能に関する情報を提供します。複数のコンポーネントがリスナと機能に関するプロパティを共有する場合はこのインタフェースでデータを一元管理することができます。

表6 Action インタフェースのメソッド
メソッド 解説
public void addPropertyChangeListener(PropertyChangeListener listener) PropertyChange リスナーを追加する。
public void removePropertyChangeListener(PropertyChangeListener listener) PropertyChange リスナーを削除する。
public void setEnabled(boolean b) 有効状態を設定する。
public boolean isEnabled() 有効状態を返す。
public void putValue(String key, Object value) 関連付けられているキーを使って、オブジェクトのプロパティの 1 つを設定する。
public Object getValue(String key) 関連付けられているキーを使ってオブジェクトのプロパティの 1 つを返す。

Action インタフェースで宣言されているメソッドは表6に表します。Action を実装するクラスはこれらのメソッドに加えて ActionListener のメソッドを実装しなければなりません。Action インタフェースはリスナに加えてプロパティを提供する機能が追加されてます。ここから、コンポーネントが共有するアイコンやテキストなどの基本情報と、イベントを処理するリスナを同時に提供することができるのです。

putValue() メソッドgetValue() メソッドは、文字列のキーを使ってプロパティを識別して目的の値を取得することができます。この考え方は JComponent クラスのクライアントプロパティと同じです。そして、Action オブジェクトのプロパティが変化すれば登録されている PropertyChangeListener が呼び出されるため、コンポーネントはプロパティの変化にも柔軟に対応することができます。

Action インタフェースでは基本的なプロパティとして NAME や SMALL_ICON 定数を宣言しています。これらの定数は String 型なので Action の実装が共通して使えるプロパティのキーとなります。NAME はこのアクションの名前表す String 型オブジェクトを、SMALL_ICON は Icon オブジェクトをプロパティの値として設定します。

ボタン関連クラスのコンストラクタは Action オブジェクトを受け取るコンストラクタを定義しています。Action オブジェクトを使ってコンポーネントを初期化すると、Action に設定されている NAME 及び SMALL_ICON 定数をキーとしてプロパティの値を取得し、これをボタンが表示する内容として利用されます。

Swing では Action インタフェースが javax.swing.AbstractAction クラスで実装されています。このクラスは Action を実装するすべての Swing クラスが継承する基本抽象クラスとなります。このクラスは Action インタフェースが求めている機能を実装しているため、開発者は AbstractAction クラスを継承して ActionListener を実装するだけで Action の実装を作ることができるのです。Action インタフェースは ActionListener インタフェースを継承しているため actionPerformed() メソッドを定義しなければなりません。

javax.swing.AbstractAction クラス
java.lang.Object
  |
  +--javax.swing.AbstractAction
public abstract class AbstractAction extends Object implements Action, Cloneable, Serializable

AbstractAction クラスのコンストラクタでは、パラメータを受け取らないコンストラクタ以外に文字列とアイコンを受け取るコンストラクタが定義されています。この場合、文字列は NAME プロパティとして、アイコンは SMALL_ICON プロパティとして保存されます。

表7 AbstractAction クラスのコンストラクタ
コンストラクタ 解説
public AbstractAction() デフォルトの説明文字列およびアイコンで初期化します。
public AbstractAction(String name) 指定された説明文字列およびデフォルトのアイコンで初期化します。
public AbstractAction(String name, Icon icon) 指定された説明文字列およびアイコンで初期化します。

Action インタフェースのフィールドで定義されているテキストとアイコン以外のプロパティについては、各々のクラスのフィールドでキーが定義されています。選択されている状態のアイコンなどを Action オブジェクトから設定するには、そのコンポーネントを実装するクラスで公開されている文字列フィールド定数を利用してください。

コード6
import java.awt.event.*;
import javax.swing.*;

public class Test {
	public static void main(String args[]) {
		Action action = new ButtonAction();
		action.putValue(Action.NAME , "Kitty on your lap");
		action.putValue(Action.SMALL_ICON , new ImageIcon("icon1.jpg"));

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(new JButton(action));
		win.show();
	}
}

class ButtonAction extends AbstractAction {
	public void actionPerformed(ActionEvent e) {
		System.out.println("On Action Event");
	}
}

コード6は、JButton() コンストラクタの引数に Action インタフェースのオブジェクトを指定しています。このオブジェクトには Action.NAME プロパティと Action.SMALL_ICON プロパティを設定しています。ボタンは Action がデフォルトで提供するこれらのプロパティを使ってアイコンとテキストを表示するでしょう。また、ボタンが押されるとアクションに設定されているリスナが呼び出されることも確認できます。