WisdomSoft - for your serial experiences.

11.4 セルのレンダラとエディタ

テーブルのセルに表示するデータは、セルレンダラと呼ばれる TableCellRenderer インタフェースによって描画されます。このインターフェスを実装することで、セルのデータを描画するカスタムのコンポーネントを提供できます。

11.4.1 セルを描画する

テーブルのセルの値には Object 型のオブジェクトを設定できるため、文字列だけではなく、事実上あらゆる型のオブジェクトをテーブルのセルの値として保存することができます。確かに、Object クラスはオブジェクトの文字列表現を返すため、この設計には問題ありませんが、文字列として値を表示するだけでは、ユーザーに伝達できる情報が限られてしまいます。

セルの値が Image や Icon を実装するオブジェクトであれば、文字列ではなくイメージを表示したほうがデータの意味を正確にユーザーに伝えることができます。Color オブジェクトであれば、色の名前を表示するよりも色そのものを表示したほうが、直感的な表現といえるでしょう。これを実現させるには、セルを描画するレンダラを設定します。

セルを描画するレンダラは javax.swing.table.TableCellRenderer インタフェースを実装しなければなりません。このインタフェースは、JTable クラスのセルを描画するコンポーネントを返す getTableCellRendererComponent() メソッドを宣言しています。

TableCellRenderer インタフェース getTableCellRendererComponent() メソッド
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)

table パラメータにはセルを描画しようとしている JTable オブジェクト、value には描画するセルの値が格納されています。value パラメータは null の可能性もあります。isSelected パラメータが true の場合はセルが選択されていることを、hasFocus パラメータが true の場合はフォーカスを持つことを表しています。通常、選択されているセルなどは他のセルと区別できるように表示しなければなりません。row パラメータはセルの行番号、column パラメータには列番号が格納されます。

テーブルがセルを描画するとき、必ず現在設定されているレンダラがセルごとに呼び出されます。getTableCellRendererComponent() メソッドは描画しようとしているセルの情報から適切なコンポーネントを生成し、結果を返す必要があります。

独自のオブジェクトデータを適切に描画する TableCellRenderer オブジェクトを用意することができれば、イメージなど、そのデータに適した表現のセルをテーブルに表示することができるようになります。

関係モデルのデータ型は列単位で定められるのが一般的なので、TableColumn クラスにレンダラを設定することで、列のセルを描画するレンダラを設定することができます。レンダラを列に設定するには TableColumn クラスの setCellRenderer() メソッドを用います。列に設定されている現在のレンダラは getCellRenderer() メソッドから取得することができます。

TableColumn クラス setCellRenderer() メソッド
public void setCellRenderer(TableCellRenderer cellRenderer)
TableColumn クラス getHeaderRenderer() メソッド
public TableCellRenderer getHeaderRenderer()

独自に TableCellRenderer インタフェースの実装を作成し、列に設定すれば、その列に属する行のデータを描画するとき、列に設定されているレンダラが利用されるでしょう。

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

