WisdomSoft - for your serial experiences.

11.3 列の制御

Swing ではテーブルの列を柔軟にカスタマイズできます。列の情報は TableColumnModel クラスで表され、列の追加や表示の制御を細かく設定できます。

11.3.1 テーブルヘッダ

JTable クラスのオブジェクトを JScrollPane コンテナに追加したときに、上部に列名を描画するヘッダが表示されていました。なぜ、JScrollPane コンテナ上に表示したときにヘッダが表示されたのかというと、コンテナが JTable クラスが提供するテーブルヘッダコンポーネントを表示していたためです。

JScrollPane コンテナに追加しない場合でも、明示的にテーブルヘッダコンポーネントを追加すれば、任意の場所にヘッダを表示することができます。テーブルヘッダは、JTable クラスの setTableHeader() メソッドでテーブルに設定することができ、getTableHeader() メソッドから取得することができます。

JTable クラス setTableHeader() メソッド
public void setTableHeader(JTableHeader tableHeader)
JTable クラス getTableHeader() メソッド
public JTableHeader getTableHeader()

これらのメソッドから設定したり、取得したりするオブジェクトは javax.swing.table.JTableHeader クラスのオブジェクトです。 このクラスは JComponent クラスを継承する軽量コンポーネントなので、コンテナに追加して表示することができます。

javax.swing.table.JTableHeader クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.table.JTableHeader
public class JTableHeader extends JComponent implements TableColumnModelListener, Accessible

このクラスは、デフォルトで列を移動させたり、列のサイズを変更することができます。JScrollPnae コンテナで表示したときは、実際に列をドラッグして移動するということができたはずです。しかし、JTableHeader クラスのメソッドから設定を変更することができます。

表1 JTableHeader クラスのコンストラクタとメソッド(抜粋)
コンストラクタ メソッド
public JTableHeader() デフォルトの TableColumnModel で JTableHeader を構築する。
public JTableHeader(TableColumnModel cm) cm を列モデルとして初期化される JTableHeader を構築する。
メソッド
public void setReorderingAllowed(boolean reorderingAllowed) ユーザが列ヘッダをドラッグして列の順序を変えられるかどうかを設定する。
public boolean getReorderingAllowed() ユーザがヘッダをドラッグして列の移動ができる場合に true を返す。
public void setResizingAllowed(boolean resizingAllowed) ユーザがヘッダ間をドラッグして列のサイズを変更できるかどうかを設定る。
public boolean getResizingAllowed() ユーザがヘッダ間をドラッグして列のサイズ変更ができる場合に true を返す。
public void setTable(JTable table) このヘッダに関連したテーブルを設定する。
public JTable getTable() このヘッダに関連したテーブルを返す。

JTableHeader クラスは新たにインスタンスを生成して設定することもできますが、通常は JTable クラスのデフォルトのテーブルヘッダで十分でしょう。

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

