用 TableModel Free 框架简化 Swing 开发——JTable
用 TableModel Free 框架簡(jiǎn)化 Swing 開發(fā)
從 TableModel 的負(fù)擔(dān)中解脫出來
Michael Abernethy, 軟件工程師 II, EMC
簡(jiǎn)介:?本文將介紹 TableModel Free(TMF) 框架,該框架利用 SWing JTable 消除了對(duì) TableModel 的需要。通過將所有特定于表的數(shù)據(jù)移到編譯好的代碼之外,并將它們放在可配置的 XML 文件中,TMF 框架使 JTable 具備了更強(qiáng)的可配置性。框架開發(fā)人員和 Java UI 的熱心支持者 Michael Abernethy 將帶您遍歷整個(gè) TMF 框架,幫助您把 TableModel 的大小從數(shù)百行代碼減少到只有區(qū)區(qū)一行代碼,把管理工作變成小菜一碟。
標(biāo)記本文!
發(fā)布日期:?2004 年 10 月 12 日?
級(jí)別:?初級(jí)?
訪問情況 :?2606 次瀏覽?
評(píng)論:?0?(查看?|?添加評(píng)論?- 登錄)
為本文評(píng)分
“Java? Desktop 的再介紹”強(qiáng)調(diào)了今年的 JavaOne 大會(huì)。對(duì)于那些抱怨 Swing 太慢、太難使用、界面太難看的開發(fā)人員來說,Swing 和 GUI 開發(fā)所做的更新努力,并沒有帶來什么受人歡迎的好消息。如果您最近沒有用過 Swing,那么您會(huì)很高興聽到其中的許多問題已經(jīng)得到解決。Swing 被重新設(shè)計(jì),它能執(zhí)行得更好,并能更好地利用 Java 2D API。Swing 的開發(fā)者在 1.4 版甚至最新發(fā)布的 5.0 版中提高了外觀支持。Swing 從沒像現(xiàn)在這么好過。
Swing 社區(qū)現(xiàn)在需要的是能夠讓 GUI 開發(fā)變成一個(gè)更加順利、更加容易的過程的工具。而這正是本文的目標(biāo)所在。
本文將介紹 TableModel Free(TMF)框架,這是一個(gè) GUI 開發(fā)包,它解除了為每個(gè) JTable 建立 TableModel 的需要(在這篇文章中,我將 TableModel 稱為?傳統(tǒng) TableModel,以便將它們與我為該框架引入的新結(jié)構(gòu)區(qū)分開來);在處理過程中,能夠使您的 JTable 具備更高的可配置性和可維護(hù)性。
如果以前曾經(jīng)用過 JTable,那么您可能也同時(shí)被迫使用了 TableModel。您可能還注意到,每個(gè) TableModel 中的所有代碼,與其他 TableModel 中的代碼幾乎是一樣的,在編譯的 Java 類中,有差異的代碼實(shí)際上是不存在的。本文將分析 TableModel/JTable 目前的設(shè)計(jì)方法,說明這種設(shè)計(jì)的不足,展示為什么它沒有實(shí)現(xiàn)模型-視圖-控制器(MVC)模式的真正目標(biāo)。您將看到框架和構(gòu)成 TMF 框架的代碼 —— 我以前編寫的代碼與最常用的開放源代碼項(xiàng)目的組合。使用該框架,開發(fā)人員可以把 TableModel 的大小從數(shù)百行代碼減少到只有區(qū)區(qū)一行,并把重要的表信息放在外部 XML 文件中。在讀完本文之后,只使用如下所示的一行代碼,您就可以管理您的 JTable 數(shù)據(jù):
| TableUtilities.setViewToModel("tableconfig.xml", "My Table", myJTable, CollectionUtilities.observableList(myData)); |
TMF 框架中的開放源代碼包
讓我們簡(jiǎn)要了解一下我用來協(xié)助構(gòu)建 TMF 框架的兩個(gè)開放源代碼包。通過組合開放源代碼包,并加入一些創(chuàng)新,您可以開發(fā)出非常強(qiáng)大的程序。請(qǐng)參閱?參考資料中這些代碼包的鏈接。
Apache Jakarta Commons Collections?
最知名也最常用的開放源代碼項(xiàng)目,可能就是 Apache 保護(hù)下的那些項(xiàng)目。Apache Jakarta 項(xiàng)目包含許多特定于 Java 的擴(kuò)展和包,其中之一就是 Commons 項(xiàng)目。它的目標(biāo),正如在 Web 站點(diǎn)上所說那樣,是“建立并維護(hù)可重用的 Java 組件”。其中列出了太多組件,而本文使用的只是 Jakarta Commons Collections 框架,它提供了大量對(duì) Java Collections 框架的擴(kuò)展。
Castor?
Castor 是一個(gè)被廣泛采用的 XML 解析工具,雖然已經(jīng)有了許多 XML 解析器,但是 Castor 的強(qiáng)大使得它非常流行。使用 Castor,可以非常容易地在簡(jiǎn)單的 XML 文檔中建立復(fù)雜的 Java 對(duì)象。它把從 XML 到 Java 對(duì)象的許多繁重的轉(zhuǎn)換工作從 XML 文件中提出取來,把復(fù)雜的轉(zhuǎn)換工作放到一個(gè)映射文件中,該映射文件充當(dāng)著 Java 與 XML 的中介。由于有一些非常能干的開發(fā)人員處理了映射文件,所以即使是最困難的轉(zhuǎn)換,也可以在 XML 中進(jìn)行,XML 文件非常便于閱讀和更新。所以,Castor 工具在解析用作配置文件的 XML 文件(由最終用戶更新,所以必須易于閱讀)時(shí),最為強(qiáng)大。
JTable 和 TableModel 存在的 MVC 問題
MVC 已經(jīng)成為非常流行的 UI 設(shè)計(jì)模式,因?yàn)樗褬I(yè)務(wù)邏輯清晰地從數(shù)據(jù)的視圖中分離了出來。Struts 是 MVC 在 Web 上應(yīng)用的一個(gè)非常好的例子。最初,Swing 最大的一個(gè)賣點(diǎn)是它采用了 MVC,將視圖從模型中分離了出來,代碼背后的想法是:代碼的模塊化程度足夠高,所以,不用修改模型中的任何代碼,就可以分離出視圖。我想,任何用過 JTables 和 TableModels 的人都會(huì)發(fā)笑,告訴您這是絕對(duì)不可能的。使用 MVC 設(shè)計(jì)模式的理想情況是,在開發(fā)人員用 JList 或 JComboBox 替換 JTable 時(shí),可以不用修改表示數(shù)據(jù)的模式中的代碼。但是,在 Swing 中做不到這點(diǎn)。Swing 使得把 JTable、 JList 和 JComboBox 熱交換到應(yīng)用程序中成為不可能,即使所有這三個(gè)組件都是用來為相同的數(shù)據(jù)模型提供視圖。對(duì)于 Swing 中的 MVC 設(shè)計(jì),這是一個(gè)嚴(yán)重的不足。如果您想為 JTable 交換 JList,就必須重寫視圖背后的全部代碼,才能實(shí)現(xiàn)該交換。
JTable/TableModel 的另一個(gè) MVC 缺陷是:模型變化的時(shí)候,視圖不會(huì)更新自身。開發(fā)人員必須保持對(duì)模型的引用,并調(diào)用一個(gè)函數(shù),這樣模型才會(huì)告訴視圖對(duì)自身進(jìn)行更新;但是,理想的情況應(yīng)當(dāng)是:不需要任何額外的代碼,就能實(shí)現(xiàn)自動(dòng)更新。
最后,JTable 和 TableModel 組件設(shè)計(jì)的問題是,它們彼此之間纏雜得過于密切。如果您修改了 JTable 中的代碼,那么您需要確保您沒有破壞負(fù)責(zé)處理的 TableModel,反之亦然。對(duì)于一個(gè)被認(rèn)為是在模塊化基礎(chǔ)上建立的設(shè)計(jì)模式來說,目前的實(shí)現(xiàn)顯然是一種存在過多依賴關(guān)系的設(shè)計(jì)。
TMF 框架更好地遵循了 MVC 的目標(biāo),它把 JTable 中視圖和模型的工作更加清晰地分離開來。雖然它還沒有達(dá)到讓組件能夠熱切換的更高目標(biāo),但是它已經(jīng)在正確方向上邁出了一步。
回頁(yè)首
框架簡(jiǎn)介
讓我們來檢視 TMF 框架,看看它是如何讓傳統(tǒng) TableModel 過時(shí)的。設(shè)計(jì)該框架的第一部分是學(xué)習(xí) JTable 的使用 —— 開發(fā)人員如何使用它,它顯示了什么內(nèi)容,以便了理解哪些東西可以內(nèi)化、通用化,哪些應(yīng)當(dāng)保留可配置狀態(tài),以便開發(fā)人員配置。對(duì)于 TableModel,也要進(jìn)行同樣的思考,我必須確定哪些東西可以從代碼中移出,哪些必須留在代碼中。一旦找出這些問題,接下來要做的就是確定能夠讓代碼足夠通用的最佳技術(shù),以便所有人都能使用它,但是,還要讓代碼具備足夠的可配置性,這也是為了讓每個(gè)人都能使用它。
該框架分成三個(gè)基本部分:一個(gè)能夠處理任何類型數(shù)據(jù)的通用 TableModel、一個(gè)外部 XML 文件(負(fù)責(zé)對(duì)不同表中不同的表內(nèi)容進(jìn)行配置),以及模型與視圖之間的橋。
請(qǐng)單擊?Code圖標(biāo)(或者參閱?下載部分),下載在本文中討論的源代碼、第三方 JAR 文件和 Javadoc。在本文中,您可以在 src 文件夾中找到文中介紹的所有源代碼。特定于 TMF 的代碼位于?com.ibm.j2x.swing.table?包中。
com.ibm.j2x.swing.table.BeanTableModel
BeanTableModel 是框架的第一部分。它充當(dāng)?shù)氖峭ㄓ?TableModel ,您可以用它來處理任何類型的數(shù)據(jù)。我知道,您可能會(huì)說,“您怎么這么肯定它適用于所有的數(shù)據(jù)呢?”確實(shí),很明顯,我不能這么肯定,而且實(shí)際上,我確信有一些它不適用的例子。但是從我使用 JTables 的經(jīng)驗(yàn)來說,我愿意打賭(即使看起來我有點(diǎn)抬杠),實(shí)際使用中的 JTables,99% 都是用來顯示數(shù)據(jù)對(duì)象列表(也就是說,JavaBeans 組件的 ArrayList)。基于這個(gè)假設(shè),我建立了一個(gè)通用表模型,它可以顯示任何數(shù)據(jù)對(duì)象列表,它就是BeanTableModel。
BeanTableModel 大量使用了 Java 的內(nèi)省機(jī)制,來檢查 bean 中的字段,顯示正確的數(shù)據(jù)。它還使用了來自 Jakarta Commons Collections 框架(請(qǐng)參閱?側(cè)欄,以了解更多信息)的兩個(gè)類來輔助設(shè)計(jì)。
在我深入研究代碼之前,請(qǐng)讓我解釋來自類的幾個(gè)概念。因?yàn)槲铱梢栽?bean 上使用內(nèi)省機(jī)制,所以我需要了解 bean 本身的信息,主要是了解字段的名稱是什么。我可以通過普通的內(nèi)省機(jī)制來完成這項(xiàng)工作:我可以檢查 bean ,找出其字段。但是,對(duì)于表來說,這還不夠好,因?yàn)槎鄶?shù)開發(fā)人員想讓他們的表按照指定順序顯示字段。除此之外,還有一項(xiàng)表需要的信息,我無法通過內(nèi)省機(jī)制從 bean 中獲得,即列名消息。所以,為了獲得正確顯示,對(duì)于表中的每個(gè)列,您需要兩條信息:列名和將要顯示的 bean 中的字段。我用鍵-值對(duì)的格式表示該信息,其中,將列名用作鍵,字段作為值。
正因?yàn)槿绱?#xff0c;我在這里使用了來自 Collections 框架的適合這項(xiàng)工作的兩個(gè)類。?BeanMap?用作實(shí)用工具類,負(fù)責(zé)處理內(nèi)省機(jī)制,它接手了內(nèi)省機(jī)制的所有繁瑣工作。普通的內(nèi)省機(jī)制開發(fā)需要大量的?try?/?catch?塊,對(duì)于表來說,這是沒有必要的。?BeanMap?把 bean 作為輸入,像處理 HashMap 那樣來處理它,在這里,鍵是 bean 中的字段(例如,?firstName?),值是 get 方法(例如,getFirstName()?)的結(jié)果。BeanTableModel 廣泛地運(yùn)用?BeanMap?,消除了操作內(nèi)省機(jī)制的麻煩,也使得訪問 bean 中的信息更加容易。
LinkedMap?是另外一個(gè)在 BeanTableModel 中全面應(yīng)用的類。我們還是回到為列名-字段映射所進(jìn)行的鍵-值數(shù)據(jù)設(shè)置,對(duì)于數(shù)據(jù)對(duì)象來說,很明顯應(yīng)當(dāng)選擇 HashMap。但是,HashPap 沒有保留插入的順序,對(duì)于表來說,這是非常重要的一部分,開發(fā)人員希望在每次顯示表的時(shí)候,都能以指定的順序顯示列。這樣,插入的順序就必須保留。解決方案是?LinkedMap?,它是?LinkedList?與?HashMap的組合,它既保留了列,也保留了列的順序信息。參見清單 1,可以查看我是如何用?LinkedMap?和?BeanMap?來設(shè)置表的信息的。
清單1. 用 LinkedMap 和 BeanMap 設(shè)置表信息
| protected List mapValues = new ArrayList();protected LinkedMap columnInfo = new LinkedMap(); protected void initializeValues(Collection values){List listValues = new ArrayList(values);mapValues.clear();for (Iterator i=listValues.iterator(); i.hasNext();){mapValues.add(new BeanMap(i.next()));}} |
在 BeanTableModel 中比較有趣的檢查代碼實(shí)際上是通用 TableModel 的那一部分,這部分代碼擴(kuò)展了?AbstractTableModel?。將清單 2 中的代碼與您通常用來建立傳統(tǒng) TableModel 的代碼進(jìn)行比較,您可以看到一些類似之處。
清單 2. BeanTableModel 中的通用 TableModel 代碼?
| /*** Returns the number of BeanMaps, therefore the number of JavaBeans*/ public int getRowCount() {return mapValues.size();}/*** Returns the number of key-value pairings in the column LinkedMap*/ public int getColumnCount() {return columnInfo.size();}/*** Gets the key from the LinkedMap at the specified index (and a * good example of why a LinkedMap is needed instead of a HashMap)*/ public String getColumnName(int col) {return columnInfo.get(col).toString();}/*** Gets the class of the column. A lot of developers wonder what * this is even used for. It is used by the JTable to use custom * cell renderers, some of which are built into JTables already * (Boolean, Integer, String for example). If you write a custom cell * renderer it would get loaded by the JTable for use in display if that * specified class were returned here.* The function uses the BeanMap to get the actual value out of the * JavaBean and determine its class. However, because the BeanMap * autoboxes things -- it converts the primitives to Objects for you * (e.g. ints to Integers) -- the code needs to unautobox it, since the * function must return a Class Object. Thus, it recognizes any primitives * and converts them to their respective Object class.*/ public Class getColumnClass(int col) {BeanMap map = (BeanMap)mapValues.get(0);Class c = map.getType(columnInfo.getValue(col).toString());if (c == null)return Object.class;else if (c.isPrimitive())return ClassUtilities.convertPrimitiveToObject(c);elsereturn c;}/*** The BeanTableModel automatically returns false, and if you * need to make an editable table, you'll have to subclass * BeanTableModel and override this function.*/ public boolean isCellEditable(int row, int col) {return false;}/*** The function that returns the value that you see in the JTable. It gets * the BeanMap wrapping the JavaBean based on the row, it uses the * column number to get the field from the column information LinkedMap, * and then uses the field to retrieve the value out of the BeanMap. */public Object getValueAt(int row, int col) {BeanMap map = (BeanMap)mapValues.get(row);return map.get(columnInfo.getValue(col));}/*** The opposite function of the getValueAt -- it duplicates the work of the * getValueAt, but instead puts the Object value into the BeanMap instead * of retrieving its value.*/public void setValueAt(Object value, int row, int col) {BeanMap map = (BeanMap)mapValues.get(row);map.put(columnInfo.getValue(col), value);super.fireTableRowsUpdated(row, row);}/*** The BeanTableModel implements the CollectionListener interface * (1 of the 3 parts of the framework) and thus listens for changes in the * data it is modeling and automatically updates the JTable and the * model when a change occurs to the data.*/ public void collectionChanged(CollectionEvent e){initializeValues((Collection)e.getSource());super.fireTableDataChanged();} |
正如您所看到的,BeanTableModel 的整個(gè) TableModel 足夠通用化,可以在任何表中使用。它充分利用了內(nèi)省機(jī)制,省去了所有特定于 bean 的編碼工作,在傳統(tǒng)的 TableModel 中,這類編碼工作絕對(duì)是必需的 —— 同時(shí)也是完全冗余的。BeanTableModel 還可以在 TMF 框架之外使用,雖然在外面使用會(huì)喪失一些威力和靈活性。
看過這段代碼之后,您會(huì)提出兩個(gè)問題。首先,BeanTableModel 從哪里獲得列名-字段與鍵-值配對(duì)的信息?第二,到底什么是ObservableCollection??這些問題會(huì)將我們引入框架的接下來的兩個(gè)部分。這些問題的答案以及更多的內(nèi)容,將在本文后面接下來的章節(jié)中出現(xiàn)。
Castor XML 解析器
保存必需的列名-字段信息的最合理的位置位于 Java 類之外,這樣,不需要再重新編譯 Java 代碼,就可以修改這個(gè)信息。因?yàn)殛P(guān)于列名和字段的信息是 TMF 框架中惟一明確與表有關(guān)的信息,這意味著整個(gè)表格都可以在外部進(jìn)行配置。
顯然,該解決方案會(huì)自然而然把 XML 作為配置文件的語言選擇。配置文件必須為多種表模型保存信息;您還需要能夠用這個(gè)文件指定每個(gè)列中的數(shù)據(jù)。配置文件還應(yīng)當(dāng)盡可能地易于閱讀,因?yàn)殚_發(fā)人員之外的人員有可能要修改它。
這些問題的最佳解決方案是 Castor XML 解析器(有關(guān)的更多信息,請(qǐng)參閱?側(cè)欄)。查看 Castor 實(shí)際使用的最佳方法就是查看如何在框架中使用它。
讓我們來考慮一下配置文件的目的:保存表模型和表中列的信息。 XML 文件應(yīng)當(dāng)盡可能簡(jiǎn)單地顯示這些信息。TMF 框架中的 XML 文件用清單 3 所示的格式來保存表模型信息。
清單3. TMF 配置文件示例
| <model><className>demo.hr.TableModelFreeExample</className><name>Hire</name><column><name>First Name</name><field>firstName</field></column><column><name>Last Name</name><field>lastName</field></column></model> |
與這個(gè)目的相反的目標(biāo)是,開發(fā)人員必須處理的 Java 對(duì)象應(yīng)當(dāng)像 XML 文件一樣容易理解。通過 Castor XML 解析器用來存儲(chǔ)列信息的三個(gè) Java 對(duì)象,就可以看到這一點(diǎn),這三個(gè)對(duì)象是:?TableData?(存儲(chǔ)文件中的所有表模型)、?TableModelData?(存儲(chǔ)特定于表模型的信息)和?TableModelColumnData?(存儲(chǔ)列信息)。這三個(gè)類提供了 Java 開發(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)單;所以一般來說,映射文件越復(fù)雜,配置文件和 Java 對(duì)象就越容易處理。映射文件所做的工作顧名思義就是把 XML 對(duì)象映射到 Java 對(duì)象。清單 4 顯示了 TMF 框架使用的映射文件。
清單 4. TMF 框架使用的 Castor 映射文件
| <?xml version="1.0"?><mapping><description>A mapping file for externalized table models</description><class name="com.ibm.j2x.swing.table.TableData"><map-to xml="data"/><field name="tableModelData" collection="arraylist" type="com.ibm.j2x.swing.table.TableModelData"><bind-xml name="tableModelData"/></field></class><class name="com.ibm.j2x.swing.table.TableModelData"><map-to xml="model"/><field name="className" type="string"><bind-xml name="className"/></field><field name="name" type="string"><bind-xml name="name"/></field><field name="columns" collection="arraylist" type="com.ibm.j2x.swing.table.TableModelColumnData"><bind-xml name="columns"/></field></class><class name="com.ibm.j2x.swing.table.TableModelColumnData"><map-to xml="column"/><field name="name" type="string"><bind-xml name="name"/></field><field name="field" type="string"><bind-xml name="field"/></field> </class></mapping> |
僅僅通過觀察這段代碼,您就可以看出,映射文件清晰地勾劃出了每個(gè)用來存儲(chǔ)表模型信息的類,定義了類的類型,并將 XML 文件中的名稱連接到了 Java 對(duì)象中的字段。請(qǐng)保持相同的名稱,這樣會(huì)讓事情簡(jiǎn)單、更好管理一些,但是沒必要保持名稱相同。從?參考資料中,可以了解有關(guān) Castor XML 映射的更多信息。
到現(xiàn)在為止,列名和字段信息都已外部化,可以讀入包含列信息的 Java 對(duì)象中,并且可以很容易地把信息發(fā)送給 BeanTableModel,并用它來設(shè)置列。
ObservableCollection
TMF 框架的最后一個(gè)關(guān)鍵部分,就是?ObservableCollection?。您們當(dāng)中的某些人可能熟悉?ObservableCollection?的概念,它是 Java Collections 框架的一個(gè)成員,在被修改的時(shí)候,它會(huì)拋出事件,從而允許其偵聽器根據(jù)這些事件執(zhí)行操作。雖然從來沒有將它引入 Java 語言的正式發(fā)行版中,但在 Internet 上,這個(gè)概念已經(jīng)有了一些第三方實(shí)現(xiàn)。就本文而言,我使用了自己的ObservableCollection?實(shí)現(xiàn),因?yàn)榭蚣苤恍枰恍┳罨镜墓δ堋N业膶?shí)現(xiàn)使用了一個(gè)稱為?collectionChanged()?的方法,每次發(fā)生修改時(shí),?ObservableCollection?都會(huì)在自己的偵聽器上調(diào)用該方法。也可以將該用法稱為 Collection 類的?Decorator(有關(guān) Collections 的 Decorator 更多信息,請(qǐng)參閱 Collections 框架的站點(diǎn)),只需要增加幾行代碼,您就可以在普通的 Collection 類中創(chuàng)建 Collection 類的 Observable 實(shí)例。 清單 5 顯示了 ObservableCollection 用法的示例。(這只是一個(gè)示例,沒有包含在 j2x.zip 中。)
清單 5. ObservableCollection 用法示例
| // convert a normal list to an ObservableListObservableList oList = CollectionUtilities.observableList(list);// A listener could then register for events from this list by callingoList.addCollectionListener(this);// trigger event oList.add(new Integer(3));// listener receives eventpublic void collectionChanged(CollectionEvent e){// event received here} |
ObservableCollection?有許多 TMF 框架之外的應(yīng)用程序。如果您決定采用 TMF 框架,您會(huì)發(fā)現(xiàn),在開發(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í),都必須用表模型的引用來更新視圖。而在 TMF 框架中使用 ObservableCollection 時(shí),當(dāng)數(shù)據(jù)發(fā)生變化時(shí),視圖會(huì)自動(dòng)更新,不需要維護(hù)一個(gè)到模型的引用。在 BeanTableModel 的collectionChanged()?方法的實(shí)現(xiàn)中,您可以看到這一點(diǎn)。
TableUtilities
在該框架中執(zhí)行的最后一步操作,是將所有內(nèi)容集成到一些實(shí)用方法中,讓 TMF 框架使用起來簡(jiǎn)單明了。這些實(shí)用方法可以在com.ibm.j2x.swing.table.TableUtilities?類中找到,該類提供了您將需要的所有輔助函數(shù):
- getColumnInfo()?:該實(shí)用方法用 Castor XML 文件解析指定的文件,并返回指定表模型的所有列信息,返回的形式是 BeanTableModel 所需的?LinkedMap?。當(dāng)開發(fā)人員選擇從 BeanTableModel 中派生子類時(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)單的方法所代替:?
TableUtilities.setViewToModel("table_config.xml", "Table", myJTable, myList);
回頁(yè)首
TMF 框架實(shí)戰(zhàn)
每篇關(guān)于 GUI 編程的文章都需要一個(gè)示例,本文當(dāng)然也不例外。該示例的目的是指出使用 TMF 框架代替?zhèn)鹘y(tǒng) TableModel 設(shè)計(jì)的主要優(yōu)勢(shì)所在。示例中的應(yīng)用程序?qū)⒃谄聊簧巷@示多個(gè)表,并且可以添加或刪除表,表中可以包含不同類型的信息(?String?類型、?int類型、?Boolean?類型和?BigDecimal?類型),而且最重要的是,其中還包含可配置的列信息,必須定期更改它們。
示例應(yīng)用程序的代碼從?J2X?包中分離了出來,您可以 HR 文件夾的 src 目錄中找到源代碼。還可以雙擊 build/lib 文件中編譯好的 JAR 文件,通過 JRE 運(yùn)行應(yīng)用程序。
在示例應(yīng)用程序中,有兩個(gè)類可以相互交換,一個(gè)叫作?TableModelFreeExample?,另一個(gè)叫作?TableModelExample?。這兩個(gè)類在應(yīng)用程序中做的是同樣的事,使應(yīng)用程序產(chǎn)生的行為也相同。但是,它們的設(shè)計(jì)不同,一個(gè)使用的是 TMF 框架,另外一個(gè)則使用傳統(tǒng)的 TableModel。您從它們身上注意到的第一件事可能是 TMF 類?TableModelFreeExample?,該類由 63 行代碼構(gòu)成,而在傳統(tǒng) TableModel 版本?TableModelExample?中,它長(zhǎng)達(dá) 285 行。
Evil HR Director 應(yīng)用程序
我要使用的示例應(yīng)用程序是 Evil HR Director 應(yīng)用程序,它允許人力資源總監(jiān)(可能很可怕,戴著眼鏡)在 JTable 中查看潛在雇員的列表,然后從表中選出雇傭的人。新雇傭的員工的資料會(huì)轉(zhuǎn)移到當(dāng)前雇員使用的兩個(gè) JTable 中;其中一個(gè)表包含個(gè)人信息,另外一個(gè)表包含財(cái)務(wù)信息。在當(dāng)前雇員表中,總監(jiān)可以隨意選擇解雇誰。您可以在圖 1 中看到該應(yīng)用程序的 UI。
圖 1. Evil HR Director 應(yīng)用程序
?
為了進(jìn)一步證明 TMF 框架的簡(jiǎn)單性,請(qǐng)看清單 6。這個(gè)清單只包含三行必需的代碼,就可以創(chuàng)建 Evil HR Director 應(yīng)用程序中包含的三個(gè)表的模型。這些代碼可以在?TableModelFreeExample?中找到。
清單 6.在 Evil HR Director 應(yīng)用程序中創(chuàng)建模型所需要的代碼
| TableUtilities.setViewToModel("demo/hr/resources/evil_hr_table.xml", "Hire", hireTable, candidates); TableUtilities.setViewToModel("demo/hr/resources/evil_hr_table.xml", "Personal", personalTable, employees);TableUtilities.setViewToModel("demo/hr/resources/evil_hr_table.xml", "Financial", financialTable, employees); |
為了進(jìn)行比較,?TableModelExample?中包含用傳統(tǒng) TableModel 方法為三個(gè)表格創(chuàng)建模型所需要的代碼。請(qǐng)查看示例包中的代碼。不過,我不想在這里列出所有代碼,因?yàn)樗阕阌?205 行!
演示 TMF 框架的靈活性
TMF 框架的巨大優(yōu)勢(shì)之一,是它能更加容易地基于 JTable 的應(yīng)用程序在其發(fā)布之后進(jìn)行修改。為了證實(shí)這一點(diǎn),讓我們來看兩個(gè)可能的場(chǎng)景,這兩個(gè)場(chǎng)景在使用 Evil HR Director 應(yīng)用程序中每天都可能出現(xiàn)。在每個(gè)場(chǎng)景中,您都會(huì)看到框架是如何讓應(yīng)用程序更加容易地適應(yīng)不斷變化的用戶需求。
場(chǎng)景 1:公司的策略發(fā)生變化,規(guī)定在公司的應(yīng)用程序中查看私人的婚姻信息是非法的。
- TMF:最終用戶需要從 XML 配置文件中刪除?<name>Married?</name><field>married</field>?。?
- 傳統(tǒng) TableModel:開發(fā)人員必須深入研究 Java 代碼,修改?getColumnName()?,讓它無法返回列名“Married?”;修改getColumnCount()?,讓它返回的結(jié)果比以前返回的結(jié)果少一列;修改?getValueAt()?,不讓它返回?isMarried()?。然后開發(fā)人員必須重新編譯 Java 代碼,并重新部署應(yīng)用程序。
場(chǎng)景 2:公司策略發(fā)生變化,公司覺得有必要在潛在雇員表中包含居住地所在的州的信息。
- TMF:: 最終用戶需要將?<name>State</name><field>state</field>?添加到 XML 配置文件中。?
- 傳統(tǒng) TableModel:開發(fā)人員必須深入研究 Java 代碼,修改?getColumnName()?,添加一個(gè)叫作 “State” 新列;修改getColumnCount()?,讓它返回的列數(shù)加 1 ;修改?getValueAt()?,讓它返回?getState()?。然后開發(fā)人員必須重新編譯 Java 代碼,并重新部署應(yīng)用程序。
您可以看到,當(dāng)應(yīng)用程序中的表發(fā)生變化時(shí)(尤其在碰到一個(gè)總是朝令夕改的老板時(shí),更改更加頻繁),編輯 XML 文件要比重新部署整個(gè)應(yīng)用程序容易得多。
回頁(yè)首
使用代碼
在您飛奔過去刪除所有 TableModel 代碼之前,我想我還得占用您一分鐘解釋一下 j2x.zip 文件的內(nèi)容,以及您怎樣才能在您自己的項(xiàng)目中使用它。(請(qǐng)記住,特定于 TMF 的代碼可以在?com.ibm.j2x.swing.table?包中找到;您還會(huì)在 J2X 包中找到我在以前的文章“Go state-of-the-art with IFrame.”中介紹的其他代碼,請(qǐng)參閱?參考資料,其中有這篇文章的鏈接。)
j2x.zip 文件包含兩上文件夾:
- src—— 包含本文中使用的源代碼。在 src 文件夾中,還有兩個(gè)文件夾:一個(gè)是 HR,包含構(gòu)成 Evil HR Director 應(yīng)用程序的源代碼;另一個(gè)是 J2X,包含 J2X 項(xiàng)目中使用的所有源代碼。?
- build—— 包含 Evil HR Director 應(yīng)用程序和 J2X 項(xiàng)目編譯后的類文件。該文件夾中的 lib 文件夾則包含 HR 應(yīng)用程序和 J2X 項(xiàng)目的 JAR 文件。
lib.zip 文件包含以下文件夾:
- lib—— 包含所有的第三方 JAR 文件,運(yùn)行應(yīng)用程序或者任何使用 J2X 項(xiàng)目的項(xiàng)目,需要使用這些文件。在這個(gè)文件夾中,您還會(huì)找到第三方項(xiàng)目的許可。
docs.zip 文件包含下列文件夾:
- docs—— 包含 J2X 項(xiàng)目的所有 JavaDoc 信息。
要在應(yīng)用程序中使用 J2X 包,則需要把?CLASSPATH?指向 build/lib 文件夾中的 j2x.jar 以及 lib 文件中包含的所有三個(gè)第三方 JAR 文件。第三方包的許可條款允許您重新發(fā)布本文包含的所有包,但是如果有興趣對(duì)這些包做些修改,請(qǐng)閱讀許可條款。
回頁(yè)首
結(jié)束語
使用 TableModel Free 框架,就不用再編寫傳統(tǒng) TableModel 了。TMF 框架改進(jìn)了 JTable 和 TableModel 模型之間的 MVC 關(guān)系,更清楚地分離了它們。在日后的發(fā)布中,您甚至可以在不修改任何模型代碼的情況下,對(duì)組件進(jìn)行熱交換。框架還允許您在模型發(fā)生變化時(shí),自動(dòng)更新視圖,從而消除傳統(tǒng) TableModel 設(shè)計(jì)中所必需的視圖和模型之間的通信。
TMF 框架還會(huì)極大地減少開發(fā) GUI 所需的時(shí)間,特別是在處理 JTable 時(shí)。幾年以前,我處理的一個(gè)應(yīng)用程序中有 150 多個(gè) JTable,每個(gè)表都來自同一個(gè)原始表模型,該應(yīng)用程序可以作為示例。使用 TMF 框架,我們只用 150 行代碼就能解決問題;但是不幸的是,當(dāng)時(shí)還沒有 TMF,所以我們最后編寫了 15,000 行額外的代碼,才生成必需的表模型。這不但增加了開發(fā)時(shí)間,還增加了測(cè)試和調(diào)試的時(shí)間。
與使用傳統(tǒng) TableModel 相比,使用 TMF 框架使您到了一個(gè)更加容易配置所有 JTable 的時(shí)代。請(qǐng)想像這樣一個(gè) POS 應(yīng)用程序:該應(yīng)用程序被銷售給了 5 個(gè)不同的客戶,每個(gè)客戶都有一套特定的信息,所以每個(gè)用戶都想有一組顯示在 GUI 上的特定的列。如果沒有 TMF 框架,您就必須為每個(gè)客戶都生成一組特定的 TableModel —— 由此,也就生成了一組特定的應(yīng)用程序。而使用可配置的 XML 文件,每個(gè)客戶都可以使用相同的應(yīng)用程序,客戶所在地的業(yè)務(wù)分析師可以根據(jù)需要修改 XML 文件。請(qǐng)想像一下,這節(jié)約了多少開發(fā)和支持成本!
TableModel Free 框架解決了 Swing 開發(fā)人員社區(qū)的特定需求:減少了處理 JTable 時(shí)的開發(fā)時(shí)間和維護(hù)開銷,提高了它們對(duì)終端用戶的易用性。Swing 桌面正在回歸,使用像 TMF 框架這樣的工具,開發(fā)人員會(huì)發(fā)現(xiàn)可以更容易地使用 Swing 和開發(fā) GUI 應(yīng)用程序。您要做的第一步就是用 TMF 框架的一行代碼代替您所有的 TableModel,把所有 TableModel 都永遠(yuǎn)地拋到虛擬空間的黑洞中去吧。
回頁(yè)首
下載
| j2x.zip | ? | HTTP |
| lib.zip | ? | HTTP |
| docs.zip | ? | HTTP |
關(guān)于下載方法的信息
參考資料
- 您可以參閱本文在 developerWorks 全球站點(diǎn)上的?英文原文。?
- 請(qǐng)單擊?Code圖標(biāo)(或者參閱?下載部分),下載在本文中討論的源代碼、第三方 JAR 文件和 Javadoc。?
- The Jakarta Commons Collections提供了 Java Collections 之外的其他功能。?
- Castor是一個(gè)強(qiáng)大的 XML 解析器。?
- 如果對(duì) XML 感興趣,請(qǐng)?jiān)L問?developerWorksXML zone?,那里有大量?jī)?yōu)秀的內(nèi)容,既適合初學(xué)者,也適合專家。?
- Michael Abernethy 的“?Go state-of-the-art with IFrame,”(?developerWorks,2004 年 3 月)中對(duì) IFrame 進(jìn)行了介紹,它是 J2X 項(xiàng)目的另外一個(gè)組件。它允許您創(chuàng)建定制設(shè)計(jì)的應(yīng)用程序窗口。?
- 由 Andy Clark 和 Dave Smith 合著的“?Building a Customized Tree View,”(?developerWorks,2001 年 1 月)提供了在 Swing 組件中使用 MVC 的技巧。?
- Malcolm Davis 撰寫的“?Struts, an open-source MVC implementation,”(?developerWorks,2001 年 2 月)提供了在 Web 應(yīng)用程序中使用的 MVC。?
- Mitch Goldstein 撰寫的“?Swing model filtering,”(?developerWorks,2001 年 2 月)提供了用于 TableModel 設(shè)計(jì)的 TMF 框架的替代方案。?
- Brett Spell 撰寫的“?Rendering cells in Swing's JTable component,”(?developerWorks,2000 年 11 月)通過提供關(guān)于繪制單元格的技巧,添加了一項(xiàng) JTable 功能。?
- 在?developerWorksJava 技術(shù)專區(qū)?中,可以找到 Java 編程各個(gè)方面的文章。?
- 參閱?Developer Bookstore,以獲得技術(shù)書籍的完整清單,其中包括數(shù)百本?Java 相關(guān)主題的書籍。?
關(guān)于作者
Michael Abernethy 目前是 WebSphere 系統(tǒng)管理功能性測(cè)試團(tuán)隊(duì)的負(fù)責(zé)人,之前,他在 WebSphere Services 團(tuán)隊(duì)工作了 4 年,一直在 WebSphere 上編寫、部署應(yīng)用程序。在業(yè)余時(shí)間里,他還涉獵了 Swing 和 UI 開發(fā)。您可以通過 Michael 的郵箱?mabernet@us.ibm.com與他聯(lián)系。
總結(jié)
以上是生活随笔為你收集整理的用 TableModel Free 框架简化 Swing 开发——JTable的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++的4种智能指针剖析使用
- 下一篇: 大兔子生小兔子问题