自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用BeanTableModel簡(jiǎn)化Swing

開(kāi)發(fā) 后端
本文介紹用BeanTableModel簡(jiǎn)化Swing,您可以在 src 文件夾中找到文中介紹的所有源代碼。特定于 TMF 的代碼位于 com.ibm.j2x.swing.table 包中。

讓我們來(lái)檢視Swing TMF 框架,看看它是如何讓傳統(tǒng) TableModel 過(guò)時(shí)的。設(shè)計(jì)該框架的第一部分是學(xué)習(xí) JTable 的使用 —— 開(kāi)發(fā)人員如何使用它,它顯示了什么內(nèi)容,以便了理解哪些東西可以內(nèi)化、通用化,哪些應(yīng)當(dāng)保留可配置狀態(tài),以便開(kāi)發(fā)人員配置。對(duì)于 TableModel,也要進(jìn)行同樣的思考,我必須確定哪些東西可以從代碼中移出,哪些必須留在代碼中。一旦找出這些問(wèn)題,接下來(lái)要做的就是確定能夠讓代碼足夠通用的最佳技術(shù),以便所有人都能使用它,但是,還要讓代碼具備足夠的可配置性,這也是為了讓每個(gè)人都能使用它。

該框架分成三個(gè)基本部分:一個(gè)能夠處理任何類(lèi)型數(shù)據(jù)的通用 TableModel、一個(gè)外部 XML 文件(負(fù)責(zé)對(duì)不同表中不同的表內(nèi)容進(jìn)行配置),以及模型與視圖之間的橋。

com.ibm.j2x.swing.table.BeanTableModel

BeanTableModel 是框架的第一部分。它充當(dāng)?shù)氖峭ㄓ?TableModel ,您可以用它來(lái)處理任何類(lèi)型的數(shù)據(jù)。我知道,您可能會(huì)說(shuō),“您怎么這么肯定它適用于所有的數(shù)據(jù)呢?”確實(shí),很明顯,我不能這么肯定,而且實(shí)際上,我確信有一些它不適用的例子。但是從我使用 JTables 的經(jīng)驗(yàn)來(lái)說(shuō),我愿意打賭(即使看起來(lái)我有點(diǎn)抬杠),實(shí)際使用中的 JTables,99% 都是用來(lái)顯示數(shù)據(jù)對(duì)象列表(也就是說(shuō),JavaBeans 組件的 ArrayList)?;谶@個(gè)假設(shè),我建立了一個(gè)通用表模型,它可以顯示任何數(shù)據(jù)對(duì)象列表,它就是 BeanTableModel。

BeanTableModel 大量使用了 Java 的內(nèi)省機(jī)制,來(lái)檢查 bean 中的字段,顯示正確的數(shù)據(jù)。它還使用了來(lái)自 Jakarta Commons Collections 框架的兩個(gè)類(lèi)來(lái)輔助設(shè)計(jì)。

在我深入研究代碼之前,請(qǐng)讓我解釋來(lái)自類(lèi)的幾個(gè)概念。因?yàn)槲铱梢栽?bean 上使用內(nèi)省機(jī)制,所以我需要了解 bean 本身的信息,主要是了解字段的名稱是什么。我可以通過(guò)普通的內(nèi)省機(jī)制來(lái)完成這項(xiàng)工作:我可以檢查 bean ,找出其字段。但是,對(duì)于表來(lái)說(shuō),這還不夠好,因?yàn)槎鄶?shù)開(kāi)發(fā)人員想讓他們的表按照指定順序顯示字段。除此之外,還有一項(xiàng)表需要的信息,我無(wú)法通過(guò)內(nèi)省機(jī)制從 bean 中獲得,即列名消息。所以,為了獲得正確顯示,對(duì)于表中的每個(gè)列,您需要兩條信息:列名和將要顯示的 bean 中的字段。我用鍵-值對(duì)的格式表示該信息,其中,將列名用作鍵,字段作為值。

