Understanding Swing’s Model (轉)

worldblog發表於2007-12-14
Understanding Swing’s Model (轉)[@more@] 

Understanding ’s Model

 :namespace prefix = o ns = "urn:schemas--com::office" />

經常用Swing 開發 GUI 的人一定聽過這樣的說法,Swing 是按MVC結構設計的。更準確地說,Swing是Model-driven的結構。但不同Swing控制元件的Model,其作用是否相同呢?比如當你在使用JButton時,你很少需要關心ButtonModel的存在,但在JTable使用時,你卻總是需要用到 TableModel。更進一步,當你頻繁的使用JTable時,你會發現你可能不僅用到了TableModel,還用到TableColumnModel, ListionModel。這使我們意識到,Model存在不同的種類,不同型別的Model實現不同的功能。

 

GUI-State Model

 

首先,我們討論第一種Model, GUI–State Model。GUI-State Model的作用在於標識控制元件的視覺狀態 (visual status)。例如按鈕是否被點選,列表中的Item是否被選中。Swing的控制元件會對GUI-State Model的操作,通常我們不要直接操作GUI–State Model。

 

ButtonModel

 

最常見的GUI-State Model是ButtonModel,屬於這個範疇的控制元件有JButton ,JToggleButton ,JCheckBox, JRadioButton, JMenu, JMenuItem, JCheckBoxMenuItem, JRadioButtonMenuItem。(所有AbstractButton的子類)

ButtonModel需要標識的狀態有:

  1. PRESSED: Button是否被點選了
  2. ENABLED: Button能否被點選(是否顯示呈灰色)
  3. ROLLOVER: 滑鼠是否從Button上劃過。Button透過判斷這個屬性判斷是否要顯示RolloverIcon,當然前提是Button透過setRolloverIcon,設定了RolloverIcon
  4. SELECTED: 只對RadioButton or Checkbox 有用
  5. ARMED: 滑鼠點選Button後,是否在Button該釋放

顯而易見,這些屬性都只和顯示有關。對於GUI-State Model,只有以下兩種情況我們需要關心它的處在 :(1)我們想改變控制元件預設的視覺行為(假定這種情況很少發生) (2)出於某種顯示目的共用Model,操作一個控制元件會改變另外一個控制元件的狀態(下面會討論到這種情況),其他情況下我們可以忽視它。當然還有一種情況我們需要注意,這就是在使用JRadioButton時。因為使用JRadioButton時,一組JRadioButton同時只能有一個被選中(SELECTED),這當然只有透過操作ButtonModel的SELECTED屬性來實現。不過,Swing針對這個問題引入了ButtonGroup類,透過ButtonGroup.add()方法設定同一個 button group,因此我們同樣不需要直接操作ButtonModel。

 

BoundedRangeModel

 

另一個常見的GUI-State Model是BoundedRangeModel,屬於這個範疇的控制元件有JProgressBar  JScrollBar JSlr。

BoundedRangeModel標識的主要狀態有:min,max,value(int),同樣的,我們很少直接操作BoundedRangeModel。使用JProgressBar 最常見的方式是在構造裡指定min,max或是透過get/set讀寫min,max,value。而控制元件再把這些請求轉發給BoundedRangeModel。

前面提到出於某種顯示目的,我們有可能需要直接操作GUI-State Model。以下是一種可能的情況(scenario):當我們把一幅面積較大的影像放在JScrollPane,同時希望透過移動滑桿(JSlider)來控制顯示影像顯示在JScrollPane的部分。常見的做法是BoundedRangeModel的ChangeEvent事件(addChangeListener(ChangeListener l)),當JSlider改變了Model的值時在ChangeListener對顯示作相應的調整。

 

TableColumnModel

 

TableColumnModel是JTable特有的GUI-State Model。TableColumnModel用於管理TableColumn。而TableColumn代表了JTable中的每一列資料的視覺屬性,比如該列對應的data-model index(這決定了要顯示的內容,參見後面敘述),該列的寬度是否可變,列的最大、最小、首選寬度;該列的繪製器TableCellRenderer和編輯器TableCellEditor(JTable是面向列的,它基於每一列進行繪製和編輯)

 

