17.6 TreeModel介面
TreeModel介面描述了基本的JTree資料模型結構。他描述了父子彙總關係,允許任何的對象成為父節點或是子節點。樹有一個根節點,而所有其他的節點都是這個節點的後代。除了返回關於不同節點的資訊以外,模型要求實作類別管理TreeModelListener對象列表,從而當模型中的節點發生變化時可以得到通知。其他的方法,valueForPathChanged(),用來提供修改特定位置節點內容的方法。
public interface TreeModel { // Properties public Object getRoot(); // Listeners public void addTreeModelListener(TreeModelListener l); public void removeTreeModelListener(TreeModelListener l); // Instance methods public Object getChild(Object parent, int index); public int getChildCount(Object parent); public int getIndexOfChild(Object parent, Object child); public boolean isLeaf(Object node); public void valueForPathChanged(TreePath path, Object newValue);}17.6.1 DefaultTreeModel類
JTree自動建立一個DefaultTreeModel執行個體來儲存其資料模型。DefaultTreeModel類提供了一個在每個節點上使用TreeNode實現的TreeModel介面的實現。
除了實現TreeModel介面的方法並且管理TreeModelListener對象的列表以外,DefaultTreeModel類還添加了一些有用的方法:
- public void insertNodeInto(MutableTreeNode child, MutableParentNode parent, index int):將子節點添加到父節點的子節點集合中的索引位置。
- public void removeNodeFromParent(MutableTreeNode node):使得節點由樹中移除。
- public void nodeChanged(TreeNode node):通知模型節點已經發生變化。
- public void nodesChanged(TreeNode node, int childIndices[]):通知模型節點的子節點已經發生變化。
- public void nodeStructureChanged(TreeNode node):如果節點及子節點已經發生變化則通知模型。
- public void nodesWereInserted(TreeNode node, int childIndices[]):通知模型節點作為樹節點的子節點被插入。
- public void nodesWereRemoved(TreeNode node, int childIndices[], Object removedChildren[]):通知模型子節點已經被由樹中移除並且在方法調用中包含節點作為參數。
- public void reload()/public void reload(TreeNode node):通知模型節點已經發生了複雜的修改並且由根節點以下或是特定節點以下的模型應重新載入。
第一對方法用於直接由樹中添加或是移除節點。其他的方法用於當樹節點被修改時通知樹模型。如果我們不使用前兩個方法的一個由樹模型中插入或是移除節點,則我們要負責調用第二個集合中的方法。
17.6.2 TreeModelListener介面與TreeModelEvent類
TreeModel使用TreeModelListener來報告模型的變化。當TreeModel發送一個TreeModelEvent,所註冊的監聽器就會得到通知。介面包括當節點被插入,移除或是修改時的通知方法,以及當這些操作中的一個或是全部依次完成時的捕獲方法。
public interface TreeModelListener implements EventListener { public void treeNodesChanged(TreeModelEvent treeModelEvent); public void treeNodesInserted(TreeModelEvent treeModelEvent); public void treeNodesRemoved(TreeModelEvent treeModelEvent); public void treeStructureChanged(TreeModelEvent treeModelEvent);}17.7 TreeSelectionModel介面
除了所有的樹支援用於排序節點的資料模型,用於顯示節點的渲染器,以及用於編輯的編輯器以外,還有一個名為TreeSelectionModel的用於樹元素選取操作的資料模型。TreeSelectionModel介面包含用來描述到選定節點的選定路徑集合的方法。每一個路徑儲存在TreePath中,其中包含由根對象到選定節點的樹節點的路徑。我們會在稍後探討TreePath類。
public interface TreeSelectionModel { // Constants public final static int CONTIGUOUS_TREE_SELECTION; public final static int DISCONTIGUOUS_TREE_SELECTION; public final static int SINGLE_TREE_SELECTION; // Properties public TreePath getLeadSelectionPath(); public int getLeadSelectionRow(); public int getMaxSelectionRow(); public int getMinSelectionRow(); public RowMapper getRowMapper(); public void setRowMapper(RowMapper newMapper); public int getSelectionCount(); public boolean isSelectionEmpty(); public int getSelectionMode(); public void setSelectionMode(int mode); public TreePath getSelectionPath(); public void setSelectionPath(TreePath path); public TreePath[] getSelectionPaths(); public void setSelectionPaths(TreePath paths[]); public int[] getSelectionRows(); // Listeners public void addPropertyChangeListener(PropertyChangeListener listener); public void removePropertyChangeListener(PropertyChangeListener listener); public void addTreeSelectionListener(TreeSelectionListener listener); public void removeTreeSelectionListener(TreeSelectionListener listener); // Other methods public void addSelectionPath(TreePath path); public void addSelectionPaths(TreePath paths[]); public void clearSelection(); public boolean isPathSelected(TreePath path); public boolean isRowSelected(int row); public void removeSelectionPath(TreePath path); public void removeSelectionPaths(TreePath paths[]); public void resetRowSelection();}TreeSelectiomModel介面支援三種選擇模式,每一種模型由一個類常量指定:CONTIGUOUS_TREE_SELECTION, DISCONTIGUOUS_TREE_SELECTION或是SINGLE_TREE_SELECTION。當選擇模式是CONTIGUOUS_TREE_SELECTION時,只有彼此相連的節點才會被同時選中。DISCONTIGUOUS_TREE_SELECTION模式意味著並沒有同時選中的限制。而另一種選擇模式,SIGNLE_TREE_SELECTION,每次只能選中一個節點。如果我們不希望任何內容被選中,可以使用null設定。這會使用受保護的JTree.EmptySelectionModel類。
注意,用於選擇多個節點的按鍵是特定於觀感類型的。嘗試使用Ctrl或中Shift鍵組合來選擇多個節點。
除了修改選擇模式以外,其他的方法允許我們監視選擇牟屬性。有些這些方法會使用行數,而有時這些方法會使用TreePath對象。選擇模型使用RowMapper來為我們將行映射到路徑。抽象的AbstractLayoutCache類提供了一個RowMapper介面的基本實現,並且由FixedHeightLayoutCache與VariableHeightLayoutCache類進行特例化。我們並不需要訪問或是修改RowMapper或是其實現。要將行映射到路徑(或是將路徑映射到行),我們僅需要請求樹即可。
17.7.1 DefaultTreeSelectionModel類DefaultTreeSelectionModel類提供了TreeSelectionModel介面的實現,這個實現初始時使用DISCONTIGUOUS_TREE_SELECTION模式並且支援所有三種選擇模式。這個類引入了一些自己的方法用來擷取監聽器列表;其他的方法僅是實現了所有的TreeSelectionModel介面方法,包括訪問表17-6中所列的11個屬性的方法。另外,DefaultTreeSelectionModel重寫了Object的clone()方法,從而可以Cloneable。
使用TreeSelectionModel的主要原因在於修改模型的選擇模式。例如,下面的兩行代碼將模式修改為單選模式:
TreeSelectionModel selectionModel = tree.getSelectionModel();selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
如果我們對找出選定路徑感興趣,我們可以直接請求JTree。我們並不需要由模型擷取選定路徑。
17.7.2 TreeSelectionListener介面與TreeSelectionEvent類當樹中的選定節點集合變化時,就會產生一個TreeSelectionEvent並且TreeSelectionModel所註冊的TreeSelectionListener對象會得到通知。TreeSelectionListener可以註冊到JTree或是直接註冊到TreeSelectionModel。該介面定義如下:
public interface TreeSelectionListener implements EventListener { public void valueChanged(TreeSelectionEvent treeSelectionEvent);}17.7.3 TreePath類
我們要探討的最後一個類就是TreePath。在本章前面的例子中我們已經多次使用這個類。他描述了一個由根節點到另一個節點映射路徑的唯讀節點集合,這裡的根可以是一個子樹的根也可以是整棵樹的根。儘管有兩個建構函式可以用來建立TreePath對象,我們通常僅其作為方法的傳回值進行處理。我們也可以通過使用public TreePath pathByAddingChild(Object child)方法向已存在的TreePath添加元素來建立新的路徑。
TreePath可以作為是一個Object數組,其中數組的第一個元素是樹的根,而最後一個元素被最後的路徑組件。在這兩者之間是串連他們的組件。通常情況下,數組的元素是TreeNode類型。然而,因為TreeModel支援任意類型的對象,TreePath的path屬性被定義為Object節點數組。表17-7列出了四個TreePath屬性。
為了更好的理解TreePath,我們重用圖17-18所示的樹遍曆樣本,17-20所示。
17.8 Additional Expansion Events還有兩個可以註冊到JTree監聽器需要探討:一個TreeExpansionListner與一個TreeWillExapndListener。
17.8.1 TreeExpansionaListener介面與TreeExpansionEvent類如果我們對確定一個樹節點何時展開與摺疊,我們可以向樹註冊一個TreeExpansionListner。在父節點被展開或是摺疊之後所註冊的監聽器就會得到通知。
public interface TreeExpansionListener implements EventListener { public void treeCollapse(TreeExpansionEvent treeExpansionEvent); public void treeExpand(TreeExpansionEvent treeExpansionEvent);}每一個方法都有一個TreeExpansionEvent作為其參數。TreeExapnsionEvent類只有一個用於擷取到展開或是摺疊節點路徑的方法:public TreePath getPath()。
17.8.2 TreeWillExpandListener介面與ExpandVetoException類JTree支援TreeWillExpandListener的註冊,其定義如下:
public interface TreeWillExpandListener implements EventListener { public void treeWillCollapse(TreeExpansionEvent treeExpansionEvent) throws ExpandVetoException; public void treeWillExpand(TreeExpansionEvent treeExpansionEvent) throws ExpandVetoException;}這兩個方法簽名類似於TreeExpansionListener,並且他們都拋出ExpandVetoException。在父節點展開或是摺疊之前所註冊的監聽器會得到通知。如果監聽器不希望展開或是摺疊發生,監聽器可以拋出異常拒絕請求,阻止節點開啟或是關閉。
為了示範TreeWillExpandListneer,下面的代碼不會允許sports節點在預設的資料模型中展開或是colors節點摺疊。
TreeWillExpandListener treeWillExpandListener = new TreeWillExpandListener() { public void treeWillCollapse(TreeExpansionEvent treeExpansionEvent) throws ExpandVetoException { TreePath path = treeExpansionEvent.getPath(); DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); String data = node.getUserObject().toString(); if (data.equals("colors")) { throw new ExpandVetoException(treeExpansionEvent); } } public void treeWillExpand(TreeExpansionEvent treeExpansionEvent) throws ExpandVetoException { TreePath path = treeExpansionEvent.getPath(); DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); String data = node.getUserObject().toString(); if (data.equals("sports")) { throw new ExpandVetoException(treeExpansionEvent); } }};不要忘記使用類似下面的代碼將監聽器註冊到樹中:
tree.addTreeWillExpandListener(treeWillExpandListener)
17.9 小結
在本章中,我們瞭解了與JTree組件使用相關的多個類。我們瞭解了使用TreeCellRenderer介面與DefaultTreeCellRenderer實現的樹節點渲染。我們深入了使用TreeCellEditor介面,DefaultCellEditor與DefaultTreeCellEditor實現的樹節點編輯。
在審視了如何顯示與編輯樹之後,我們瞭解了用於手動建立樹對象的TreeNode介面,MutableTreeNode介面,與DefaultMutableTreeNode類。我們探討了用於儲存樹資料模型的的TreeModel介面與DefaultTreeModel實現,以及用於儲存樹選擇模型的TreeSelectionModel介面與DefaultTreeSelectionModel實現。
另外,我們瞭解了用於各種樹類的事件相關類以及用於描述節點串連路徑的TreePath。
在第18章,我們將會探討javax.swing.table包及其用於JTable組件的多個類。