正因?yàn)槿绱?,我在這里使用了來(lái)自 Collections 框架的適合這項(xiàng)工作的兩個(gè)類(lèi)。 BeanMap 用作實(shí)用工具類(lèi),負(fù)責(zé)處理內(nèi)省機(jī)制,它接手了內(nèi)省機(jī)制的所有繁瑣工作。普通的內(nèi)省機(jī)制開(kāi)發(fā)需要大量的 try / catch 塊,對(duì)于表來(lái)說(shuō),這是沒(méi)有必要的。 BeanMap 把 bean 作為輸入,像處理 HashMap 那樣來(lái)處理它,在這里,鍵是 bean 中的字段(例如, firstName ),值是 get 方法(例如, getFirstName() )的結(jié)果。BeanTableModel 廣泛地運(yùn)用 BeanMap ,消除了操作內(nèi)省機(jī)制的麻煩,也使得訪問(wèn) bean 中的信息更加容易。

LinkedMap 是另外一個(gè)在 BeanTableModel 中全面應(yīng)用的類(lèi)。我們還是回到為列名-字段映射所進(jìn)行的鍵-值數(shù)據(jù)設(shè)置,對(duì)于數(shù)據(jù)對(duì)象來(lái)說(shuō),很明顯應(yīng)當(dāng)選擇 HashMap。但是,HashPap 沒(méi)有保留插入的順序,對(duì)于表來(lái)說(shuō),這是非常重要的一部分,開(kāi)發(fā)人員希望在每次顯示表的時(shí)候,都能以指定的順序顯示列。這樣,插入的順序就必須保留。解決方案是 LinkedMap ,它是 LinkedList 與 HashMap 的組合,它既保留了列,也保留了列的順序信息。參見(jiàn)清單 1,可以查看我是如何用 LinkedMap 和 BeanMap 來(lái)設(shè)置表的信息的。