Selection Model

 

考慮這樣一個問題,當使用JTable時,如何設定從X行到Y行處於選擇狀態呢?

我們可以透過調要JTable.setRowSelectionInterval(int index0, int index1)來實現。再進一步,如果想實現反轉選擇(Toggle Selection),即單擊齊數次處於選擇狀態,偶數次則處於非選擇狀態,JTable沒有提供直接的方法來實現。因為JTable將選擇的工作交由Selection Model來實現。察看setRowSelectionInterval的實現

public void setRowSelectionInterval(int index0, int index1) {

selectionModel.setSelectionInterval(boundRow(index0), boundRow(index1));

  }

要實現Toggle Selection就只有直接對Selection Model進行。

Selection Model也屬於GUI- State Model的範疇,因為它標識也是一種視覺的狀態,選中的Item會加亮(high light)。和其他GUI- State Model一樣,通常我們不需要直接操作Selection Model。

Selection Model標識的狀態有:

  1. SINGLE_SELECTION
  2. SINGLE_INTERVAL_SELECTION
  3. MULTIPLE_INTERVAL_SELECTION

 

我們分析一下其他需要判斷選擇幾種控制元件

  1. JList, 和 JTable一樣用的是ListSelectionModel
  2. JTree, JTree需要的Selection Model最複雜,因此它有專門Selection Model: TreeSelectionModel。
  3. JComboBox只能是單選,因此不需要有專門Selection Model
  4. JFileChooser,透過設定 FILES_ONLY ,DIRECTORIES_ONLY FILES_AND_DIRECTORIES (int) 來設定使用者是否可選擇,目錄,或兩者都可以。透過設定multiSelectionEnabled屬性來決定是否單選或多選,透過File陣列 selectedFiles記錄當前的選擇
  5. JTabbedPane, JTabbedPane的情況有些特殊,雖然JTabbedPane也只能單選,但它能有專門的Selection Model: SingleSelectionModel

對待Selection Model的方式和其他GUI-State Model一樣,相應Jcomponent都提供專門的函式遮蔽我們對它的直接操作。

 

Application-data model

 

這類的Model決定了顯示在控制元件中的內容,因此往往需要我們直接的操作。他們分別是:

  1. JList: ListModel
  2. JTable: TableModel
  3. JComboBox: ComboBoxModel
  4. JTree: TreeModel
  5. 各類Text控制元件:Document

 

ListModel

 

Swing首先定義了介面ListModel

然後定義了抽象類AbstractListModel實現這個介面。在抽象類裡沒有定義實際資料的方式。因此要實現AbstractListModel,使用者還需要定義這兩個函式

  1. public int getSize();
  2. public getElementAt(int index);

因為沒有定義實際資料的儲存方式,當然沒有辦法提供這兩個函式的實現。

最後Swing提供預設類DefaultListModel實現抽象類,預設類以Vector作為儲存資料的方式。

構造一個JList的例項有四種方式:

JList() 

JList(final Object[] listData)

JList(final Vector listData)

JList(ListModel dataModel)

前三種建構函式里會分別生成相應的ListModel。還可以在構造完後JList還可以用以下的函式來制定ListModel

void setListData(final Object[] listData) 

void setListData(final Vector listData) 

void setModel(ListModel model)

 

JList沒有提供編輯其Item的方法,使用者是無法直接編輯其Item的(這點和JComboBox不同,JComboBox提供了直接編輯其Item的方法),要改變Item的內容需要直接操作ListModel(用陣列和Vector生成JList不適合用來顯示可變內容的資料)。

要顯示可變內容的JList,最方便的方法是用DefaultListModel,但由於它用Vcetor作為其內部的儲存資料的方式,決定他們在處理大資料量的顯示時是不適宜的。首先Vector有內部容量的概念,當容量不足以容納更多的資料時,它需要重新分配一塊,複製原記憶體的東西,並把原來的記憶體丟棄,這是非常耗時的動作;其次,Vector是執行緒的容器(thread-safe collection),所有對容器的操作都需要同步(synchronized),對於包含大資料量的collection這也是非常耗時的。

