现在,可以回到TreeModel看看到底有哪些需要实现,JTree组件使用了一个根“对象”作为模型的锚,可以用getRoot找到它,然后仅仅使用四种方法就可以浏览整棵树了,这四种方法是getChild()、getChildCount()、getIndexOfChild() 和isLeaf()。
这些方法调用使用对象的类作为参数,返回值则指向了树的节点,这一点要非常注意。你可能希望树通过节点对象来实现TreeNode的界面,但是事实并不是这样,当创建TreeModel的时候,你可以加入任意你喜欢的对象,这是因为界面本身并不知道节点要对自身应用那些信息。
此时,我应该介绍我的示例代码了。(链接指向的事一个.zip文件)
“Messages”是一个关于如何使用动态树的示例,MessageBox类是一个存储于内存中的关于SimpleMessage的简单表,每个 SimpleMessage包括一个ID号、父节点(可能是一个ID号或是空指针) 和一个消息ID的列表,ID所指向的消息就是父节点;在实际应用中,MessageBox可以是一个持续的消息库,这个消息库在后台使用一个数据库或是一个使用远程服务的网络客户端。在我们使用的范围内,使用MessageBox中的getNumberOfCommentingMessages()和 getChildMessageId()就已经足够了,这样应用程序只需要使用ID就可以浏览消息线程乐,现在,MessageBox还没有树,因为还没有可供其他事件附加的“根消息”。
如果有一个根节点,那么创建TreeModel将会更容易,当查询根的时候,模型只需要返回MessageBox的根节点ID即可,其他的 TreeModel方法可以把ID当作它们的对象参数,只需把它们的请求传递给MessageBox。因为此处没有根节点,我们需要“虚构”一个,在 MessageBoxTreeModel中,你会发现getRoot方法会返回一个虚构的对象,这个对象是专门用于指示根节点的, MessageBoxTreeModel保存了一个没有父节点的消息的数组表,如果在这个模型中查询根节点就会用到这个数组,使用getChild方法可以检查父节点是否是根节点,如果父节点是根节点,就会返回数组中的一个值,否则,就从MessageBox中获得一个结果。
如果树是静态的,利用这种间接风格重复使用isLeaf、、getChildCount和getIndexOfChild方法就足以实现一个模型。
但是,如果有消息加入或是删除,MessageBox同样会通知监听器。然我们来看看MessageBoxTreeModel中的 newMessage()方法,并以此来说明如何使用,MessageBox只是将将新消息的ID传送给监听器,此时,第一件要做的事情就是在 MessageBox中查询父节点消息的ID,如果父节点是空的,就将这个ID添加到数组表中,然后再计算出ID是在何处添加到列表中的;如果不节点不是空的,我们就要在MessageBox中查询索引值,最后一件要做的事情是父节点消息的TreePath,这样我们就可以将变化通知给 MessageBox,TreePath是一个从根到树中的某一点运行的简单节点对象列表,该列表中列出了其中所有的节点。在我们的例子中,有我们自己的 getTreePath()方法,如果递交到getTreePath()的方法是根对象,我们将返回包含了根节点的TreePath,否则,我们就要遍历整个父节点列表(在MessageBox中查询树的关系),同时将每个节点添加到列表中直到遍历完所有的父节点,然后我们将根节点放在列表的末尾,反转列表并从中创建一个TreePath。
现在,我们有了父节点的路径、子节点添加的位置和添加的消息的ID,这样就能调用AbstractTreeModel的方法了, fireChildAdded将会执行适当的TreeModelEvent,你只需直接调用fireChildAdded,尽管有不必影响Swing事件线程而有改变树的好机会,而这些Swing线程可能会导致用户界面的破坏。所以我们使用SwingUtilities.invokeLater()方法,这将在等待运行的Swing事件队列中会加入一个可以运行的对象,不要试图计算TreePath和这个可以运行的对象的索引信息,在经常迅速变化的数据源中,这在未来任何时间都有可能发生,而取得路径信息也许不太可能。在deleteMessage方法中,使用了一个相似的想法,尽管它所需要的索引值和父节点ID的提示作为消息,而这些数据已经不再数据源中了;我们将fireChildRemoved方法与deleteMessage一起调用。
有了这些实现了的方法,我们现在已经有了一个个动态的树模型,在MessagesFrame中有一个简单的用户界面,它可以创建一个 MessageBox,然后利用MessageTreeBoxModel创建一个JTree,这是“禅宗”方法的最后一部分。你需要安装一个 CellRenderer树,它知道怎样处理ID值,在这个例子中,MessageRenderer类将文本设为一个直接从MessageBox获取的字符串。
当运行示例代码的时候,会看到最后有一个快速响应的树,它可以处理大量的更新和大量节点而不必锁定整个用户界面。只有ExpandAll按钮会占用一些时间,因为计算出完整的扩展开的树的高度和宽度需要进行大量复杂的计算。你可以对此进行一些改进,使用setRowHeight()方法来限制树的高度,并将setLargeModel()设为setLargeModel(true),就不会在缓冲区经常计算树的规模了,这样做可以提高少许的性能,虽然对规模大一些的树的计算速度有所提高,但是却失去了可灵活变化高度的树元素的灵活性。
希望通过这棵没有根的树和“禅宗”的编程方法可以让你对Swing的JTree有更加深入的理解。
你可以下载这边指南的所有源代码,他们在这个.zip文件中。
Walker-Morgan是一位具有顾问资格的开发人员,专门研究Java和用户-用户的消息及会议信息交互。