清單1. 用 LinkedMap 和 BeanMap 設(shè)置表信息

  1. protected List mapValues = new ArrayList();  
  2. protected LinkedMap columnInfo = new LinkedMap();    
  3. protected void initializeValues(Collection values)  
  4. {  
  5. List listValues = new ArrayList(values);  
  6. mapValues.clear();  
  7. for (Iterator i=listValues.iterator(); i.hasNext();)  
  8. {  
  9. mapValues.add(new BeanMap(i.next()));  
  10. }  

在 BeanTableModel 中比較有趣的檢查代碼實(shí)際上是通用 TableModel 的那一部分,這部分代碼擴(kuò)展了 AbstractTableModel 。將清單 2 中的代碼與您通常用來(lái)建立傳統(tǒng) TableModel 的代碼進(jìn)行比較,您可以看到一些類(lèi)似之處。

清單 2. BeanTableModel 中的通用 TableModel 代碼

  1. /**  
  2. *ReturnsthenumberofBeanMaps,thereforethenumberofJavaBeans  
  3. */  
  4. publicintgetRowCount()  
  5. {  
  6. returnmapValues.size();  
  7. }  
  8. /**  
  9. *Returnsthenumberofkey-valuepairingsinthecolumnLinkedMap  
  10. */  
  11. publicintgetColumnCount()  
  12. {  
  13. returncolumnInfo.size();  
  14. }  
  15.  
  16. /**  
  17. *GetsthekeyfromtheLinkedMapatthespecifiedindex(anda  
  18. *goodexampleofwhyaLinkedMapisneededinsteadofaHashMap)  
  19. */  
  20. publicStringgetColumnName(intcol)  
  21. {  
  22. returncolumnInfo.get(col).toString();  
  23. }  
  24. /**  
  25. *Getstheclassofthecolumn.Alotofdeveloperswonderwhat  
  26. *thisisevenusedfor.ItisusedbytheJTabletousecustom  
  27. *cellrenderers,someofwhicharebuiltintoJTablesalready  
  28. *(Boolean,Integer,Stringforexample).Ifyouwriteacustomcell  
  29. *rendereritwouldgetloadedbytheJTableforuseindisplayifthat  
  30. *specifiedclasswerereturnedhere.  
  31. *ThefunctionusestheBeanMaptogettheactualvalueoutofthe  
  32. *JavaBeananddetermineitsclass.However,becausetheBeanMap  
  33. *autoboxesthings--itconvertstheprimitivestoObjectsforyou  
  34. *(e.g.intstoIntegers)--thecodeneedstounautoboxit,sincethe  
  35. *functionmustreturnaClassObject.Thus,itrecognizesanyprimitives  
  36. *andconvertsthemtotheirrespectiveObjectclass.  
  37. */publicClassgetColumnClass(intcol)  
  38. {  
  39. BeanMapmap=(BeanMap)mapValues.get(0);  
  40. Classc=map.getType(columnInfo.getValue(col).toString());  
  41. if(c==null)  
  42. returnObject.class;  
  43. elseif(c.isPrimitive())  
  44. returnClassUtilities.convertPrimitiveToObject(c);  
  45. else  
  46. returnc;  
  47. }  
  48. /**  
  49. *TheBeanTableModelautomaticallyreturnsfalse,andifyou  
  50. *needtomakeaneditabletable,you'llhavetosubclass  
  51. *BeanTableModelandoverridethisfunction.  
  52. */  
  53. publicbooleanisCellEditable(introw,intcol)  
  54. {  
  55. returnfalse;  
  56. }  
  57. /**  
  58. *ThefunctionthatreturnsthevaluethatyouseeintheJTable.Itgets  
  59. *theBeanMapwrappingtheJavaBeanbasedontherow,itusesthe  
  60. *columnnumbertogetthefieldfromthecolumninformationLinkedMap,  
  61. *andthenusesthefieldtoretrievethevalueoutoftheBeanMap.  
  62. */  
  63. publicObjectgetValueAt(introw,intcol)  
  64. {  
  65. BeanMapmap=(BeanMap)mapValues.get(row);  
  66. returnmap.get(columnInfo.getValue(col));  
  67. }  
  68. /**  
  69. *TheoppositefunctionofthegetValueAt--itduplicatestheworkofthe  
  70. *getValueAt,butinsteadputstheObjectvalueintotheBeanMapinstead  
  71. *ofretrievingitsvalue.  
  72. */  
  73. publicvoidsetValueAt(Objectvalue,introw,intcol)  
  74. {  
  75. BeanMapmap=(BeanMap)mapValues.get(row);  
  76. map.put(columnInfo.getValue(col),value);  
  77. super.fireTableRowsUpdated(row,row);  
  78. }  
  79.  
  80. /**  
  81. *TheBeanTableModelimplementstheCollectionListenerinterface  
  82. *(1ofthe3partsoftheframework)andthuslistensforchangesinthe  
  83. *dataitismodelingandautomaticallyupdatestheJTableandthe  
  84. *modelwhenachangeoccurstothedata.  
  85. */  
  86. publicvoidcollectionChanged(CollectionEvente)  
  87. {  
  88. initializeValues((Collection)e.getSource());  
  89. super.fireTableDataChanged();  

正如您所看到的,BeanTableModel 的整個(gè) TableModel 足夠通用化,可以在任何表中使用。它充分利用了內(nèi)省機(jī)制,省去了所有特定于 bean 的編碼工作,在傳統(tǒng)的 TableModel 中,這類(lèi)編碼工作絕對(duì)是必需的 —— 同時(shí)也是完全冗余的。BeanTableModel 還可以在 TMF 框架之外使用,雖然在外面使用會(huì)喪失一些威力和靈活性。

#p#

看過(guò)這段代碼之后,您會(huì)提出兩個(gè)問(wèn)題。首先,BeanTableModel 從哪里獲得列名-字段與鍵-值配對(duì)的信息?第二,到底什么是 ObservableCollection ?這些問(wèn)題會(huì)將我們引入框架的接下來(lái)的兩個(gè)部分。這些問(wèn)題的答案以及更多的內(nèi)容,將在本文后面接下來(lái)的章節(jié)中出現(xiàn)。

Swing Castor XML 解析器

保存必需的列名-字段信息的最合理的位置位于 Java 類(lèi)之外,這樣,不需要再重新編譯 Java 代碼,就可以修改這個(gè)信息。因?yàn)殛P(guān)于列名和字段的信息是 TMF 框架中惟一明確與表有關(guān)的信息,這意味著整個(gè)表格都可以在外部進(jìn)行配置。

顯然,該解決方案會(huì)自然而然把 XML 作為配置文件的語(yǔ)言選擇。配置文件必須為多種表模型保存信息;您還需要能夠用這個(gè)文件指定每個(gè)列中的數(shù)據(jù)。配置文件還應(yīng)當(dāng)盡可能地易于閱讀,因?yàn)殚_(kāi)發(fā)人員之外的人員有可能要修改它。

