精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

用TableModel框架簡化Swing開發

開發 后端
對于那些抱怨 Swing 太慢、界面太難看的開發人員來說,Swing開發所做的更新努力,并沒有帶來什么受人歡迎的好消息,這里介紹TableMode這個東西,它可以簡化Swing開發。

  如果您最近沒有用過Swing開發,那么您會很高興聽到其中的許多問題已經得到解決。Swing 被重新設計,它能執行得更好,并能更好地利用 Java 2D API。Swing 開發者在 1.4 版甚至***發布的 5.0 版中提高了外觀支持。Swing 從沒像現在這么好過。

  如果以前曾經用過 JTable,那么您可能也同時被迫使用了TableModel。您可能還注意到,每個 TableModel 中的所有代碼,與其他 TableModel 中的代碼幾乎是一樣的,在編譯的 Java 類中,有差異的代碼實際上是不存在的。本文將分析 TableModel/JTable 目前的設計方法,說明這種設計的不足,展示為什么它沒有實現模型-視圖-控制器(MVC)模式的真正目標。您將看到框架和構成 TMF 框架的代碼 —— 我以前編寫的代碼與最常用的開放源代碼項目的組合。使用該框架,開發人員可以把 TableModel 的大小從數百行代碼減少到只有區區一行,并把重要的表信息放在外部 XML 文件中。在讀完本文之后,只使用如下所示的一行代碼,您就可以管理您的 JTable 數據:

1 TableUtilities.setViewToModel("tableconfig.xml", "My Table",
2     myJTable, CollectionUtilities.observableList(myData));
3

  JTable 和 TableModel 存在的 MVC 問題

  MVC 已經成為非常流行的 UI 設計模式,因為它把業務邏輯清晰地從數據的視圖中分離了出來。Struts 是 MVC 在 Web 上應用的一個非常好的例子。最初,Swing ***的一個賣點是它采用了 MVC,將視圖從模型中分離了出來,代碼背后的想法是:代碼的模塊化程度足夠高,所以,不用修改模型中的任何代碼,就可以分離出視圖。我想,任何用過 JTables 和 TableModels 的人都會發笑,告訴您這是絕對不可能的。使用 MVC 設計模式的理想情況是,在開發人員用 JList 或 JComboBox 替換 JTable 時,可以不用修改表示數據的模式中的代碼。但是,在 Swing 中做不到這點。Swing 使得把 JTable、 JList 和 JComboBox 熱交換到應用程序中成為不可能,即使所有這三個組件都是用來為相同的數據模型提供視圖。對于 Swing 中的 MVC 設計,這是一個嚴重的不足。如果您想為 JTable 交換 JList,就必須重寫視圖背后的全部代碼,才能實現該交換。

  JTable/TableModel 的另一個 MVC 缺陷是:模型變化的時候,視圖不會更新自身。開發人員必須保持對模型的引用,并調用一個函數,這樣模型才會告訴視圖對自身進行更新;但是,理想的情況應當是:不需要任何額外的代碼,就能實現自動更新。

  ***,JTable 和 TableModel 組件設計的問題是,它們彼此之間纏雜得過于密切。如果您修改了 JTable 中的代碼,那么您需要確保您沒有破壞負責處理的 TableModel,反之亦然。對于一個被認為是在模塊化基礎上建立的設計模式來說,目前的實現顯然是一種存在過多依賴關系的設計。

  TMF 框架更好地遵循了 MVC 的目標,它把 JTable 中視圖和模型的工作更加清晰地分離開來。雖然它還沒有達到讓組件能夠熱切換的更高目標,但是它已經在正確方向上邁出了一步。

  讓我們來檢視 TMF 框架,看看它是如何讓傳統 TableModel 過時的。設計該框架的***部分是學習 JTable 的使用 —— 開發人員如何使用它,它顯示了什么內容,以便了理解哪些東西可以內化、通用化,哪些應當保留可配置狀態,以便開發人員配置。對于 TableModel,也要進行同樣的思考,我必須確定哪些東西可以從代碼中移出,哪些必須留在代碼中。一旦找出這些問題,接下來要做的就是確定能夠讓代碼足夠通用的***技術,以便所有人都能使用它,但是,還要讓代碼具備足夠的可配置性,這也是為了讓每個人都能使用它。

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

  在本文中,您可以在 src 文件夾中找到文中介紹的所有源代碼。特定于 TMF 的代碼位于 com.ibm.j2x.swing.table 包中。

  com.ibm.j2x.swing.table.BeanTableModel

  BeanTableModel 是框架的***部分。它充當的是通用 TableModel ,您可以用它來處理任何類型的數據。我知道,您可能會說,“您怎么這么肯定它適用于所有的數據呢?”確實,很明顯,我不能這么肯定,而且實際上,我確信有一些它不適用的例子。但是從我使用 JTables 的經驗來說,我愿意打賭(即使看起來我有點抬杠),實際使用中的 JTables,99% 都是用來顯示數據對象列表(也就是說,JavaBeans 組件的 ArrayList)。基于這個假設,我建立了一個通用表模型,它可以顯示任何數據對象列表,它就是 BeanTableModel。

  BeanTableModel 大量使用了 Java 的內省機制,來檢查 bean 中的字段,顯示正確的數據。它還使用了來自 Jakarta Commons Collections 框架的兩個類來輔助設計。

  在我深入研究代碼之前,請讓我解釋來自類的幾個概念。因為我可以在 bean 上使用內省機制,所以我需要了解 bean 本身的信息,主要是了解字段的名稱是什么。我可以通過普通的內省機制來完成這項工作:我可以檢查 bean ,找出其字段。但是,對于表來說,這還不夠好,因為多數開發人員想讓他們的表按照指定順序顯示字段。除此之外,還有一項表需要的信息,我無法通過內省機制從 bean 中獲得,即列名消息。所以,為了獲得正確顯示,對于表中的每個列,您需要兩條信息:列名和將要顯示的 bean 中的字段。我用鍵-值對的格式表示該信息,其中,將列名用作鍵,字段作為值。

  正因為如此,我在這里使用了來自 Collections 框架的適合這項工作的兩個類。 BeanMap 用作實用工具類,負責處理內省機制,它接手了內省機制的所有繁瑣工作。普通的內省機制開發需要大量的 try / catch 塊,對于表來說,這是沒有必要的。 BeanMap 把 bean 作為輸入,像處理 HashMap 那樣來處理它,在這里,鍵是 bean 中的字段(例如, firstName ),值是 get 方法(例如, getFirstName() )的結果。BeanTableModel 廣泛地運用 BeanMap ,消除了操作內省機制的麻煩,也使得訪問 bean 中的信息更加容易。

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

  清單1. 用 LinkedMap 和 BeanMap 設置表信息

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

  在 BeanTableModel 中比較有趣的檢查代碼實際上是通用 TableModel 的那一部分,這部分代碼擴展了 AbstractTableModel 。將清單 2 中的代碼與您通常用來建立傳統 TableModel 的代碼進行比較,您可以看到一些類似之處。

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

