WisdomSoft - for your serial experiences.

11.2 テーブルモデル

テーブルが表示するデータはテーブルモデルで管理されます。テーブルモデルは、任意のオブジェクトと、それを表示するテーブル内の位置(行と列)といった情報を提供します。

11.2.1 データの入出力と監視

JTable クラスが実行時に参照するセルのデータを提供するのはテーブルモデルです。テーブルモデルとは javax.swing.table.TableModel インタフェースを実装するオブジェクトで、このインタフェースはデータの入出力などを監視します。

表1 TableModel インタフェースのメソッド
メソッド 解説
public void addTableModelListener(TableModelListener l) TableModelListener を追加する。
public void removeTableModelListener(TableModelListener l) 設定されている TableModelListener を削除する。
public void setValueAt(Object aValue, int rowIndex, int columnIndex) 列番号 columnIndex と行番号 rowIndex にあるセルの値を設定する。
public Object getValueAt(int rowIndex, int columnIndex) 列番号 columnIndex と行番号 rowIndex にあるセルの値を返す。
public boolean isCellEditable(int rowIndex, int columnIndex) 行番号 rowIndex、列番号 columnIndex に位置するセルが編集可能であれば true を返す。
public Class getColumnClass(int columnIndex) 列のセル値の最も明確なスーパークラスを返す。
public String getColumnName(int columnIndex) 列番号 columnIndex にある列の名前を返す。
public int getColumnCount() モデルの列数を返す。
public int getRowCount() モデルの行数を返す。

JTable クラスがデータを表示したり、データを保存する操作を行うと、最終的にテーブルモデルが呼び出されます。ユーザーからの入力などでデータモデルに変更が加えられると、設定されている javax.swing.event.TableModelListener インタフェースが呼び出されます。 このリスナには、データモデルが変更されたときに呼び出される tableChanged() メソッドだけが宣言されてます。

TableModelListener インタフェース tableChanged() メソッド
public void tableChanged(TableModelEvent e)

このメソッドが引数から受け取るのは javax.swing.event.TableModelEvent クラスのオブジェクトです。このオブジェクトからは、テーブルのどの列が変更されたのか、変更の内容はどのようなものなのかを得ることができます

javax.swing.event.TableModelEvent クラス
java.lang.Object
  |
  +--java.util.EventObject
        |
        +--javax.swing.event.TableModelEvent
public class TableModelEvent extends EventObject

このクラスで新しく定義されているメソッドは 4 つです。テーブルに対する変更は 1 つのセルとは限りません。複数の列、複数の行が変更される可能性があるため、変更された列を得る getColumn() メソッド、変更された最初の行を得る getFirstRow() メソッド、変更された最後の行を得る getLastRow() メソッドを使って、変更された範囲を判断しなければなりません。

TableModelEvent クラス getFirstRow() メソッド
public int getFirstRow()
TableModelEvent クラス getLastRow() メソッド
public int getLastRow()
TableModelEvent クラス getColumn() メソッド
public int getColumn()

また、テーブルに対する変更の種類は getType() メソッドから得ることができます。種類は TableModelEvent クラスのフィールド定数を用いて区別します。変更内容が削除の場合は DELETE、更新の場合は UPDATE、新しい行、または列の追加は INSERT となります。

TableModelEvent クラス getType() メソッド
public int getType()

ユーザーが自由にセルにテキストを入力できるテーブルなどの場合、TableModelListener を使ってテーブルモデルを監視することで、入力に対して何らかのアクションを発生させることができるようになるでしょう。

テーブルに設定されているテーブルモデルを取得するには、JTable クラスの getModel() メソッドを使います。逆に setModel() メソッドからテーブルに新しいモデルを設定することも可能です。

JTable クラス setModel() メソッド
public void setModel(TableModel dataModel)
JTable クラス getModel() メソッド
public TableModel getModel()

特に設定しなければ、JTable クラスはデフォルトのテーブルモデルを保有しているので、インタフェースからデータを操作することができます。

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

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

	private JTable table = new JTable(10 , 5);
	private JLabel label = new JLabel("データ変更を監視しています");
	public Test() {		
		JScrollPane pane = new JScrollPane(table);
		table.getModel().addTableModelListener(this);

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

	public void tableChanged(TableModelEvent e) {
		label.setText(
			"列=" + e.getColumn() +
			" : 最初の行=" + e.getFirstRow() +
			" : 最後の行=" + e.getLastRow()
		);
	}
}

コード1は、生成した JTable オブジェクトのデフォルトのデータモデルを取得し、そこから addTableModelListener() メソッドを使ってリスナを追加してます。このデータモデルの設定が、ユーザーの入力やプログラムの制御で変更されると登録したりスナが呼び出されます。このプログラムでは、tableChanged() メソッドが呼び出されると変更内容の情報をウィンドウ下部のラベルに表示します。

11.2.2 デフォルトのモデル