這些問(wèn)題的最佳解決方案是 Castor XML 解析器。查看 Castor 實(shí)際使用的最佳方法就是查看如何在框架中使用它。

讓我們來(lái)考慮一下配置文件的目的:保存表模型和表中列的信息。 XML 文件應(yīng)當(dāng)盡可能簡(jiǎn)單地顯示這些信息。TMF 框架中的 XML 文件用清單 3 所示的格式來(lái)保存表模型信息。

清單3. TMF 配置文件示例

  1. <model> 
  2. <className>demo.hr.TableModelFreeExampleclassName> 
  3. <name>Hirename> 
  4. <column> 
  5. <name>First Namename> 
  6. <field>firstNamefield> 
  7. column> 
  8. <name>Last Namename> 
  9. <field>lastNamefield> 
  10. column> 
  11. model> 

與這個(gè)目的相反的目標(biāo)是,開(kāi)發(fā)人員必須處理的 Java 對(duì)象應(yīng)當(dāng)像 XML 文件一樣容易理解。通過(guò) Castor XML 解析器用來(lái)存儲(chǔ)http://storage.it168.com/" target=_blank>存儲(chǔ)列信息的三個(gè) Java 對(duì)象,就可以看到這一點(diǎn),這三個(gè)對(duì)象是: TableData (存儲(chǔ)文件中的所有表模型)、 TableModelData (存儲(chǔ)特定于表模型的信息)和 TableModelColumnData (存儲(chǔ)列信息)。這三個(gè)類(lèi)提供了 Java 開(kāi)發(fā)人員所需的所有包裝器,以便得到有關(guān) TableModel 的所有必要信息。

將所有這些包裝在一起所缺少的一個(gè)環(huán)節(jié)就是 映射文件,它是一個(gè) XML 文件,Castor 用它把簡(jiǎn)單的 XML 映射到簡(jiǎn)單的 Java 對(duì)象中。在完美的世界中,映射文件也應(yīng)當(dāng)很簡(jiǎn)單,但事實(shí)要比這復(fù)雜得多。良好的映射文件要使別的一切東西都保持簡(jiǎn)單;所以一般來(lái)說(shuō),映射文件越復(fù)雜,配置文件和 Java 對(duì)象就越容易處理。映射文件所做的工作顧名思義就是把 XML 對(duì)象映射到 Java 對(duì)象。清單 4 顯示了 TMF 框架使用的映射文件。

清單 4. TMF 框架使用的 Castor 映射文件

  1. xmlversionxmlversion="1.0"?> 
  2. <mapping> 
  3. <description>Amappingfileforexternalizedtablemodelsdescription> 
  4. <classnameclassname="com.ibm.j2x.swing.table.TableData"> 
  5. <map-toxmlmap-toxml="data"/> 
  6. <fieldnamefieldname="tableModelData"collection="arraylist"type=  
  7. "com.ibm.j2x.swing.table.TableModelData"> 
  8. <bind-xmlnamebind-xmlname="tableModelData"/> 
  9. field> 
  10. class> 
  11. <classnameclassname="com.ibm.j2x.swing.table.TableModelData"> 
  12. <map-toxmlmap-toxml="model"/> 
  13. <fieldnamefieldname="className"type="string"> 
  14. <bind-xmlnamebind-xmlname="className"/> 
  15. field> 
  16. <fieldnamefieldname="name"type="string"> 
  17. <bind-xmlnamebind-xmlname="name"/> 
  18. field> 
  19. <fieldnamefieldname="columns"collection="arraylist"type=  
  20. "com.ibm.j2x.swing.table.TableModelColumnData"> 
  21. <bind-xmlnamebind-xmlname="columns"/> 
  22. field> 
  23. class> 
  24. <classnameclassname="com.ibm.j2x.swing.table.TableModelColumnData"> 
  25. <map-toxmlmap-toxml="column"/> 
  26. <fieldnamefieldname="name"type="string"> 
  27. <bind-xmlnamebind-xmlname="name"/> 
  28. field> 
  29. <fieldnamefieldname="field"type="string"> 
  30. <bind-xmlnamebind-xmlname="field"/> 
  31. field> 
  32. class> 
  33. mapping> 

