構建應用程序應該以數據為中心,而不是以用戶界面為中心,這是一個良好 的編程習慣。為支持這種編程范式,Swing為每種帶有邏輯數據或值的組件定義 了獨立的模型接口,這種分割使程序可以選擇向Swing組件中嵌入自己的模型實 現。
下面表格列出Swing中組件及其模型的映射關系:
組件 Model接口 Model類型 JButton ButtonModel GUI狀態 JToggleButton ButtonModel GUI狀態/應用數據 JCheckBox ButtonModel GUI狀態/應用數據 JRadioButton ButtonModel GUI狀態/應用數據 JMenu ButtonModel GUI狀態 JMenuItem ButtonModel GUI狀態 JCheckBoxMenuItem ButtonModel GUI狀態/應用數據 JRadioButtonMenuItem ButtonModel GUI狀態/應用數據 JComboBox ComboBoxModel 應用數據 JProgressBar BoundedRangeModel GUI狀態/應用數據 JScrollBar BoundedRangeModel GUI狀態/應用數據 JSlider BoundedRangeModel GUI狀態/應用數據 JTabbedPane SingleSelectionModel GUI狀態 JList ListModel 應用數據 JList ListSelectionModel GUI狀態 JTable TableModel 應用數據 JTable TableColumnModel GUI狀態 JTree TreeModel 應用數據 JTree TreeSelectionModel GUI狀態 JEditorPane Document 應用數據 JTextPane Document 應用數據 JTextArea Document 應用數據 JTextField Document 應用數據 JPasswordField Document 應用數據
Swing模型分類
Swing提供的模型分為兩大類:GUI狀態模型和應用數據模型。
GUI狀態模型是描述GUI控件可視化狀態的接口,如按鈕是否按下,或列表中 那一項被選中。GUI狀態模型通常僅在圖形用戶界面(GUI)環境中用到。通常來說 ,雖然編寫使用GUI狀態模型分離程序,尤其是當多個GUI控件共享狀態,或當操 作一個控件自動更新另一個的值時比較有用,但GUI狀態模型在Swing中並不是必 需的,完全可以通過組件頂層方法操作GUI控件的狀態,而不必和模型直接交互 。
應用數據模型是描述具有應用程序含義數據的接口,比如表格中的數據,或 列表顯示的選項。這些數據模型為Swing提供了一個清晰分割應用程序界面和數 據邏輯的強大編程模式。對於以數據為核心的Swing組件,比如JTree和JTable, 強烈推薦使用數據模型進行交互。
當然一些組件的模型根據應用場景的不同其分類介於GUI狀態模型和應用數據 模型之間,比如JSlider和JProgressBar 的BoundedRangeModel。
Swing的可分離模型接口並沒有明確界定GUI狀態模型和應用數據模型。這兒 所以做此說明,目的是讓你更好的理解何時以及為何要需要使用分離的模型。
共享模型定義
值得注意的是,上文中表格中,許多組件的數據抽象相似,只需一個接口而 不用過分泛化時,組件可以共享同一模型定義。共享模型定義允許在不同組件之 間自動連接。比如,JSlider和JScrollBar都使用BoundedRangeModel接口,因此 可以在一個JScrollBar和一個JSlider之間共享同一個BoundedRangeModel實例, 這樣它們之間的狀態就總是同步的。
分離模型編程接口
使用模型的Swing組件必須提供訪問修改模型的set/get方法,即模型必須是 該組件的限定性屬性。比如,JSlider使用BoundedRangeModel接口作為它的模型 定義,因此它必須提供下面方法:
public BoundedRangeModel getModel()
publicvoidsetModel(BoundedRangeModelmodel)
所有Swing組件有一個共同點:如果你不設置它的模型,組件會在內部創建/ 安裝一個缺省模型。這些缺省模型類的命名習慣是在接口名稱之前加上 “Default”,比如JSlider的構造函數中初始化一個 DefaultBoundedModel對象。
public JSlider(int orientation, int min, int max, intvalue){
checkOrientation(orientation);
this.orientation = orientation;
this.model = newDefaultBoundedRangeModel(value, 0, min, max);
this.model.addChangeListener(changeListener);
updateUI();
}
如果程序接著調用setModel(),缺省的模型就被替換了,比如下面例子:
JSlider slider = new JSlider();
BoundedRangeModel myModel = new DefaultBoundedRangeModel() {
public void setValue(int n){
System.out.println("SetValue: "+ n);
super.setValue(n);
}
});
slider.setModel(myModel);
對於更復雜的模型(如JTable和JList),Swing還提供一個抽象模型實現, 讓開發者不需要從頭開始創建自己的模型。
如JList的模型接口是ListModel,Swing同時提供了DefaultListModel和 AbstractListModel兩個類來協助開發者創建自定義的列表模型。
模型改變通知
當數據或者發生變動時,模型必須通知所有相關方(比如視圖)。Swing模型 使用前面文章所講述的事件模型來實現這種觸發。Swing中有兩種方法發送這種 通知:
發送輕量級通知,表明狀態已經改變,需要Listener通過查詢模型,發現什 麼改變了並做出響應。此方法的優點是單獨事件實例能用作該模型的所有通知, 同時對於需要頻繁通知的事件非常有用(比如JScrollBar被拖動時)。
發送狀態化通知,詳細描述模型如何改變。這種方法需要為每個通知創建一 個新的事件實例。當通知通過查詢模型不能有效地給Listener提供足夠的信息時 ,此方法非常有用。比如當JTable的一列表格數據發生改變時。