1   /**
2     * Returns the number of BeanMaps, therefore the number of JavaBeans
3     */    
4    public int getRowCount()
5    {
6       return mapValues.size();
7    }
8    /**
9     * Returns the number of key-value pairings in the column LinkedMap
10     */    
11    public int getColumnCount()
12    {
13       return columnInfo.size();
14    }
15   
16    /**
17     * Gets the key from the LinkedMap at the specified index (and a
18     * good example of why a LinkedMap is needed instead of a HashMap)
19     */  
20    public String getColumnName(int col)
21    {
22       return columnInfo.get(col).toString();
23    }
24    /**
25     * Gets the class of the column.  A lot of developers wonder what
26     * this is even used for.  It is used by the JTable to use custom
27     * cell renderers, some of which are built into JTables already
28     * (Boolean, Integer, String for example).  If you  write a custom cell
29     * renderer it would get loaded by the JTable for use in display  if that
30     * specified class were returned here.
31     * The function uses the BeanMap to get the actual value out of the
32     * JavaBean and determine its class.  However, because the BeanMap
33     * autoboxes things -- it converts the primitives to Objects for you
34     * (e.g. ints to Integers) -- the code needs to unautobox it, since the
35     * function must return a Class Object.  Thus, it recognizes any primitives
36     * and converts them to their respective Object class.
37     */  
38    public Class getColumnClass(int col)
39    {
40       BeanMap map = (BeanMap)mapValues.get(0);
41       Class c = map.getType(columnInfo.getValue(col).toString());
42       if (c == null)
43          return Object.class;
44       else if (c.isPrimitive())
45          return ClassUtilities.convertPrimitiveToObject(c);
46       else
47          return c;
48    }
49    /**
50     * The BeanTableModel automatically returns false, and if you
51     * need to make an editable table, you'll have to subclass
52     * BeanTableModel and override this function.
53     */    
54    public boolean isCellEditable(int row, int col)
55    {
56       return false;
57    }
58    /**
59     * The function that returns the value that you see in the JTable.  It gets
60     * the BeanMap wrapping the JavaBean based on the row, it uses the
61     * column number to get the field from the column information LinkedMap,
62     * and then uses the field to retrieve the value out of the BeanMap.  
63     */
64    public Object getValueAt(int row, int col)
65    {
66       BeanMap map = (BeanMap)mapValues.get(row);
67       return map.get(columnInfo.getValue(col));
68    }
69    /**
70     * The opposite function of the getValueAt -- it duplicates the work of the
71     * getValueAt, but instead puts the Object value into the BeanMap instead
72     * of retrieving its value.
73     */
74    public void setValueAt(Object value, int row, int col)
75    {
76       BeanMap map = (BeanMap)mapValues.get(row);
77       map.put(columnInfo.getValue(col), value);
78       super.fireTableRowsUpdated(row, row);
79    }
80   
81    /**
82     * The BeanTableModel implements the CollectionListener interface
83     * (1 of the 3 parts of the framework) and thus listens for changes in the
84     * data it is modeling and automatically updates the JTable and the
85     * model when a change occurs to the data.
86     */  
87    public void collectionChanged(CollectionEvent e)
88    {
89       initializeValues((Collection)e.getSource());
90       super.fireTableDataChanged();
91    }

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

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

  Castor XML 解析器

  保存必需的列名-字段信息的最合理的位置位于 Java 類之外,這樣,不需要再重新編譯 Java 代碼,就可以修改這個信息。因為關于列名和字段的信息是 TMF 框架中惟一明確與表有關的信息,這意味著整個表格都可以在外部進行配置。

  顯然,該解決方案會自然而然把 XML 作為配置文件的語言選擇。配置文件必須為多種表模型保存信息;您還需要能夠用這個文件指定每個列中的數據。配置文件還應當盡可能地易于閱讀,因為開發人員之外的人員有可能要修改它。

  這些問題的***解決方案是 Castor XML 解析器。查看 Castor 實際使用的***方法就是查看如何在框架中使用它。

  讓我們來考慮一下配置文件的目的:保存表模型和表中列的信息。 XML 文件應當盡可能簡單地顯示這些信息。TMF 框架中的 XML 文件用清單 3 所示的格式來保存表模型信息。

  清單3. TMF 配置文件示例

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

  與這個目的相反的目標是,開發人員必須處理的 Java 對象應當像 XML 文件一樣容易理解。通過 Castor XML 解析器用來存儲列信息的三個 Java 對象,就可以看到這一點,這三個對象是: TableData (存儲文件中的所有表模型)、 TableModelData (存儲特定于表模型的信息)和 TableModelColumnData (存儲列信息)。這三個類提供了 Java 開發人員所需的所有包裝器,以便得到有關 TableModel 的所有必要信息。

  將所有這些包裝在一起所缺少的一個環節就是 映射文件,它是一個 XML 文件,Castor 用它把簡單的 XML 映射到簡單的 Java 對象中。在***的世界中,映射文件也應當很簡單,但事實要比這復雜得多。良好的映射文件要使別的一切東西都保持簡單;所以一般來說,映射文件越復雜,配置文件和 Java 對象就越容易處理。映射文件所做的工作顧名思義就是把 XML 對象映射到 Java 對象。清單 4 顯示了 TMF 框架使用的映射文件。

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