public class Test {
	public static void main(String args[]) {
		JTable table = new JTable(3 , 1);

		table.getColumnModel().getColumn(0).setCellRenderer(new IconCellRenderer());
		Icon icon = new ImageIcon("icon1.jpg");
		table.setValueAt(icon , 0 , 0);
		table.setRowHeight(0 , icon.getIconHeight());

		table.setValueAt(null , 1 , 0);
		table.setValueAt("Kitty on your lap" , 2 , 0);

		JScrollPane pane = new JScrollPane(table);

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

class IconCellRenderer implements TableCellRenderer {
	public Component getTableCellRendererComponent(
			JTable table,  Object value, boolean isSelected,
			boolean hasFocus, int row, int column) {
		JLabel label = new JLabel();

		if (value == null) label.setText("value is Null");
		else if (value instanceof Icon) {
			Icon icon = (Icon)value;
			label.setIcon(icon);
		}
		else label.setText("No Image:" + value.toString());

		if (isSelected) {
			label.setOpaque(true);
			label.setBackground(new Color(0xFFEEEE));
		}
		return label;
	}
}
実行結果
コード1 実行結果

コード1は、アイコンを表示することができる簡単なレンダラの実装例です。このプログラムは、フォーカスや編集に対応していないことから不十分なレンダラではありますが、レンダラの拡張方法を学習するには十分でしょう。TableCellRenderer インタフェースを実装する IconCellRenderer クラスでは、セルの値が Icon 型にキャスト可能であれば、そのアイコンを表示する JLabel オブジェクトを返すというものです。

実行結果を見て確認できるように、このプログラムのテーブルはアイコンイメージに対応しています。

11.4.2 セルの編集

ツリーを編集するコンポーネントを、ツリーのセルエディタを用いて拡張可能だったように、テーブルの場合もテーブル用のセルエディタを用いて独自の実装を実現することができます。

コード1のように、文字列以外のデータを表示するセルを実装する場合、これに連動して適切なセルエディタを設定しなければなりません。コード1はレンダラを独自に拡張していますが、エディタはデフォルトの設定なので、セルの編集は文字列として行われてしまいます。イメージを表示するセルであれば、当然イメージを選択するようなセルエディタを実装するべきでしょう。

JTable クラスのセルエディタは javax.swing.table.TableCellEditor インタフェースを実装しなければなりません。 このインタフェースは CellEditor インタフェースを継承しているため、エディタの実装方法は TreeCellEditor の例とほぼ同じと考えてよいでしょう。TableCellEditor インタフェースは、編集のためのコンポーネントを返す getTableCellEditorComponent() メソッドを宣言しています。

TableCellEditor インタフェース getTableCellEditorComponent() メソッド
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)

table にはデータの編集を要求している JTable オブジェクトが、value には編集されるセルの値が指定されます。isSelected が true の場合はセルが選択されているため、ハイライトで表示されていることを表します。row にはセルの行番号、column には列番号が格納されています。

セルレンダラを用意することができれば、TableColumn クラスから列に対してエディタを設定することができます。エディタを設定するには TableColumn クラスのセルレンダラとセルエディタを設定するコンストラクタか setCellEditor() メソッドを用います。列のエディタは getCellEditor() メソッドから得ることができます。

TableColumn クラス setCellEditor() メソッド
public void setCellEditor(TableCellEditor cellEditor)
TableColumn クラス getCellEditor() メソッド
public TableCellEditor getCellEditor()

テーブルのセルエディタの実装も、ツリーの時と同様に CellEditor インタフェースを実装する AbstractCellEditor クラスを継承するとスマートに開発することができます。

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

public class Test {
	public static void main(String args[]) {
		JTable table = new JTable(3 , 1);
		TableColumn column = table.getColumnModel().getColumn(0);
		column.setCellRenderer(new IconCellRenderer());
		column.setCellEditor(new IconCellEditor());
		
		table.setRowHeight(80);
		table.setValueAt(new ImageIcon("icon1.jpg") , 0 , 0);

		JScrollPane pane = new JScrollPane(table);

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

class IconCellRenderer implements TableCellRenderer {
	public Component getTableCellRendererComponent(
			JTable table,  Object value, boolean isSelected,
			boolean hasFocus, int row, int column) {
		JLabel label = new JLabel();

		if (value == null) label.setText("value is Null");
		else if (value instanceof Icon) {
			Icon icon = (Icon)value;
			label.setIcon(icon);
		}
		else label.setText("No Image:" + value.toString());

		if (isSelected) {
			label.setOpaque(true);
			label.setBackground(new Color(0xFFEEEE));
		}
		return label;
	}
}
class IconCellEditor extends AbstractCellEditor implements TableCellEditor , ActionListener {
	JFileChooser filec = new JFileChooser();
	JButton button = new JButton();

	public IconCellEditor() {
		button.addActionListener(this);
	}

	public Component getTableCellEditorComponent(
			JTable table , Object value, boolean isSelected,
			int row , int column) {

		if (value == null) button.setText("value is Null");
		else button.setText(value.toString());

		if (value instanceof Icon) button.setIcon((Icon)value);
		else button.setIcon(null);

		return button;
	}
	public void actionPerformed(ActionEvent e) {
		if (JFileChooser.APPROVE_OPTION == filec.showOpenDialog(button)) {
			stopCellEditing();
		}
	}
	public Object getCellEditorValue() {
		if (filec.getSelectedFile() == null) return null;
		Icon icon = new ImageIcon(filec.getSelectedFile().getPath());
		return icon;
	}
}
実行結果
コード2 実行結果

コード2は、コード1のセルレンダラの拡張に加えて、独自のテーブル用セルエディタを定義して設定しています。セルをクリックするとセルエディタが起動され、セルにアイコン付きのボタンが表示されます。ボタンを押すと、アイコンを選択するためのファイル選択ダイアログが開くので、ディスクから画像ファイルを選択すると、その画像ファイルが新しいセルのアイコンデータとして利用されます。