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