1 xml version="1.0"?>
2    <mapping>
3       <description>A mapping file for externalized table modelsdescription>
4     
5       <class name="com.ibm.j2x.swing.table.TableData">
6          <map-to xml="data"/>
7          <field name="tableModelData" collection="arraylist" type=
8            "com.ibm.j2x.swing.table.TableModelData">
9             <bind-xml name="tableModelData"/>
10          field>
11       class>
12     
13       <class name="com.ibm.j2x.swing.table.TableModelData">
14          <map-to xml="model"/>
15          <field name="className" type="string">
16             <bind-xml name="className"/>
17          field>
18          <field name="name" type="string">
19             <bind-xml name="name"/>
20          field>
21          <field name="columns" collection="arraylist" type=
22            "com.ibm.j2x.swing.table.TableModelColumnData">
23             <bind-xml name="columns"/>
24          field>
25       class>
26     
27       <class name="com.ibm.j2x.swing.table.TableModelColumnData">
28          <map-to xml="column"/>
29          <field name="name" type="string">
30             <bind-xml name="name"/>
31          field>
32          <field name="field" type="string">
33             <bind-xml name="field"/>
34          field>        
35       class>
36     
37    mapping>
38

  僅僅通過觀察這段代碼,您就可以看出,映射文件清晰地勾劃出了每個用來存儲表模型信息的類,定義了類的類型,并將 XML 文件中的名稱連接到了 Java 對象中的字段。請保持相同的名稱,這樣會讓事情簡單、更好管理一些,但是沒必要保持名稱相同。

  到現在為止,列名和字段信息都已外部化,可以讀入包含列信息的 Java 對象中,并且可以很容易地把信息發送給 BeanTableModel,并用它來設置列。

  ObservableCollection

  TMF 框架的***一個關鍵部分,就是 ObservableCollection 。您們當中的某些人可能熟悉 ObservableCollection 的概念,它是 Java Collections 框架的一個成員,在被修改的時候,它會拋出事件,從而允許其偵聽器根據這些事件執行操作。雖然從來沒有將它引入 Java 語言的正式發行版中,但在 Internet 上,這個概念已經有了一些第三方實現。就本文而言,我使用了自己的 ObservableCollection 實現,因為框架只需要一些最基本的功能。我的實現使用了一個稱為 collectionChanged() 的方法,每次發生修改時, ObservableCollection 都會在自己的偵聽器上調用該方法。也可以將該用法稱為 Collection 類的 Decorator(有關 Collections 的 Decorator 更多信息,請參閱 Collections 框架的站點),只需要增加幾行代碼,您就可以在普通的 Collection 類中創建 Collection 類的 Observable 實例。 清單 5 顯示了 ObservableCollection 用法的示例。(這只是一個示例,沒有包含在 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
11    }
12

  ObservableCollection 有許多 TMF 框架之外的應用程序。如果您決定采用 TMF 框架,您會發現,在開發代碼期間, ObservableCollection 框架有許多實際的用途。

  但是,它在 TMF 框架中的用途,重點在于它能更好地定義視圖和模型之間的關系,當數據發生變化時,可以自動更新視圖。您可以回想一下,這正是傳統 TableModel 的***限制,因為每當數據發生變化時,都必須用表模型的引用來更新視圖。而在 TMF 框架中使用 ObservableCollection 時,當數據發生變化時,視圖會自動更新,不需要維護一個到模型的引用。在 BeanTableModel 的 collectionChanged() 方法的實現中,您可以看到這一點。

  TableUtilities

  在該框架中執行的***一步操作,是將所有內容集成到一些實用方法中,讓 TMF 框架使用起來簡單明了。這些實用方法可以在 com.ibm.j2x.swing.table.TableUtilities 類中找到,該類提供了您將需要的所有輔助函數:

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

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

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

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

 

  每篇關于 GUI 編程的文章都需要一個示例,本文當然也不例外。該示例的目的是指出使用 TMF 框架代替傳統 TableModel 設計的主要優勢所在。示例中的應用程序將在屏幕上顯示多個表,并且可以添加或刪除表,表中可以包含不同類型的信息( String 類型、 int 類型、 Boolean 類型和 BigDecimal 類型),而且最重要的是,其中還包含可配置的列信息,必須定期更改它們。

  示例應用程序的代碼從 J2X 包中分離了出來,您可以 HR 文件夾的 src 目錄中找到源代碼。還可以雙擊 build/lib 文件中編譯好的 JAR 文件,通過 JRE 運行應用程序。

  在示例應用程序中,有兩個類可以相互交換,一個叫作 TableModelFreeExample ,另一個叫作 TableModelExample 。這兩個類在應用程序中做的是同樣的事,使應用程序產生的行為也相同。但是,它們的設計不同,一個使用的是 TMF 框架,另外一個則使用傳統的 TableModel。您從它們身上注意到的***件事可能是 TMF 類 TableModelFreeExample ,該類由 63 行代碼構成,而在傳統 TableModel 版本 TableModelExample 中,它長達 285 行。

  Evil HR Director 應用程序

  我要使用的示例應用程序是 Evil HR Director 應用程序,它允許人力資源總監(可能很可怕,戴著眼鏡)在 JTable 中查看潛在雇員的列表,然后從表中選出雇傭的人。新雇傭的員工的資料會轉移到當前雇員使用的兩個 JTable 中;其中一個表包含個人信息,另外一個表包含財務信息。在當前雇員表中,總監可以隨意選擇解雇誰。您可以在圖 1 中看到該應用程序的 UI。