因此對於大資料量的Application-data,使用者如果想用collection,應該在ArrayList和LinkedList(thread-unsafe collection)之間選擇:

  1. ArrayList也有內部容量的概念,但它提供了隨機存取的功能 (ran access), 使用它時可以預先申請一塊較大的記憶體,以免以後重新分配記憶體。
  2. LinkedList沒有內部容量的概念,因此不會重新分配記憶體,但它不提供隨機存取的功能。

 

TableModel

 

Swing對TableModel的處理和ListModel類似:

首先定義了介面TableModel,

然後定義了抽象類實現這個介面AbstractTableModel。在抽象類裡沒有定義實際資料的儲存方式。

要實現AbstractTableModel,使用者還需要定義這三個函式

  1. public getColumnCount()
  2. public Object getValueAt(int rowIndex, int columnIndex)
  3. public int getRowCount()

public String getColumnName(int column) 往往也需要定義,不然在表頭將顯示為A,B,C,D …

最後Swing提供預設類DefaultTableModel實現抽象類,預設類以Vector作為儲存資料的方式。因此對大資料量的操作(比如用JTable顯示查詢的結果)同樣不適合用DefaultTableModel。當用JTable顯示資料庫查詢的結果,最好是擴充套件

AbstractTableModel,並讓資料庫每次只返回批次的資料。

 

JTable的建構函式比起JList要複雜一些, 因為它還需要指定TableColumnModel。但對於Application-data model 的處理和JList是一樣的。

 

ComboBoxModel

 

JComboBox和JList很相似,都是用來顯示一個列表項,並可以接受使用者的選擇 (JRadioButton也實現這個功能)。但他們也有本質的不同,JComboBox可以接受使用者的輸入,並可以編輯已有的選項。通常在使用JComboBox是把它分成兩類:可編輯的和不可編輯的。預設狀態是不可編輯的,透過JComboBox.setEditable(true)可將JComboBox設定成可編輯。對應這兩種狀態JComboBox定義了兩類data-model介面,ComboBoxModel和MutableComboBoxModel。

 

ComboBoxModel的定義如下:

public interface ComboBoxModel extends ListModel {

  void setSelectedItem(Object anItem);

  Object getSelectedItem();

}

它擴充套件ListModel並只是定義了用於獲取和設定當前選項的辦法,這是因為JComboBox沒有Selection Model。

MutableComboBoxModel,顧名思義,當然是定義修改data-model的方法,它的定義如下:

public interface MutableComboBoxModel extends ComboBoxModel {

  public void addElement( Object obj );

  public void removeElement( Object obj );

  public void insertElementAt( Object obj, int index );

  public void removeElementAt( int index );

}

Swing提供對MutableComboBoxModel的實現DefaultComboBoxModel,其內部Vector來儲存資料,當我們想提供自己的現實時,最方便的方法是可以擴充套件AbstractListModel, 並選擇是實現ComboBoxModel還是MutableComboBoxModel。

 

構造一個JComboBox例項有四種方法:

public JComboBox()

public JComboBox(final Object items[])

public JComboBox(Vector items)

public JComboBox(ComboBoxModel aModel)

前三種建構函式里會分別生成相應的DefaultComboBoxModel。(個人覺得這樣構造JComboBox並不好,由於沒有透過建構函式建立不變性 (invariants) ,即該JComboBox是否可編輯的,當需要修改data-model時,JComboBox都需要首先判斷data-model是否可編輯,對於不可編輯的JComboBox呼叫編輯函式會丟出RuntimeException ())

 

TreeModel

 

TreeModel時最複雜的一種data-model,參考文獻一有詳細說明

 

 

各類Text控制元件

 

各類Text控制元件是比較獨立的主題,這裡不再詳述。

參考文獻

[1] Understanding the TreeModel :

[2] A Swing Architecture Overview :

[3] JGuru Faq:

[4] « Swing » by Matthew Robinson & Pavel Vorobiev


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-993469/,如需轉載,請註明出處,否則將追究法律責任。

相關文章