JTree也是一個(gè)分層顯示數(shù)據(jù)的組件,它在Swing里復(fù)雜度算是中等,涉及的類不是很多,也沒有復(fù)雜的UI,所以單獨(dú)對(duì)一個(gè)樹來說,構(gòu)造和渲染都不難,對(duì)于樹的操作比較麻煩的是拖拽和與其他組件數(shù)據(jù)交互.
對(duì)于JTree有幾個(gè)比較重要的類和接口: TreeMode是一個(gè)接口,用來構(gòu)建樹的模型,實(shí)現(xiàn)這個(gè)接口之后我們可以把一些比較另類的組件抽象成一棵樹,TreeTable就是這個(gè)原理實(shí)現(xiàn)的;TreeNode是一個(gè)接口,代表樹的節(jié)點(diǎn),DefaultMutableTreeNode是該接口的一個(gè)實(shí)現(xiàn),我們一般構(gòu)建樹使用它就可以了,當(dāng)然也可以繼承它添加新的功能,或者實(shí)現(xiàn)TreeNode把節(jié)點(diǎn)變成其它組件; TreePath是一個(gè)類,負(fù)責(zé)節(jié)點(diǎn)到根的路徑,一般不需要我們來修改;
TreeSelectionModel是一個(gè)接口,表示樹選擇組件的當(dāng)前狀態(tài),只是使用皆可以了,一般除非做很大的改動(dòng),否則不需要修改這個(gè)類;還有就是TreeCellRenderer和TreeCellEditor了,它們是樹的渲染器,實(shí)現(xiàn)特殊效果都需要實(shí)現(xiàn)它們;還有就是TreeUI了,樹的UI比較死板,很少需要修改.
由上面類和接口可以,樹還是一個(gè)不是很復(fù)雜的組件的,除了基本使用外,我們一般會(huì)用到的也就是對(duì)Cell進(jìn)行渲染,替換樹的節(jié)點(diǎn)圖片,或者給節(jié)點(diǎn)加個(gè)CheckBox這些了.
先看樹的簡(jiǎn)單使用,Sun官方給我們提供了例子:
這個(gè)例子只是簡(jiǎn)單的構(gòu)建一棵樹:
// Create the root nodes.
DefaultMutableTreeNode top = new DefaultMutableTreeNode(
"The Java Series");
//add node
createNodes(top);
// Create a tree that allows one selection at a time.
tree = new JTree(top);
tree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
當(dāng)然我們也可以先創(chuàng)建樹,再設(shè)置數(shù)據(jù),這都是JTree的構(gòu)造函數(shù),本質(zhì)上是沒區(qū)別的,最終JTree也是通過setModel設(shè)置數(shù)據(jù)的:
tree = new JTree();
tree.setModel(treeModel);
構(gòu)造完樹之后還要把樹放在JScollPanel上,否則樹顯示會(huì)有問題:
// Create the scroll pane and add the tree to it.
JScrollPane treeView = new JScrollPane(tree);
到此為止,樹就完成了,當(dāng)然Sun也提供了許多方法便捷于我們操作樹:
取得根節(jié)點(diǎn):
// get the
tree root node
TreeNode rootNode = (TreeNode)tree.getModel().getRoot();
取得樹的數(shù)據(jù)模型:
// get the
tree model.
DefaultTreeModel treeModel =
(DefaultTreeModel)tree.getModel();
取得樹的某一個(gè)節(jié)點(diǎn)的位置:
// get
tree node path.
TreePath nodePath = new TreePath(treeModel.getPathToRoot(node));
設(shè)置節(jié)點(diǎn)的展開可見和選擇:
//select
the node.
tree.setSelectionPath(nodePath);
// Ensures
that the node identified by path is currently viewable.
tree.makeVisible(nodePath);
展開后滾動(dòng)至節(jié)點(diǎn)可見:
// Makes
sure all the path components display
tree.scrollPathToVisible(nodePath);
還有一個(gè)是設(shè)置鼠標(biāo)點(diǎn)擊展開樹節(jié)點(diǎn)的(默認(rèn)是雙擊,改成了0就是不可點(diǎn)擊):
// Sets
the number of mouse clicks before a node will expand or close.
tree.setToggleClickCount(1);
另外就是一個(gè)設(shè)置Cell的高度的,但這個(gè)方法我們一般不用,大多時(shí)候是在Renderer里設(shè)置的,因?yàn)檫@樣設(shè)置可能有UI問題:
// Sets
the height of each cell, in pixels.
tree.setRowHeight(15);
其它還有很多,因?yàn)?/span>JTree繼承JComponent,所以它有父類的所有方法,我們通過setXXX和getXXX就可以設(shè)置了.
說完基本的設(shè)置之后,說一下JTree的事件:
JTree繼承JComponent,所以它有JComponent的事件,但我們一般只關(guān)注鼠標(biāo)的鍵盤的:
// add
mouse listener
tree.addMouseListener(this);
// add key
listener
tree.addKeyListener(this);
然后是JTree獨(dú)有的事件:
tree.addTreeExpansionListener(this);
tree.addTreeWillExpandListener(this);
tree.addTreeSelectionListener(this);
分別是樹節(jié)點(diǎn)展開(折疊)、樹節(jié)點(diǎn)將要展開(折疊)和樹節(jié)點(diǎn)選擇事件,我們一般用的多的是addTreeExpansionListener和addTreeSelectionListener事件:
事件處理都不是很復(fù)雜,增加監(jiān)聽后實(shí)現(xiàn)方法就可以了,需要注意的是TreeSelectionListener和MouseListener,它們有事件重疊的地方:但在樹的節(jié)點(diǎn)點(diǎn)擊鼠標(biāo)左鍵時(shí)觸發(fā)這兩個(gè)事件,點(diǎn)擊右鍵時(shí)觸發(fā)鼠標(biāo)事件,所以這兩個(gè)事件都做監(jiān)聽時(shí),處理一定要注意:
先看TreeSelectionListener的處理:
@Override
publicvoid valueChanged(TreeSelectionEvent event) {
然后可以取得TreePath,之后取得Node
// Returns
the current lead path.
TreePath newPath =
event.getNewLeadSelectionPath();
TreePath oldPath =
event.getOldLeadSelectionPath();
或者通過JTree取得Node
JTree tree = (JTree) event.getSource();
tree.getLastSelectedPathComponent();
再看MouseListener的處理:
@Override
publicvoid mouseClicked(MouseEvent e) {
鼠標(biāo)組件場(chǎng)合比較簡(jiǎn)單,因?yàn)檫@個(gè)時(shí)候節(jié)點(diǎn)被選中:
if (SwingUtilities.isLeftMouseButton(e)
&&
e.getClickCount() == 1) {
// Returns the last path component
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) tree
.getLastSelectedPathComponent();
}
右鍵則要得出節(jié)點(diǎn)位置再處理:
if
(SwingUtilities.isRightMouseButton(e)) {
// Returns the row for the specified location.
int selectRow
= tree.getRowForLocation(e.getX(), e.getY());
// Returns the path for the node at the specified
location.
TreePath selectPath = tree.getPathForLocation(e.getX(), e
.getY());
if (selectPath
!= null) {
// set select
tree.setSelectionPath(selectPath);
// get tree node.
DefaultMutableTreeNode treeNode
= (DefaultMutableTreeNode) selectPath.getLastPathComponent();
}
之后有了樹和選擇節(jié)點(diǎn)就可以做自己的業(yè)務(wù)邏輯了.
另外JTree的節(jié)點(diǎn)的增加、修改和刪除也需要看一下,如果我們使用DefaultTreeModel的話,這些操作都很簡(jiǎn)單,通過它的insertNodeInto、
removeNodeFromParent、nodeChanged等方法就可以實(shí)現(xiàn)了,但是如果是我們自己實(shí)現(xiàn)的TreeModel,必須再做完數(shù)據(jù)的操作之后UpdateUI更新界面,
或者學(xué)習(xí)Sun的事件機(jī)制,觸發(fā)事件然后在View層更新畫面,一般都不推薦這么做,除非你的TreeModel的數(shù)據(jù)特別復(fù)雜,沒辦法使用DefaultTreeModel
這個(gè)數(shù)據(jù)容器,例如TreeTable.
到此,除了JTree的Renderer和Editor之外,只剩下JTree的取數(shù)據(jù)了.JTree的數(shù)據(jù)取得我們一般都是用遞歸,這兒寫個(gè)從任何節(jié)點(diǎn)遞歸其下所有的例子:
private
List<TreeNode> treeNodeList = new
ArrayList<TreeNode>();
publicvoid getChildNodeList(TreeNode treeNode) {
if (treeNode
== null) {
thrownew RuntimeException("error treenode.");
}
if (treeNodeList == null) {
treeNodeList = new ArrayList<TreeNode>();
}
if
(treeNode.getChildCount() >= 0) {
for
(Enumeration<TreeNode> e = treeNode.children(); e
.hasMoreElements();) {
TreeNode childNode = (TreeNode)
e.nextElement();
treeNodeList.add(childNode);
getChildNodeList(childNode);
}
}
}
最后我們看一個(gè)Renderer的例子,Sun官方提供的:
首先看它的Renderer實(shí)現(xiàn),這個(gè)是直接繼承DefaultTreeCellRenderer
privateclass MyRenderer extends DefaultTreeCellRenderer {
然后復(fù)寫它的getTreeCellRendererComponent方法:
@Override
public Component
getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
它的默認(rèn)實(shí)現(xiàn)時(shí)一個(gè)JLabel,在里面我們就可以設(shè)置它的效果了,這里設(shè)置一個(gè)圖片:
setIcon(tutorialIcon);
當(dāng)然我們也可以繼承其它組件,實(shí)現(xiàn)TreeCellRenderer接口,實(shí)現(xiàn)更多效果.
使用很簡(jiǎn)單,直接使用JTree的setCellRenderer方法就可以了:
tree.setCellRenderer(new MyRenderer(tutorialIcon));
總結(jié),基本樹的操作和呈現(xiàn)設(shè)置都這里就完成了,可以看出,樹還是比較簡(jiǎn)單的,后面我預(yù)計(jì)還有兩個(gè)主題,一個(gè)是關(guān)于JTree的Renderer和Editor的,在那兒實(shí)現(xiàn)一棵樹的節(jié)點(diǎn)的圖片特性,可用與否,選擇隱藏等;另外就是關(guān)于樹的選擇框了,其實(shí)也是Renderer和Editor的使用,只不過為了邏輯和應(yīng)用,做的更復(fù)雜.