Evil HR Director 應用程序

  為了進一步證明 TMF 框架的簡單性,請看清單 6。這個清單只包含三行必需的代碼,就可以創建 Evil HR Director 應用程序中包含的三個表的模型。這些代碼可以在 TableModelFreeExample 中找到。

  清單 6.在 Evil HR Director 應用程序中創建模型所需要的代碼

1 TableUtilities.setViewToModel("demo/hr/resources/evil_hr_table.xml",
2      "Hire", hireTable, candidates);    
3    TableUtilities.setViewToModel("demo/hr/resources/evil_hr_table.xml",
4      "Personal", personalTable, employees);
5    TableUtilities.setViewToModel("demo/hr/resources/evil_hr_table.xml",
6      "Financial", financialTable, employees);
7

  為了進行比較, TableModelExample 中包含用傳統 TableModel 方法為三個表格創建模型所需要的代碼。請查看示例包中的代碼。不過,我不想在這里列出所有代碼,因為它足足有 205 行!

  演示 TMF 框架的靈活性

  TMF 框架的巨大優勢之一,是它能更加容易地基于 JTable 的應用程序在其發布之后進行修改。為了證實這一點,讓我們來看兩個可能的場景,這兩個場景在使用 Evil HR Director 應用程序中每天都可能出現。在每個場景中,您都會看到框架是如何讓應用程序更加容易地適應不斷變化的用戶需求。

  場景 1:公司的策略發生變化,規定在公司的應用程序中查看私人的婚姻信息是非法的。

  TMF:最終用戶需要從 XML 配置文件中刪除 Married?married 。

  傳統 TableModel:開發人員必須深入研究 Java 代碼,修改 getColumnName() ,讓它無法返回列名“Married?”;修改 getColumnCount() ,讓它返回的結果比以前返回的結果少一列;修改 getValueAt() ,不讓它返回 isMarried() 。然后開發人員必須重新編譯 Java 代碼,并重新部署應用程序。

  場景 2:公司策略發生變化,公司覺得有必要在潛在雇員表中包含居住地所在的州的信息。

  TMF:: 最終用戶需要將 Statestate 添加到 XML 配置文件中。

  傳統 TableModel:開發人員必須深入研究 Java 代碼,修改 getColumnName() ,添加一個叫作 “State” 新列;修改 getColumnCount() ,讓它返回的列數加 1 ;修改 getValueAt() ,讓它返回 getState() 。然后開發人員必須重新編譯 Java 代碼,并重新部署應用程序。

  您可以看到,當應用程序中的表發生變化時(尤其在碰到一個總是朝令夕改的老板時,更改更加頻繁),編輯 XML 文件要比重新部署整個應用程序容易得多。

  使用代碼

  在您飛奔過去刪除所有 TableModel 代碼之前,我想我還得占用您一分鐘解釋一下 j2x.zip 文件的內容,以及您怎樣才能在您自己的項目中使用它。(請記住,特定于 TMF 的代碼可以在 com.ibm.j2x.swing.table 包中找到;您還會在 J2X 包中找到我在以前的文章“Go state-of-the-art with IFrame.”中介紹的其他代碼。)

  j2x.zip 文件包含兩上文件夾:

  src—— 包含本文中使用的源代碼。在 src 文件夾中,還有兩個文件夾:一個是 HR,包含構成 Evil HR Director 應用程序的源代碼;另一個是 J2X,包含 J2X 項目中使用的所有源代碼。

  build—— 包含 Evil HR Director 應用程序和 J2X 項目編譯后的類文件。該文件夾中的 lib 文件夾則包含 HR 應用程序和 J2X 項目的 JAR 文件。

  lib.zip 文件包含以下文件夾:

  lib—— 包含所有的第三方 JAR 文件,運行應用程序或者任何使用 J2X 項目的項目,需要使用這些文件。在這個文件夾中,您還會找到第三方項目的許可。

  docs.zip 文件包含下列文件夾:

  docs—— 包含 J2X 項目的所有 JavaDoc 信息。

  要在應用程序中使用 J2X 包,則需要把 CLASSPATH 指向 build/lib 文件夾中的 j2x.jar 以及 lib 文件中包含的所有三個第三方 JAR 文件。第三方包的許可條款允許您重新發布本文包含的所有包,但是如果有興趣對這些包做些修改,請閱讀許可條款。

  結束語

  使用 TableModel Free 框架,就不用再編寫傳統 TableModel 了。TMF 框架改進了 JTable 和 TableModel 模型之間的 MVC 關系,更清楚地分離了它們。在日后的發布中,您甚至可以在不修改任何模型代碼的情況下,對組件進行熱交換。框架還允許您在模型發生變化時,自動更新視圖,從而消除傳統 TableModel 設計中所必需的視圖和模型之間的通信。

  TMF 框架還會極大地減少開發 GUI 所需的時間,特別是在處理 JTable 時。幾年以前,我處理的一個應用程序中有 150 多個 JTable,每個表都來自同一個原始表模型,該應用程序可以作為示例。使用 TMF 框架,我們只用 150 行代碼就能解決問題;但是不幸的是,當時還沒有 TMF,所以我們***編寫了 15,000 行額外的代碼,才生成必需的表模型。這不但增加了開發時間,還增加了測試和調試的時間。

  與使用傳統 TableModel 相比,使用 TMF 框架使您到了一個更加容易配置所有 JTable 的時代。請想像這樣一個 POS 應用程序:該應用程序被銷售給了 5 個不同的客戶,每個客戶都有一套特定的信息,所以每個用戶都想有一組顯示在 GUI 上的特定的列。如果沒有 TMF 框架,您就必須為每個客戶都生成一組特定的 TableModel —— 由此,也就生成了一組特定的應用程序。而使用可配置的 XML 文件,每個客戶都可以使用相同的應用程序,客戶所在地的業務分析師可以根據需要修改 XML 文件。請想像一下,這節約了多少開發和支持成本!

  TableModel Free 框架解決了 Swing 開發人員社區的特定需求:減少了處理 JTable 時的開發時間和維護開銷,提高了它們對終端用戶的易用性。Swing 桌面正在回歸,使用像 TMF 框架這樣的工具,開發人員會發現可以更容易地使用 Swing 和開發 GUI 應用程序。您要做的***步就是用 TMF 框架的一行代碼代替您所有的 TableModel,把所有 TableModel 都永遠地拋到虛擬空間的黑洞中去吧。