僅僅通過(guò)觀察這段代碼,您就可以看出,映射文件清晰地勾劃出了每個(gè)用來(lái)存儲(chǔ)表模型信息的類(lèi),定義了類(lèi)的類(lèi)型,并將 XML 文件中的名稱連接到了 Java 對(duì)象中的字段。請(qǐng)保持相同的名稱,這樣會(huì)讓事情簡(jiǎn)單、更好管理一些,但是沒(méi)必要保持名稱相同。

到現(xiàn)在為止,列名和字段信息都已外部化,可以讀入包含列信息的 Java 對(duì)象中,并且可以很容易地把信息發(fā)送給 BeanTableModel,并用它來(lái)設(shè)置列。

Swing ObservableCollection

TMF 框架的最后一個(gè)關(guān)鍵部分,就是 ObservableCollection 。您們當(dāng)中的某些人可能熟悉 ObservableCollection 的概念,它是 Java Collections 框架的一個(gè)成員,在被修改的時(shí)候,它會(huì)拋出事件,從而允許其偵聽(tīng)器根據(jù)這些事件執(zhí)行操作。雖然從來(lái)沒(méi)有將它引入 Java 語(yǔ)言的正式發(fā)行版中,但在 Internet 上,這個(gè)概念已經(jīng)有了一些第三方實(shí)現(xiàn)。就本文而言,我使用了自己的 ObservableCollection 實(shí)現(xiàn),因?yàn)榭蚣苤恍枰恍┳罨镜墓δ?。我的?shí)現(xiàn)使用了一個(gè)稱為 collectionChanged() 的方法,每次發(fā)生修改時(shí), ObservableCollection 都會(huì)在自己的偵聽(tīng)器上調(diào)用該方法。也可以將該用法稱為 Collection 類(lèi)的 Decorator(有關(guān) Collections 的 Decorator 更多信息,請(qǐng)參閱 Collections 框架的站點(diǎn)),只需要增加幾行代碼,您就可以在普通的 Collection 類(lèi)中創(chuàng)建 Collection 類(lèi)的 Observable 實(shí)例。清單 5 顯示了 ObservableCollection 用法的示例。(這只是一個(gè)示例,沒(méi)有包含在 j2x.zip 中。)

清單 5. ObservableCollection 用法示例

  1. convert a normal list to an ObservableList  
  2. ObservableList oList = CollectionUtilities.observableList(list);  
  3. // A listener could then register for events from this list by calling  
  4. oList.addCollectionListener(this);  
  5. // trigger event  
  6. oList.add(new Integer(3));  
  7. // listener receives event  
  8. public void collectionChanged(CollectionEvent e)  
  9. {  
  10. // event received here}  

ObservableCollection 有許多 TMF 框架之外的應(yīng)用程序。如果您決定采用 TMF 框架,您會(huì)發(fā)現(xiàn),在開(kāi)發(fā)代碼期間, ObservableCollection 框架有許多實(shí)際的用途。

但是,它在 TMF 框架中的用途,重點(diǎn)在于它能更好地定義視圖和模型之間的關(guān)系,當(dāng)數(shù)據(jù)發(fā)生變化時(shí),可以自動(dòng)更新視圖。您可以回想一下,這正是傳統(tǒng) TableModel 的最大限制,因?yàn)槊慨?dāng)數(shù)據(jù)發(fā)生變化時(shí),都必須用表模型的引用來(lái)更新視圖。而在 TMF 框架中使用 ObservableCollection 時(shí),當(dāng)數(shù)據(jù)發(fā)生變化時(shí),視圖會(huì)自動(dòng)更新,不需要維護(hù)一個(gè)到模型的引用。在 BeanTableModel 的 collectionChanged() 方法的實(shí)現(xiàn)中,您可以看到這一點(diǎn)。

