從7月14日開始來上海實(shí)習(xí)已經(jīng)3個(gè)星期了,項(xiàng)目還沒正式開始,前期工作準(zhǔn)備了很多,而我主要是負(fù)責(zé)GUI這塊,工具是Swing,所以陪伴Swing也已經(jīng)快一個(gè)月的日子了.項(xiàng)目下個(gè)星期正式啟動(dòng),所以對(duì)前面的體會(huì)作個(gè)小小的總結(jié).
以前在inforsense公司的KDE平臺(tái)上已經(jīng)有一個(gè)Table Editor,點(diǎn)擊主界面上含有表格數(shù)據(jù)的節(jié)點(diǎn),可以打開表格,然后可以對(duì)各種表格進(jìn)行編輯,增刪等簡單操作,而同時(shí)這些操作也會(huì)反映到主界面上的工作流模型中.現(xiàn)在的目標(biāo)是對(duì)這個(gè)工具進(jìn)一步擴(kuò)展其功能,不僅融如Excel spreadsheet的功能(過濾,對(duì)cell進(jìn)行編輯等),還有將樹圖與表格視圖連接起來,可以進(jìn)行兩種視圖之間的拖拽(dnd),切換等.現(xiàn)在更要與化學(xué)專業(yè)結(jié)合起來,本來這款軟件是為化學(xué)家設(shè)計(jì)的,目的使他們操作起來更方便.所以還要把擴(kuò)展后的表格編輯器和Interactive Browser結(jié)合起來,做到對(duì)同一組數(shù)據(jù)的多種視圖,而且它們是同步的.比如表格中會(huì)有Structure(化學(xué)分子結(jié)構(gòu)),分子量這樣的特定的域,而點(diǎn)擊后可啟動(dòng)特定的編輯化學(xué)分子結(jié)構(gòu)的軟件進(jìn)行編輯,同時(shí)變化反映在表格數(shù)據(jù)中.
而我接觸的都是Swing,它給我的感覺雖然好象僅僅是在AWT的類前面都加上了個(gè)J,但仔細(xì)研究,里面有各種設(shè)計(jì)模式的存在,這一點(diǎn)讓我興奮不已,正好借這個(gè)機(jī)會(huì)學(xué)習(xí)設(shè)計(jì)模式.我大部分時(shí)間接觸的都是JTable和JTree.所以主要談?wù)勊麄?Swing基本是就是個(gè)MVC的設(shè)計(jì)架構(gòu),就拿JTable來說,JTable就是View的部分,而TableModel就是M的部分.下面一點(diǎn)點(diǎn)講講實(shí)現(xiàn)的細(xì)節(jié):
1.Filter(過濾器):
要在表格中實(shí)現(xiàn)過濾的功能,而實(shí)際上不影響原來的模型,可以考慮在原來的模型增加一個(gè)過濾器.它其實(shí)上也是一個(gè)TableModel(可以子類化TableModel的實(shí)現(xiàn)框架AbstractTableModel.),它把原來的TM作為自己的成員,任何實(shí)際的操作如getColumnCount(),getRowCount(),getColumnName()等都交給原來的TM來完成(調(diào)用TM的相應(yīng)方法),只是在應(yīng)該控制的地方控制一下,比如,getValueAt(i,j)就通過控制i,j來只返回過濾器想顯示的行或列的數(shù)據(jù),而具體的返回?cái)?shù)據(jù)的操作還是由TM來完成.對(duì)setValueAt(),isCellEditable()也是同樣的道理.我具體的做法就是用一個(gè)List把我想顯示的行(列)號(hào)保存下來,在getValueAt(i,j)中,i的取值范圍就是這個(gè)List了.這其實(shí)是一種Adapter模式的思想.同樣,實(shí)現(xiàn)Sort也可以用這種方式.
2)Selection:
JTable中的選擇都是由ListSelectionModel來完成的,行列都有默認(rèn)的選擇模型,訪問行的SelectionModel的方式是getSelectionModel(),訪問列的SelectionModel的方式是getColumnModel().getSelectionModel().你也可以實(shí)現(xiàn)自己的選擇模型.可以通過
getRowSelectionAllowed()和getColumnSelectionAllowed()獲取現(xiàn)在行列是否可選的信息,如果都可選,則在Cell級(jí)別是可選的.這就是為什么在行列都可選的情況下,設(shè)置i行被選中setRowSelectionInterval(i),同時(shí)設(shè)置j列被選中setColumnSelectionInterval(j),這樣只有(i,j)的Cell單元被選中得到原因.但是反過來,如果我只想使(i,j)的Cell不被選中,而僅僅靠removeColumnSelectionInterval(j)和removeRowSelectionInterval(i)是實(shí)現(xiàn)不了的.這難道是Swing的漏洞?
前面已經(jīng)講到,設(shè)置改變選擇狀態(tài)主要是通過行列SelectionModel的setSelectionInterval(),addSelectionInterval(),removeSelectionInterval()三個(gè)方式實(shí)現(xiàn)的.
3)header
表的行,列的表頭著實(shí)讓我頭痛了一陣.尤其是row header.我的row header是用一個(gè)JTable實(shí)現(xiàn)的,關(guān)鍵是要和表格同步起來.可以考慮與表格共用一個(gè)Filter,關(guān)鍵是改寫getValueAt()和getRowCount()這兩個(gè)方法.這樣表格過濾留下的行也是表頭這個(gè)JTable中所需要留下的行.而選擇的同步則是覆蓋changeSelection()這個(gè)方法實(shí)現(xiàn)的.而操作的方法就是在2)中提到的那幾個(gè)方法.設(shè)置rowHeader為表頭只需要在JScrollPane中用setRowHeaderView()指定即可,而表格最左上角的單元(行表頭的表頭)用setCorner()指定.
ColumnHeader其實(shí)在JTable中已有實(shí)現(xiàn),如果要通過單擊列頭來選擇全列的話,實(shí)現(xiàn)的方法可通過在列頭上添加一個(gè)MouseListener,然后在它的MouseClicked方法中進(jìn)行選擇的同步,其余步驟與行在changeSelection()中的類似,有一點(diǎn)值得注意,要獲取單擊的列的索引是通過getTableHeader()后得到的tableHeader.columnAtPoint(e.getPoint())得到的,這里e是MouseEvent,也就是這個(gè)單擊的動(dòng)作事件.
具體的控制代碼如下:
/**
* once click on the header, that column should be selected
*/
public void mouseClicked(MouseEvent e) {
JTableHeader header = table.getTableHeader();
TableColumnModel columns = header.getColumnModel();
if(!columns.getColumnSelectionAllowed())
return;
//get the column index being clicked
int column = header.columnAtPoint(e.getPoint());
if(column == -1)
return;
int count = table.getRowCount();
//set the entire column to be selected
if(count != 0)
table.setRowSelectionInterval(0,count-1);
ListSelectionModel selection = columns.getSelectionModel();
//if the shift modifier is pushed down, need to select multiple columns
if(e.isShiftDown()) {
int anchor = selection.getAnchorSelectionIndex();// the first index
int lead = selection.getLeadSelectionIndex();//the last index
if(anchor != -1) {
boolean old = selection.getValueIsAdjusting();
selection.setValueIsAdjusting(true);
boolean anchorSelected = selection.isSelectedIndex(anchor);
if(lead != -1) {
if(anchorSelected)
selection.removeSelectionInterval(anchor,lead);
else
selection.addSelectionInterval(anchor,lead);
}
if(anchorSelected)
selection.addSelectionInterval(anchor,column);
else
selection.removeSelectionInterval(anchor,column);
selection.setValueIsAdjusting(old);
}
else
//select single column
selection.setSelectionInterval(column,column);
}
else if(e.isControlDown()) {
if(selection.isSelectedIndex(column))
selection.removeSelectionInterval(column,column);//unselect this column
else
selection.setSelectionInterval(column,column);
}
else {
selection.setSelectionInterval(column,column);
}
}
4)dnd:
構(gòu)造一個(gè)Transferable對(duì)象,保存?zhèn)魉偷臄?shù)據(jù).而兩方分別實(shí)現(xiàn)自己的TransferHandler即可.
5)表示器和編輯器.
如果想在JTree中添加JCheckbox,其實(shí)只需要實(shí)現(xiàn)自己的CellRenderer和CellEditor,在getTreeCellRendererComponent(Object value)和setTreeCellRendererComponent(Object value)中返回或設(shè)置一個(gè)JCheckBox(value.toString())即可.value就是Tree中節(jié)點(diǎn)node的UserObject.如果你想更改樹中顯示的文字,比如在父節(jié)點(diǎn)中顯示子節(jié)點(diǎn)的數(shù)量,只需要在TreeNode類中(子類化DefaultMutableTreeNode)改寫toString()方法即可.
目前的代碼可以在"文件"中下載.