Swing がデフォルトで実装しているテーブルモデルは、インタフェースの宣言に続いて抽象クラスで基本的なメソッドを実装し、これに続いてデフォルトのクラスを提供するという基本的な設計論に基づいています。TableModel インタフェースを実装するのは javax.swing.table.AbstractTableModel クラスです。

javax.swing.table.AbstractTableModel クラス
java.lang.Object
  |
  +--javax.swing.table.AbstractTableModel
public abstract class AbstractTableModel extends Object implements TableModel, Serializable

この抽象クラスでは、TableModel インタフェースで宣言されているメソッドの実装を提供し、サブクラスの生産性を高める役割があります。リスナの管理やの呼び出しなどは、このクラスが管理してくれます。特別な事情がなければ、独自の TableModel を実装する場合もこのクラスを継承して作成します。

Swing では、TableModel のデフォルトの実装として AbstractTableModel を継承した javax.swing.table.DefaultTableModel クラスを提供しています。JTable クラスでモデルを設定しない場合はこのクラスがモデルとして利用されています。

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

データを参照したり、新しく設定するだけであれば JTable クラスのメソッドから行うことができましたが、プログラムから動的に列や行を追加したり、削除する必要がある場合は、直接データモデルを制御しなければなりません。幸い、DefaultTableModel クラスは列や行を動的に追加したり、削除することができます。

表2 DefaultTableModel クラスのコンストラクタとメソッド
コンストラクタ 解説
public DefaultTableModel() 列 0、行 0 の DefaultTableModel を構築する。
public DefaultTableModel(int rowCount, int columnCount) 行数 rowCount、列数 columnCount で DefaultTableModel を構築する。
public DefaultTableModel(Vector columnNames, int rowCount) 列名 columnNames、行数 rowCount の DefaultTableModel を構築する。
public DefaultTableModel(Object[] columnNames, int rowCount) 列名 columnNames、行数 rowCount の DefaultTableModel を構築する。
public DefaultTableModel(Vector data, Vector columnNames) 2次元の表データ data と、列名 columnNames でDefaultTableModel を構築する。
public DefaultTableModel(Object[][] data, Object[] columnNames) 2次元の表データ data と、列名 columnNames でDefaultTableModel を構築する。
メソッド
public void addColumn(Object columnName) 列名 columnName の列を追加する。
public void addColumn(Object columnName, Vector columnData) 列名 columnName、列データ columnData で列を追加する。
public void addColumn(Object columnName, Object[] columnData) 列名 columnName、列データ columnData で列を追加する。
public void addRow(Object[] rowData) 最後に行 rowData を追加する。
public void addRow(Vector rowData) 最後に行 rowData を追加する。
public void insertRow(int row, Vector rowData) 行番号 row に行 rowData を追加する。
public void insertRow(int row, Object[] rowData) 行番号 row に行 rpwData を追加する。
public void moveRow(int start, int end, int to) start から end までの行を、to の位置に移動する。
public void removeRow(int row) 行番号 row にある行を削除する。
public void setRowCount(int rowCount) モデルの行数を設定する。
public int getRowCount() データテーブル内の行の数を返す。
public void setColumnCount(int columnCount) モデルの列数を設定する。
public int getColumnCount() データテーブル内の列の数を返す。

DefaultTableModel クラスは、Object 型の配列か Vector オブジェクトを使って表データや列名をしてした列を指定することができます。addColumn() メソッドaddRow() メソッドを用いれば、プログラムが任意のタイミングで列や行を追加することができます。同様に列数や行数を指定するか、removeRow() メソッドで列や行を削除することができます。

コード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();
	}

	private DefaultTableModel model = new DefaultTableModel();
	private JButton rowButton = new JButton("行を追加");
	private JButton columnButton = new JButton("列を追加");

	public Test() {
		JPanel panel = new JPanel();
		panel.setLayout(new FlowLayout());
		panel.add(rowButton);
		panel.add(columnButton);

		rowButton.addActionListener(this);
		columnButton.addActionListener(this);

		JTable table = new JTable(model);
		JScrollPane pane = new JScrollPane(table);

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

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == columnButton) {
			model.addColumn("列:" + model.getColumnCount());
		}
		else if (e.getSource() == rowButton) {
			if (model.getColumnCount() == 0) return;
			String [] row = new String[model.getColumnCount()];
			for(int i = 0 ; i < row.length ; i++) {
				row[i] = "行:" + model.getRowCount() + "列:" + i;
			}
			model.addRow(row);
		}
	}
}
実行結果
コード2 実行結果

コード2は、ウィンドウ下部に表示されている追加ボタンを押すことで、表に列や行を任意のタイミングで追加することができるというプログラムです。プログラムでは、ボタンが押されたタイミングで DefaultTableModel オブジェクトの addColumn() メソッドや addRow() メソッドを呼び出すことで、列や行を追加しています。ただし、行は列が存在しなければ追加することができません。列を追加したとき、行がすでに存在する場合、新しく生成された列のセルは null で初期化されてます。