在本節中,我們将要介紹一個實用的示例程式,用來說明在實際環境中XML的用法。請回憶一下卷Ⅰ第12章,GridBagLayout是Swing構件中最有用的布局管理器。然而,人們都很畏懼它,這不僅是因為它的複雜性,還因為其編碼冗長乏味。把布局描述放到一個文本檔案中來替代大量重複代碼将會帶來很大便利。在本節中,你将看到怎樣用XML來描述網格組(grid bag)布局和怎樣解析布局檔案。
網格組是由行和列構成的,它和HTML表格非常相似。與HTML表格相似的是,我們把它描述成一個行的序列,每個行都包含若幹單元格:
有些單元格可以跨多行多列。在網格組布局中,這是通過将gridwidth和gridheight設定為大于1的值來實作的。我們将使用相同的名字作為屬性名:
同樣,我們将屬性應用于網格組的其他限制:f?ill、anchor、gridx、gridy、weightx、weighty、ipadx和ipady。(我們不處理insets限制,因為它的值不是簡單類型,但是要支援它也是很簡單的。)例如:
對大多數屬性,我們都提供了與為GridBagConstraints的無參構造器所提供的預設值相同的預設值:
gridx和gridy的值受到了特殊處理,因為如果手工設定會很冗長且易于出錯。是以,提供它們的值是一項可選操作:
如果沒有提供這些值,程式會通過如下的啟發式方法來确定它們:在第0列,gridx的預設值是0;否則,它是前面的gridx加上前面的gridwidth;gridy的預設值總是與行數相同。這樣,在大多數跨越多行的情況下,你都不必指定gridx和gridy的值。但是,如果一個構件跨越多列,那麼每當要跨過這個構件時,就必須指定gridx。
注意:網格組專家可能會奇怪,我們為什麼不使用RELATIVE和REMAINDER機制讓網格組布局自動确定gridx和gridy的位置呢?我們試過這種方法,但是怎麼也不能産生圖3-4中那個字型對話框示例的布局。閱讀了GridBagLayout的源代碼後,我們發現,很明顯,它的算法沒有完成恢複絕對位置所必需的繁重任務。
這個程式對屬性進行解析,并且設定了網格組的限制條件。例如,要讀取網格寬度,程式隻需包含下面這行語句:
程式不必擔心屬性的缺失,因為當文檔中沒有指定任何其他的值時,解析器會自動提供其預設值。
如果要測試是否指定了gridx或gridy屬性,我們可以調用getAttribute方法來檢查它是否傳回空串:
我們發現允許單元格包含任意對象會顯得很友善,這使我們能夠指定如邊界那樣的非構件類型。我們隻要求這些對象屬于這樣的類:它具有一個預設構造器,而對每個屬性都提供了相應的擷取器(getter)/ 設定器(setter)對。(例如被稱為JavaBean的類。)
bean是由一個類名和0或多個屬性定義的:
屬性包含一個名字和一個值。
把字元串用标簽圍起來似乎有點麻煩。為什麼不隻用#PCDATA表示字元串而隻留下用于其他類型的标簽呢?因為那樣我們就需要使用混合式内容,并且會把value元素的規則弱化為:
這樣的規則允許由任意文本和标簽構成的混合内容。
程式可以使用BeanInfo類來設定屬性,而BeanInfo可以枚舉bean的屬性描述符。我們用比對名字的方式來查找屬性,然後調用它的setter方法來設定其值。
當我們的程式讀入一個使用者界面描述時,它有足夠的資訊來建構和布局使用者界面構件。但是,當然,這個界面是死的,因為它沒有事件監聽器。如果要添加事件監聽器,我們必須先定位構件。因為這個緣故,我們為每個bean提供了ID類型的可選屬性:
例如,下面是一個帶有ID的組合框:
注意:在這個示例中,我們隻使用了XML來描述構件布局,而把在Java代碼中添加事件處理器的工作留給了程式員。你可以更進一步,将該代碼添加到XML描述中去。最有前途的方式是用JavaScript這樣的腳本語言來編碼這種代碼。如果你想添加這樣的增強功能,請參考第8章描述的Nashorn JavaScript解釋器。
程式清單3-2的程式顯示了如何使用GridBagPane類來完成設定網格組布局時所有的無聊工作,這個布局是在程式清單3-4中定義的。圖3-4顯示了運作結果。該程式隻初始化了組合框(這項工作對于GridBagPane支援的bean屬性設定機制來說過于複雜了)和添加事件監聽器;程式清單3-3中的GridBagPane類用于解析XML檔案,構造構件并放置它們;程式清單3-5顯示的是DTD檔案。
如果選擇了包含字元串-Schema的檔案,那麼該程式除了DTD,還可以處理Schema。
程式清單3-6就包含了這樣的Schema。
這個例子是XML的典型用法。XML格式十分健壯,足以表達複雜的關系。在此基礎上,通過接管有效性檢查和提供預設值等例行工作,XML解析器添加了新的價值。
程式清單3-2 read/GridBagTest.java
程式清單3-3 read/GridBagPane.java