TableCellEditorのバリデーションとイベント
例えばJTextFieldやJComboBox等の文字列を入力することのできるコンポーネントの場合であれば、バリデーションを実施するのはフォーカスの遷移時と、データがキーボード等で入力される時である。それぞれ、FocusListener、KeyListenerのイベント通知か、Document又はDocumentFilterの実装で実現できる。
問題はJTable。このコンポーネントの場合は「セルエディタ」と呼ばれるエディタオブジェクトの参照を得た上でイベントを拾う必要がある。具体的にはTableCellEditorはCellEditorListenerに二つのイベントを通知できるので、このリスナを実装してイベントの通知を受けるのだ。
public interface CellEditorListener extends java.util.EventListener { public void editingStopped(ChangeEvent e); public void editingCanceled(ChangeEvent e); }
このリスナのメソッドはそれぞれ、セルエディタが編集を止めた際、編集を取り消した際に呼ばれるので、例えばデータ入力の完了後にバリデーションを実施したいのであれば、以下のように書けると思ったのだが、
@Override public void editingStopped(ChangeEvent e) { //tableはJTableへの参照を持つフィールド int row = this.table.getEditingRow(); int col = this.table.getEditingColumn(); boolean result = validateCell(row, col); //セルの値のバリデーション if ( !result ) { //バリデーションが失敗:エラー処理 } }
残念ながらこれは駄目だ。
getEditingRow、getEditingColumnでそれぞれ現在編集中の行、列の序数を取得しているのだが、同editingStoppedメソッドが呼ばれる際には、編集中の行と列は-1にリセットされているのである。つまり、編集が完了した際にセルエディタが編集していた行と列を知る術が無いのである。※
自分で都合の良いJTableやTableCellEditorを継承、実装すれば解決するだろうし、それを使うことを使う側に強制すれば如何様にもなるのだが、フレームワークを使うアプリケーションプログラマへの負担はできるだけ減らしたいので(それでなくとも、人の書いたフレームワークを使うことはストレスになるのだし)、それはできれば避けたい。
※追記
本来の用途ではないと思うが、JTable#getColumnModel()で取得できるカラムモデルのTableColumnModelListener、getSelectionModel()で取得できるセレクションモデルのListSelectionListenerのイベントを監視することで、それぞれ列、行の変更をトレースすることはできそうだ。
JTable table; int currentRow; int currentCol; : : table.getColumnModel().addColumnModelListener(this); table.getSelectionModel().addListSelectionListener(this); : : @Override public void columnSelectionChanged(ListSelectionEvent e) { currentCol = table.getSelectedColumn(); } @Override public void valueChanged(ListSelectionEvent e) { currentRow = table.getSelectedRow(); }
追記
コメントでateraiさんよりJTable#getSelectedRow/Columnを使ってはどうか、というアドバイスを頂いた。
早速試してみたが、editingStoppedで選択セルは動いているように見えているものの、このメソッドで取得できる値はまだリセットされていないのを確認した。これで余計なリスナ登録もしなくてよさそうだ。