【編輯推薦】

  1. Red Hat CEO呼吁甲骨文繼續保持Java開放
  2. 自學Javabean迅速成為Java高手
  3. Java通過JNI調用C語言的方法
  4. 高手Java核心技術學習筆記
  5. 成為Java高手需要注意的25個學習目標
責任編輯:王觀 來源: 中國IT實驗室
相關推薦

2009-07-14 09:28:26

JTable和TablSwing

2009-07-15 09:06:07

BeanTableMoSwing

2009-07-14 09:52:10

TableModelESwing

2016-01-27 09:39:13

JoobyJava Web流程

2025-05-21 08:23:52

IO開發Java

2012-04-06 11:19:11

JavaGriffon

2009-07-15 10:23:51

Swing SWT集成

2009-10-28 12:39:33

Java EE無框架

2012-02-16 09:11:34

Swing

2009-07-17 12:44:01

NetBeans開發S

2019-10-10 10:03:09

開源技術 開發

2010-05-24 09:47:32

AjaxAjax框架

2011-01-24 13:20:49

2015-07-16 14:40:20

簡化切換框架

2017-04-19 08:47:42

AsyncJavascript異步代碼

2009-07-15 16:50:07

Swing項目

2009-07-14 18:10:38

Swing應用程序框架

2019-11-11 14:15:36