Swing TableUtilities

在該框架中執(zhí)行的最后一步操作,是將所有內(nèi)容集成到一些實(shí)用方法中,讓 TMF 框架使用起來(lái)簡(jiǎn)單明了。這些實(shí)用方法可以在 com.ibm.j2x.swing.table.TableUtilities 類(lèi)中找到,該類(lèi)提供了您將需要的所有輔助函數(shù):

getColumnInfo() :該實(shí)用方法用 Castor XML 文件解析指定的文件,并返回指定表模型的所有列信息,返回的形式是 BeanTableModel 所需的 LinkedMap 。當(dāng)開(kāi)發(fā)人員選擇從 BeanTableModel 中派生子類(lèi)時(shí),這個(gè)方法很重要。

getTableModel() :該實(shí)用方法是建立在上面的 getColumnInfo() 方法之上,它獲得列的信息,然后把信息傳遞給 BeanTableModel,返回已經(jīng)設(shè)置好所有信息的 BeanTableModel。

setViewToModel() :該實(shí)用方法是最重要的函數(shù),也是 TMF 框架的主要吸引人的地方。它也是建立在 getTableModel() 方法之上,也有一個(gè)到 JTable 的引用(JTable 中有這個(gè)表的模型),以及一個(gè)到數(shù)據(jù)(要在表中顯示)的引用。它對(duì) JTable 上的 TableModel 進(jìn)行設(shè)置,并把數(shù)據(jù)傳遞給 TableModel,結(jié)果是:只需一行代碼,就為 JTable 完成了 TableModel 的設(shè)置。TMF 框架在該方法上得到了最佳印證,TableModel 將永遠(yuǎn)地被下面這個(gè)簡(jiǎn)單的方法所代替:

  1. TableUtilities.setViewToModel("table_config.xml", "Table", myJTable, myList);  

【編輯推薦】

  1. 淺談學(xué)習(xí)Swing組件
  2. Swing使用invokeLater()方法
  3. 淺析Swing組件的規(guī)則
  4. 詳解Swing中JTree組件的功能
  5. 簡(jiǎn)單介紹Swing外觀
責(zé)任編輯:佚名 來(lái)源: 中國(guó)IT實(shí)驗(yàn)室
相關(guān)推薦

2009-06-29 15:28:00

TableModelSwing開(kāi)發(fā)

2009-07-14 09:52:10

TableModelESwing

2009-07-14 09:28:26

JTable和TablSwing

2017-04-19 08:47:42

AsyncJavascript異步代碼

2019-08-29 08:00:00

微服務(wù)架構(gòu)服務(wù)網(wǎng)格

2011-11-04 10:22:43

Java Mail

2012-01-16 12:53:51

JavaSwing

2012-02-16 09:11:34

Swing

2009-07-15 15:35:59

Swing程序Swing性能

2011-04-02 09:34:38

2009-07-16 08:53:03

Swing任務(wù)Swing線程

2024-08-23 08:50:51

監(jiān)控Python開(kāi)發(fā)

2009-07-16 16:01:55

EventQueue

2021-03-03 23:20:51

機(jī)器學(xué)習(xí)保險(xiǎn)人工智能

2009-07-14 18:28:58

Swing入門(mén)

2009-07-15 14:29:24

構(gòu)造JListSwing

2009-07-10 10:37:50

Swing Set示例

2009-07-15 13:06:38

Swing組件

2009-07-15 11:19:17

invokeLaterSwing

2009-07-10 13:36:32

Swing容器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)