在文檔中對JTable 解釋是:用來顯示和編輯規則的二維單元表。
也就是說JTable的類型定義決定了它是一個規則的二維單元表,但是對于二維單元表內單元格的顯示和編輯組件的選擇又是極其靈活的.
有如下兩個接口:
TableCellEditor
Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column);
TableCellRenderer
Component getTableCellRendererComponent(JTable table, Object
value, boolean isSelected, boolean hasFocus, int row, int column)
所有實現這兩個接口的對象都可以配置到JTable.JTable自身定義了單元格的狀態:表現或編輯.
當JTabel處于表現狀態時它會調用
ableCellRenderer的Component getTableCellRendererComponent(JTable
table, Object value, boolean isSelected, boolean hasFocus, int row, int
column)請求表現組件.
當JTabel處于編輯狀態時它會調用TableCellEditor的Component
getTableCellEditorComponent(JTable table, Object value, boolean
isSelected, int row, int column);請求編輯組件.
這樣我們實現TableCellEditor,TableCellRenderer這兩個接口就可以靈活的控制表格單元格的編輯和顯示.
當然為了方便swing已經定義了這兩個接口的默認實現,如DefaultCellEditor,DefaultTableCellRenderer.
雖然我們有了靈活控制編輯和表現的接口,但是如何控制編輯和表現狀態的轉換呢!
首先單元格可不可以編輯由表格的模型控制。因為通常可不可以編輯取決于數據內容,所以將它定義在模型中是合理的!
TableModel 接口有如下一個方法:
boolean isCellEditable(int rowIndex, int columnIndex) ;
JTabel會通過他來確定可不可以編輯。
在可編輯的情況下,通過JTabel的 boolean editCellAt(int row, int column) ,可以啟動單元格進入編輯狀態。那么如何控制從編輯狀態退出呢?
這個稍微復雜一點,因為從編輯狀態退出還很可能意味著要將編輯器的內容放入表格模型。
這里有一個接口
public interface CellEditor{
void addCellEditorListener(CellEditorListener l)
Object getCellEditorValue()
void removeCellEditorListener(CellEditorListener l)
void cancelCellEditing()
boolean stopCellEditing()
.................
}
TableCellEditor繼承自它,也就是說swing對單元格編輯器作了更抽象的定義。因為不僅僅是表格需要編輯。這里有個監聽器的注冊方法,看一下CellEditorListener的定義
public interface CellEditorListener extends EventListener
{
void editingCanceled(ChangeEvent e)
void editingStopped(ChangeEvent e)
}
很顯然編輯狀態結束的關鍵在CellEditor上。你可以主動發出事件通知CellEditorListener說結束了。也可以由外部調用
void cancelCellEditing()
boolean stopCellEditing()
觸發。而CellEditorListener得到通知后通常可以調用
Object getCellEditorValue()來獲得編輯器的值。例如JTable 就實現了CellEditorListener,當你將你的TableCellEditor設置到JTable時,JTable就會注冊上去。
有了靈活控制編輯和表現的接口,也有了控制編輯和表現狀態的轉換機制.不過完全從這些接口開始構建一套自己的實現,也是很累的。顯然swing已經有一套比較通用的實現。
首先看一下對于控制編輯和表現狀態的轉換,JTable有自己的一套定義,在外部事件觸發下單元格編輯和表現狀態的轉換,比如在單元格上雙擊
會使得該單元格進入編輯狀態,當編輯狀態的單元格失去焦點時,該單元格離開編輯狀態進入表現狀態。在同一時刻只有一個單元格可以進入編輯狀態等。
再來看一下DefaultTableCellRenderer和DefaultCellEditor
DefaultTableCellRenderer繼承JLabel實現TableCellRenderer 接口。也就是說表格通常的單元格表現都是JLabel組件。
這個實現其實有一個巧妙之處,在實現
Component getTableCellRendererComponent(JTable table, Object
value, boolean isSelected, boolean hasFocus, int row, int column) {
。。。。。。。。。。。
return this;
}這個方法中
最后一句return this;表明不管單元格有多少,JLabel對象一直只有一個,只是針對不同的單元格JLabel對象的狀態不一樣,但實例只有一個,儉省很多資源。
DefaultCellEditor繼承AbstractCellEditor,而AbstractCellEditor
實現了TableCellEditor。這里順便講一下,swing對很多接口的實現都有這兩個層次,對于default的都是一個可用的實現,而
Abstract多是抽象類,他只實現了接口的一部分,而這一部分通常都是很通用的。如果覺得default不能滿足要求,而覺得實現整個接口又麻煩,
Abstract的就很有用。
DefaultCellEditor有三個構造函數:
DefaultCellEditor(JCheckBox checkBox)
DefaultCellEditor(JComboBox comboBox)
DefaultCellEditor(JTextField textField)
因為編輯不像表現那么單純,通常使用編輯器的對象(如JTable)都要獲得編輯器的值,然而不同編輯器的對外接口又是非常繁多的,所以CellEditor有這樣一個方法
Object getCellEditorValue()
;也就是說使用編輯器的對象(如JTable)不管實際編輯器有多繁雜,它就只通過Object
getCellEditorValue()這個方法獲取值。那么當你要把你的編輯器用到比如JTable上,那么就要考慮如何將你的編輯器接口適配到
JTable期望的Object getCellEditorValue() 上。
DefaultCellEditor的構造函數就是一個提供了可以將三種編輯器進行適配。其實這是一種適配器模式。也就是DefaultCellEditor可以適配三種編輯器。
忽略了一個很重要的問題,就是如何將我們的編輯器或表現器注冊到JTable上?
這個看是很簡單的問題,其實也并非想象當中那么簡單。
先看一下JTable提供的明顯的注冊接口
void setCellEditor(TableCellEditor anEditor) ;
void setDefaultEditor(Class<?> columnClass, TableCellEditor editor)
void setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer)
第一個接口很顯然整個表格單元格的編輯器將由這個注冊的編輯器接管。
后面兩個是基于數據類型進行配置的,也就是說這種數據類型的單元格編輯器將由注冊的編輯器接管。
那么如何決定數據類型呢?看表格模型TableModel里有一個方法
Class<?> getColumnClass(int columnIndex);
很明顯和是否可編輯一樣,數據類型由模型決定。
除此之外還有另外的注冊方法,那就是表格本身也是有其他元素組成,在JTable中下一級元素是列,TableColumn。它有這兩個方法
void setCellEditor(TableCellEditor cellEditor)
void setCellRenderer(TableCellRenderer cellRenderer)
可以將編輯器和表現器直接注冊在列上,那么這一列的編輯或表現將由你注冊的東西接管。