谷歌開源開發

2019-08-29 08:00:00

微服務架構服務網格
點贊
收藏

51CTO技術棧公眾號

波多野结衣在线一区二区| 成人毛片视频免费看| 日本福利一区| 亚洲国产综合91精品麻豆| 97免费资源站| 精品成人久久久| 欧美日中文字幕| 在线电影院国产精品| www.日本在线播放| av网在线观看| 成人夜色视频网站在线观看| 日韩暖暖在线视频| 欧美精品入口蜜桃| 国产精品免费大片| 欧美一级生活片| 欧美xxxxx在线视频| 亚洲夜夜综合| 国产欧美视频在线观看| 99久久国产免费免费| 亚洲永久精品一区| 在线 亚洲欧美在线综合一区| 亚洲色图日韩av| 乱码一区二区三区| japanese23hdxxxx日韩| 亚洲国产综合在线| 麻豆md0077饥渴少妇| 欧美18xxxxx| 成人在线一区二区三区| 国产欧美日韩免费看aⅴ视频| 国产性xxxx高清| 欧美午夜国产| 精品国产一区二区三区四区在线观看 | 亚洲成a人在线观看| 亚洲电影一二三区| 香蕉视频免费看| 国产成人av资源| 91影院在线免费观看视频| 日本免费精品视频| 亚洲深夜影院| 久久免费视频网| 免费看一级大片| 97精品国产| 中文字幕国内精品| 亚洲第一综合网| 免费久久精品| 亚洲欧洲国产伦综合| 超碰97在线资源站| 九九热hot精品视频在线播放| 日韩情涩欧美日韩视频| 国产毛片久久久久久| 日韩国产一二三区| 欧美写真视频网站| 亚洲一区二区三区四区五区xx| 一区二区三区短视频| 婷婷开心激情综合| 成年人午夜免费视频| 国产盗摄精品一区二区酒店| 一区二区三区丝袜| 欧美大片免费播放| 亚洲淫性视频| 亚洲国产精品久久久男人的天堂| av动漫在线免费观看| 中文字幕资源网在线观看| 亚洲欧美日韩中文播放 | 99久久免费国| 亚洲AV无码一区二区三区少妇| 国产高清视频一区| 国产精品综合久久久久久| 免费av网站在线播放| 不卡欧美aaaaa| 美女主播视频一区| 国产对白叫床清晰在线播放| 中文字幕乱码亚洲精品一区| 一区二区三区不卡在线| caopo在线| 亚洲成a人片在线观看中文| 国模无码视频一区二区三区| 亚洲欧洲自拍| 欧美日韩黄色影视| gogo亚洲国模私拍人体| 国产乱人伦丫前精品视频| 亚洲精品视频二区| 色噜噜噜噜噜噜| 亚洲第一天堂| 久久久视频免费观看| 国产又黄又猛又粗又爽| 美腿丝袜亚洲一区| 亚洲www在线| 四虎永久在线观看| 中文子幕无线码一区tr| 草草草视频在线观看| 香蕉伊大人中文在线观看| 欧美色综合影院| www.com日本| 精品免费视频| 久久99精品久久久久久噜噜 | 欧美蜜桃一区二区三区| 四虎成人免费视频| 日韩精品诱惑一区?区三区| 欧美精品18videos性欧美| 免费av网站在线| 国产一区二区三区四区在线观看| 国产美女在线精品免费观看| 成人好色电影| 午夜影院在线观看欧美| 亚洲精品久久久中文字幕| 韩国女主播一区二区三区| 中文字幕无线精品亚洲乱码一区| 久久无码精品丰满人妻| 欧美aaa在线| 国外成人免费视频| 国产三级在线播放| 色婷婷综合久久久中文一区二区| 永久看看免费大片| 日韩精品一卡| 日韩美女视频在线观看| 亚洲成人精品女人久久久| 日本一区二区三区免费乱视频| 成人av在线不卡| 欧洲精品久久久久毛片完整版| 日韩视频免费观看高清完整版 | 99国产超薄丝袜足j在线观看| 国产区高清在线| 亚洲成a人v欧美综合天堂下载| 欧美在线aaa| 免费观看久久av| 久久噜噜噜精品国产亚洲综合| 这里只有精品国产| 91蜜桃免费观看视频| 今天免费高清在线观看国语| 粉嫩av一区二区三区四区五区| 日韩av一区二区在线| 免看一级a毛片一片成人不卡| 蜜臀91精品一区二区三区| 欧美大香线蕉线伊人久久| 国产第一页在线视频| 日韩欧美国产午夜精品| 婷婷激情四射网| 免费的成人av| 亚洲精品二区| 日本精品网站| 亚洲最新av在线网站| 国产精品100| 91亚洲国产成人精品一区二三| 国产尤物av一区二区三区| 韩国一区二区三区视频| 精品国偷自产在线| 国产精品视频一区二区三区,| 欧美激情一区二区在线| 亚洲污视频在线观看| 国产亚洲一卡2卡3卡4卡新区| 欧美亚洲免费电影| 色视频在线看| 色婷婷激情综合| b站大片免费直播| 视频一区视频二区中文字幕| 欧美日韩成人一区二区三区| 全亚洲第一av番号网站| 国产香蕉精品视频一区二区三区| 日本一本在线观看| 国产精品伦理一区二区| 国产精欧美一区二区三区白种人| 日韩欧美视频| 日产精品久久久一区二区福利| 欧美美乳在线| 欧美亚洲国产一区二区三区 | 黄色av电影网站| 中文一区在线| 日韩激情久久| 一区在线不卡| 久久久久久久久久久av| 天堂在线视频免费观看| 日韩欧美在线视频观看| 日本成人午夜影院| 国产一区二区在线免费观看| 青青草视频国产| 久久资源综合| 国产精品一二三视频| aaa大片在线观看| 亚洲国产精品大全| 这里只有精品电影| 中文字幕第20页| 精品一区二区三区免费播放| 成人在线免费观看网址| 免费看久久久| 国产美女91呻吟求| 欧洲在线视频| 亚洲视频一区二区| 91久久久久国产一区二区| 亚洲最大成人综合| 亚洲成人网在线播放| 久久成人av少妇免费| 分分操这里只有精品| 精品免费在线| 国产伦精品一区| 成人激情视屏| 久久久久久香蕉网| 搞黄视频在线观看| 欧美xxx久久| 夜夜爽妓女8888视频免费观看| 亚洲欧美国产77777| 草草影院第一页| 国产伦精品一区二区三区在线观看| 丰满的少妇愉情hd高清果冻传媒| 国产一区二区三区四区五区传媒 | 欧美国产日韩一区二区三区| 飘雪影院手机免费高清版在线观看| 欧美日韩国产高清一区二区三区| 中文字幕第28页| 国产精品不卡在线观看| 精品无码国产一区二区三区51安| 男女男精品网站| 国产精品va无码一区二区| 91av精品| 亚洲国产成人不卡| 日韩美女国产精品| 99精品国产高清一区二区| 先锋欧美三级| 欧美一区二区三区……| 手机在线免费看av| 日韩中文在线视频| 可以在线观看的av| 亚洲黄色av女优在线观看 | 日本熟妇一区二区| 亚洲欧美国产77777| 欧美一级片久久久久久久| 国产精品成人国产乱| 日韩一区欧美小说| 男人舔女人下部高潮全视频| 成人国产电影网| 日韩不卡的av| 麻豆一区二区99久久久久| 男人操女人免费| 9国产精品视频| 欧美一级视频免费看| 最新精品国产| 大桥未久一区二区三区| 久久综合99| 亚洲欧美国产精品桃花| 欧美日韩中文一区二区| 日本黄网免费一区二区精品| 日韩在线黄色| 久久精品美女| 少妇久久久久| 欧美日韩在线观看一区二区三区 | 久久国产精品99久久久久久老狼 | 日韩人妻精品一区二区三区| 日本一区二区三区视频| 色综合久久88色综合天天提莫| 国产精品美女久久久久久不卡 | 中文视频在线观看| 成人永久aaa| 欧美双性人妖o0| 波多野结衣中文一区| 欧美精品一区二区久久婷婷| 男女视频免费看| 欧美三级免费观看| 欧美一区免费看| 在线精品视频一区二区三四| 五月激情丁香网| 欧美色电影在线| 国产精品毛片久久久久久久av| 欧美精品一卡二卡| 国产99久久九九精品无码免费| 日韩欧美在线不卡| 欧美自拍偷拍一区二区| 亚洲级视频在线观看免费1级| 四虎国产精品永远| 亚洲人成网站色ww在线| av中文字幕一区二区三区| 久久久999国产| 超级碰碰不卡在线视频| 欧美与欧洲交xxxx免费观看| 国产亚洲一区二区手机在线观看 | 国产传媒久久久| 国产午夜精品一区二区三区欧美| 丰满少妇被猛烈进入高清播放| 日韩综合在线视频| 欧美美女一级片| gogogo免费视频观看亚洲一| aaaaa级少妇高潮大片免费看| 欧美极品xxx| 99精品久久久久| 精品久久久久久久久久久久| 樱花视频在线免费观看| 91精品国产一区二区人妖| 国产香蕉在线观看| 亚洲片av在线| 亚洲按摩av| 日本精品视频在线| 麻豆精品在线| 欧美一区二视频在线免费观看| 国产韩国精品一区二区三区| 黄页免费在线观看视频| 日韩电影免费在线| xxxxwww一片| 欧美国产精品劲爆| 国产无码精品在线播放| 欧美性一级生活| 日韩在线一区二区三区四区| 在线视频国产日韩| heyzo在线| 91久久久久久久久久| 日韩高清电影免费| 日韩国产精品毛片| 久久久噜噜噜久久狠狠50岁| 宇都宫紫苑在线播放| 国产天堂亚洲国产碰碰| 国产精品成人久久| 7777精品伊人久久久大香线蕉| 天天av综合网| 九九热精品在线| julia一区二区三区中文字幕| 国产精品免费一区二区三区在线观看 | 欧美一区二区三区视频免费播放| 日韩av地址| 九九热r在线视频精品| 国产资源一区| 欧美日韩精品免费看| 亚洲无吗在线| www.桃色.com| 国产精品每日更新在线播放网址| 日韩久久中文字幕| 亚洲韩国欧洲国产日产av| 婷婷丁香在线| 成人av番号网| 第一社区sis001原创亚洲| 国产l精品国产亚洲区久久| 国产成人综合亚洲网站| 免费在线观看黄色小视频| 在线观看亚洲a| 欧美日韩国产中文字幕在线| 国语自产精品视频在线看| 日韩成人久久| 黄色网zhan| 国产一区二区三区av电影| 国产wwwwxxxx| 欧美日韩视频第一区| 成人在线观看黄色| 国产成人精品最新| 亚洲人和日本人hd| 精品一卡二卡三卡| 久久综合av免费| 手机在线看片1024| 亚洲欧美激情另类校园| 免费观看欧美大片| 日本一区二区三区在线视频| 蜜桃视频一区| 中文字幕网站在线观看| 色狠狠色噜噜噜综合网| 韩国三级av在线免费观看| 国产精品2018| 色综合咪咪久久网| 亚洲黄色av片| 亚洲免费在线视频一区 二区| 精品国产99久久久久久宅男i| 久久亚洲精品视频| av日韩精品| 国产精品网站免费| www国产成人| 久久久久久久久久一级| www欧美日韩| 日本伊人久久| 国产中文字幕二区| 久久久精品tv| 国产一区二区女内射| 欧美成人精品在线| 给我免费播放日韩视频| 高清在线观看免费| 国产欧美视频一区二区| 亚洲天堂中文字幕在线| 久久成人精品视频| 久久夜色电影| 自拍偷拍21p| 亚洲综合在线免费观看| 日韩av免费观影| 成人黄色在线播放| 亚洲美女黄网| 中文天堂资源在线| 欧美成人激情免费网| 中文字幕在线视频网站| 亚洲一区二区三区乱码 | 日本丰满少妇裸体自慰| 91福利视频在线| 97影院秋霞午夜在线观看| 国模精品娜娜一二三区| 欧美96一区二区免费视频| 欧美成人精品一区二区免费看片| 亚洲精品www| 在线不卡一区| 欧美三级一级片| 亚洲三级理论片| 亚洲欧洲精品视频| 国产美女久久久| 国产一区二区三区四区老人| 欧美一区二区三区成人精品| 欧美色综合影院| 天堂中文av在线资源库| 国产精品美女诱惑| 精品一区二区三区欧美|