與VCL、AWT等框架中的圖形界面框架不同,Swing創造性的采用了MVC(Model View Controller) 模式。MVC 把控件(Component)劃分成三個部分:模型( Model):管理這個模塊中所用到的數據和值,如某個數據的最大值、最小值、當前值等數據;視圖( View):管理如何將模型顯示給用戶;控制器(Controller) 決定如何處理用戶和該模塊交互時產生的事件,如用戶單擊一個按鈕等。Sun 出于對視圖和控制器之間的依賴關系的考慮, 在 Swing 設計中將 MVC體系簡化為分離模型體系 ( Separable Model Architecture) , 將其中的控制器和視圖結合成 UI 代理。
界面中的每個控件都包含三種特征:
它的狀態:比如一個按鈕的狀態
它的外觀:顏色、尺寸等
它的行為:對事件作出的反應
以一個按鈕為例,它有可用、不可用狀態,在不同的LookAndFeel中有不同的外觀顯示,在鼠標按下、鼠標右擊等事件中有自己獨特的響應方式。如果管理按鈕狀態、繪制按鈕外觀,響應時間等任務都由按鈕負責的話就違背了面向對象設計中的“單一責任原則”。Swing開發人員采用MVC模式解決了此問題,將控件的LookAndFeel同一個對象關聯到一起,同時將其內容保存到另一個對象中。控制器負責控制用戶輸入事件。比如鼠標單擊、按鍵操作等,它會決定將這些事件轉換成模型中的改變.還是視圖中的改變。例如,假定用戶在文本框中按下某個鍵,控制器就會調用模型的“插入字符”命令。隨后,模型會通知視圖更新來顯示新的模型。視圖不用關心什么時候進行文字改變,只要模型通知它更新它就會更新。這樣控制器只用與用戶交互并把交互結果反映到模型中去;模型負責維護狀態,當狀態變化時通知視圖更新顯示;視圖不負責用戶交互的狀態維護,它只是根據模型中的狀態繪制不同的界面。
Swing中的大多數控件的模型是由一個名字以Model結尾的接口實現的。比如按鈕對應的模型接口就是 ButtonModel,JDK中定義了ButtonModel的默認實現類DefaultButtonModel。下面是ButtonModel各個方法的說明:
boolean isArmed():如果按鈕被按下,且鼠標指針仍停留在按鈕上則返回true
boolean isSelected():如果按鈕處于選擇狀態則返回true
boolean isEnabled():如果按鈕可用則返回true
boolean isPressed():如果按鈕被按下,但鼠標沒有松開,則返回true
boolean isRollover():如果鼠標指針在按鈕之上則返回true
public int getMnemonic():返回按鈕的助記鍵
public String getActionCommand():返回命令字符串
此外還有對應的設置狀態方法:public void setArmed(boolean b)、public void setSelected(boolean b)、public void setEnabled(boolean b)、public void setPressed(boolean b)、public void setRollover(boolean b)、public void setMnemonic(int key)、public void setActionCommand(String s)等。
每一個JButton都保存著一個ButtonModel對象,我們可以通過JButton的getModel方法來取得該模型對象:
JButton btn = new JButton(“test”);
ButtonModel btnModel = btn.getModel();
通過這個模型對象我們就可以得到按鈕的是否可用等狀態,不過這個模型是給控件開發者使用的,對于普通使用者來說無需直接調用它,JButton提供了方法來間接的取得這些屬性,這一點可以從AbstractButton類的isSelected方法中看出來:
public boolean isSelected()
{
return model.isSelected();
}
Swing中大部分控件都由自己的模型,比如JList控件的ListModel、JTable的TableModel、JSpinner的SpinnerModel、JComboBox的SpinnerModel(SpinnerModel是從ListModel派生出來的),這些模型也由默認的實現,名稱通常為模型名前加Default。
Swing中的大多數控件的視圖是由一個名字以UI結尾的類實現的,比如按鈕對應的模型接口就是 ButtonUI。由于視圖在不同的LookAndFeel中有不同的展現形式,所以控件的視圖對每一種LookAndFeel都提供了不同的實現。以JLabel為例,它就有MetalLabelUI、MotifLabelUI、WindowsLabelUI等對應不同LookAndFeel的實現。所有的視圖都要直接或者間接的從ComponentUI抽象類派生,ComponentUI類中的方法都是供Model回調使用的,下面是ComponentUI主要方法的說明:
public void installUI(JComponent c):這個方法在ComponentUI 實例被安裝到UI代理的時候被觸發,用來根據LookAndFeel配置控件。它需要完成如下工作:為Color、Font、Border、Icon等類型的屬性設定默認值;根據需要設置布局管理器;創建子控件;初始化監聽器;為控件設置PropertyChangeListener 監聽器以檢測控件屬性變化事件;初始化快捷鍵、Tab鍵順序等;初始化數據;
public void uninstallUI(JComponent c):這個方法在ComponentUI 實例被從UI代理移除的時候觸發。需要在此方法中撤銷任何在installUI中進行的配置,要保證JComponent實例變為潔凈狀態(也就是沒有監聽器,沒有LookAndFeel專有屬性等)。它需要完成如下工作:從控件中移除border;從控件中移除布局管理器;從控件中移除子控件;從控件中移除事件、屬性監聽器、從控件中移除快捷鍵、Tab鍵順序等;將數據標記為可以垃圾回收。
public void paint(Graphics g, JComponent c):為本視圖的LookAndFeel繪制控件。
public void update(Graphics g, JComponent c):通知UI代理繪制指定控件。當特定的控件被繪制的時候此方法會被觸發。這個方法的默認實現是用背景色填充控件,并且立即調用paint方法。
public Dimension getPreferredSize(JComponent c):返回當前LookAndFeel下控件的最佳尺寸。默認實現是返回null;
public Dimension getMinimumSize(JComponent c):返回當前LookAndFeel下控件的最小尺寸。默認實現是返回getPreferredSize的值;
public Dimension getMaximumSize(JComponent c):返回當前LookAndFeel下控件的最大尺寸。默認實現是返回getPreferredSize的值;
public boolean contains(JComponent c, int x, int y):判斷指定的x、y坐標是否存在于當前LookAndFeel下的控件中。
public static ComponentUI createUI(JComponent c):為指定的控件返回UI代理實例。如果UI代理子類是無狀態的,它也可以返回多控件共享的實例。如果UI代理子類是有狀態的,則它必須為某個控件返回一個新的實例。
public int getAccessibleChildrenCount(JComponent c):返回所有可訪問子控件的數量。
public Accessible getAccessibleChild(JComponent c, int i):返回指定的子控件