public class Test {
	public static void main(String args[]) {
		JTable table = new JTable(10 , 5);
		for(int c = 0 ; c < table.getColumnCount() ; c++) {
			for(int r = 0 ; r < table.getRowCount() ; r++) {
				table.setValueAt("行" + r + ":列" + c , r , c);
			}
		}

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

コード1は、JTable コンポーネントを JScrollPane に追加するのではなく、ウィンドウのコンテンツペインに直接追加しています。この状態ではテーブルヘッダが表示されないので、getTableHeader() メソッドから JTable に設定されているテーブルヘッダコンポーネントを取得し、このコンポーネントをコンテンツペインの下部に表示しています。

11.3.2 列モデル

JTable クラスでは、列を初期化する方法の 1 つに、列の情報を提供する javax.swing.table.TableColumnModel インタフェースを実装するオブジェクトを受け取るコンストラクタがありました。TableColumnModel は列モデルと呼ばれ、列の情報を提供するメソッドを宣言しています。

表2 TableColumnModel インタフェースのメソッド
メソッド 解説
public void addColumn(TableColumn aColumn) 列の最後に aColumn を追加する。
public void removeColumn(TableColumn column) 列から column を削除する。
public void moveColumn(int columnIndex, int newIndex) columnIndex にある列およびヘッダを newIndex に移動する。
public void setColumnMargin(int newMargin) 列のマージンを newMargin に設定する。
public int getColumnMargin() 列のマージンを返す。
public int getColumnCount() モデルの列数を返す。
public Enumeration getColumns() モデルのすべての列の Enumeration を返す。
public int getColumnIndex(Object columnIdentifier) columnIdentifier と等しい最初の列のインデックスを返す。
public TableColumn getColumn(int columnIndex) columnIndex にある列の TableColumn オブジェクトを返す。
public int getColumnIndexAtX(int xPosition) xPosition にある列のインデックスを返す。
public int getTotalColumnWidth() すべての列の幅の合計を返す。
public void setColumnSelectionAllowed(boolean flag) 列を選択できるかどうかを設定する。
public boolean getColumnSelectionAllowed() 列を選択できる場合は true を返す。
public int[] getSelectedColumns() 選択されている列すべてのインデックスの配列を返す。
public int getSelectedColumnCount() 選択されている列の数を返す。
public void setSelectionModel(ListSelectionModel newModel) 選択モデルを設定する。
public ListSelectionModel getSelectionModel() 現在の選択モデルを返す。
public void addColumnModelListener(TableColumnModelListener x) テーブル列モデルイベント用のリスナーを追加する。
public void removeColumnModelListener(TableColumnModelListener x) テーブル列モデルイベント用のリスナーを削除する。

いくつか重要なメソッドがありますが、この中で最も重要なのは列を追加したり、削除したりすることができる addColumn() メソッドと removeColumn() メソッドでしょう。これらのメソッドを使えば、プログラムが任意のタイミングで列を変更することができます。moveColumn() メソッドを使えば、列の位置をプログラムから移動することができます。列は移動可能なので getColumnIndexAtX() メソッドの存在からも想像できるように、実際に表示されている列の視覚表現とインデックスが異なる可能性があります。

addColumn() メソッドremoveColumn() メソッドの対象となる列を表すオブジェクトは javax.swing.table.TableColumn クラスのオブジェクトです。このクラスは、列に関する様々なプロパティを提供します。

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

TableColumn クラスを用いれば、列のインデックスや幅などを指定することができます。

表3 TableColumn クラスのコンストラクタとメソッド(抜粋)
コンストラクタ 解説
public TableColumn() デフォルト列を生成する。
public TableColumn(int modelIndex) モデルインデックスを指定して列を生成する。
public TableColumn(int modelIndex, int width) モデルインデックスと幅を指定して列を生成する。
public TableColumn(int modelIndex, int width, TableCellRenderer cellRenderer, TableCellEditor cellEditor) モデルインデックス、幅、レンダラとエディタを指定した列を生成する。
メソッド
public void setModelIndex(int modelIndex) この列のモデルインデックスを設定する。
public int getModelIndex() この列のモデルインデックスを返す。
public void setHeaderValue(Object headerValue) ヘッダレンダリングの値として使われる Object を設定する。
public Object getHeaderValue() ヘッダレンダリングの値として使われる Object を返す。
public void setIdentifier(Object identifier) 列の識別子を anIdentifier に設定する。
public Object getIdentifier() この列の identifier オブジェクトを返す。
public void setResizable(boolean isResizable) この列がサイズ変更できるかどうかを設定する。
public boolean getResizable() ユーザが列の幅を変更できる場合に true を返す。
public void setWidth(int width) 列の幅を設定する。
public int getWidth() 列の幅を返す。
public void setPreferredWidth(int preferredWidth) 列の適切な幅を preferredWidth に設定する。
public int getPreferredWidth() 列の適切な幅を返す。
public void setMinWidth(int minWidth) 列の最小の幅を minWidth に設定する。
public int getMinWidth() 列の最小の幅を返す。
public void setMaxWidth(int maxWidth) 列の最大の幅を maxWidth に設定する。
public int getMaxWidth() 列の最大の幅を返す。

表3は、TableColumn クラスの主なコンストラクタとメソッドです。TableCellRenderer 型と TableCellEditor 型というオブジェクト設定するコンストラクタがありますが、これは TableColumn クラスの列の描画や編集に関する強力なオブジェクトも提供するためのものです。これに関しては「セルのレンダラとエディタ」で詳しく解説したいと思います。

TableColumn クラスは、列の幅に関する情報と、列の値に関する情報を設定したり取得することができます。テーブルヘッダに描画される列の値は setHeaderValue() メソッドで設定し、getHeaderValue() メソッドから取得することができます。列の識別子を設定する setIdentifier() メソッドと、取得する getIdentifier() メソッドは、実際に表示される値とは関係のない内部の識別用オブジェクトを管理します。列の識別子は、JTable クラスでは利用されず、列を検索ときの検索情報などとして利用することができます。

列の幅は、setWidth() メソッドから設定することができますが、レイアウトマネージャが状況に応じて最適なサイズを設定するため、長期的な意味を持つことはできません。通常は、setPreferredWidth() メソッドから列の最適幅を指定します。ただし、列の幅は常に最小幅と最大幅の影響を受けます。デフォルトの最小値は 15、最大値は Integer.MAX_VALUE が設定されています。

JTable のデフォルトの設定では javax.swing.table.DefaultTableColumnModel クラスのオブジェクトが設定されています。 DefaultTableColumnModel クラスは、ほぼ TableColumnHeader インタフェースの単純な実装だと考えてよいでしょう。特別な事情がない限り、列モデルは JTable クラスがデフォルトで設定しているオブジェクトで十分です。

javax.swing.table.DefaultTableColumnModel クラス
java.lang.Object
  |
  +--javax.swing.table.DefaultTableColumnModel
public class DefaultTableColumnModel extends Object implements TableColumnModel,
	PropertyChangeListener, ListSelectionListener, Serializable

このクラスのコンストラクタは、引数を受け取らない単純なコンストラクタだけが公開されてます。

JTable クラスのオブジェクトに新しい列モデルを設定するには JTable クラスの setColumnModel() メソッドを使います。同様に、設定されている列モデルを取得したければ getColumnModel() メソッドから得ることができます。

JTable クラス setColumnModel() メソッド
public void setColumnModel(TableColumnModel columnModel)
JTable クラス getColumnModel() メソッド
public TableColumnModel getColumnModel()

何らかの動的な関係データを簡易表示するテーブルなどでは、列を動的に挿入したり、削除したりする必要があるかもしれません。そのような場合は、テーブルの列モデルを所得し、列を制御することができるのです。

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

public class Test extends JFrame implements ActionListener {
	public static void main(String args[]) {
		JFrame win = new Test();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.show();
	}

	JTable table = new JTable();
	private JButton addButton = new JButton("列を追加");
	private JButton removeButton = new JButton("列を削除");

	public Test() {
		JPanel panel = new JPanel();
		panel.setLayout(new FlowLayout());
		panel.add(addButton);
		panel.add(removeButton);

		addButton.addActionListener(this);
		removeButton.addActionListener(this);

		table.getColumnModel().setColumnSelectionAllowed(true);
		JScrollPane pane = new JScrollPane(table);

		getContentPane().add(pane);
		getContentPane().add(panel , BorderLayout.SOUTH);
	}

	public void actionPerformed(ActionEvent e) {
		TableColumnModel model = table.getColumnModel();

		if (e.getSource() == addButton) {
			TableColumn column = new TableColumn();
			column.setHeaderValue("列:" + model.getColumnCount());
			model.addColumn(column);
		}
		else if (e.getSource() == removeButton) {
			if (model.getColumnCount() == 0) return;
			model.removeColumn(model.getColumn(
				model.getColumnCount() - 1
			));
		}
	}
}
実行結果
コード2 実行結果

このプログラムは、ウィンドウ下部のボタンを押すことで、中央に表示されているテーブルの列を、任意のタイミングで追加したり削除したりすることができます。追加ボタンを押せば新しい列が末尾に挿入され、削除ボタンを押せば末尾の列が削除されます。プログラムの actionPerformed() メソッドでは、テーブルに設定されている列モデルを取得し、押されたボタンに応じて TableColumn オブジェクトを追加したり、削除したりしているのです。

列に対する変更は、列モデルに設定されているリスナ javax.swing.event.TableColumnModelListener インタフェースに報告されます。このインタフェースには、表 11_03_03 に示す 5 つのメソッドが宣言されています。

表4 TableColumnModelListener インタフェースのメソッド
メソッド 解説
public void columnAdded(TableColumnModelEvent e) モデルに列が追加されたことを通知する。
public void columnRemoved(TableColumnModelEvent e) モデルから列が削除されたことを通知する。
public void columnMoved(TableColumnModelEvent e) 列が再配置されたことを通知する。
public void columnMarginChanged(ChangeEvent e) マージンの変更のために列が移動されたことを通知する。
public void columnSelectionChanged(ListSelectionEvent e) TableColumnModel の選択モデルが変更されたことを通知する。

列の状態が変化したタイミングで何らかの処理が必要である場合や、列の数を常に監視したい場合などは、TableColumnModelListener インタフェースを実装し、TableColumnModel クラスの addColumnModelListener() から追加すれば良いでしょう。