Posted on 2012-11-02 15:20
TWaver 閱讀(2489)
評論(0) 編輯 收藏
這篇文章介紹的內(nèi)容其實跟TWaver的組件沒有任何關(guān)系,但是個人覺得還是挺有意思的,所以發(fā)出來共享一下。這個文件樹(如下圖)完全基于swing的JTree組件實現(xiàn),先看一下最后我們完成的效果:
運行截圖:
功能介紹:
- 樹狀呈現(xiàn)文件系統(tǒng)結(jié)構(gòu)
- 文件的圖標(biāo)應(yīng)該使用系統(tǒng)圖標(biāo)
- 鼠標(biāo)滑過時改變當(dāng)前節(jié)點的背景色(如圖中"Windows"節(jié)點的磚紅色背景)
先看一下類結(jié)構(gòu):
- FileTreeTest 程序入口
- FileTree 文件樹,從JTree繼承
- FileNode 封裝的文件節(jié)點,包括文件名,文件圖標(biāo)和實際的File類及其它標(biāo)識
- FileTreeRenderer 定制的節(jié)點渲染器,從DefaultTreeCellRenderer繼承
- FileTreeModel 定制的TreeModel,從DefaultTreeModel繼承
考慮到系統(tǒng)文件會很多,在程序開始就初始化整個樹是不現(xiàn)實的,所以我采取一種延遲加載的策略,只有當(dāng)要展開某個節(jié)點的時候,才去初始化它的子節(jié)點,在FileTree的構(gòu)造里加一個監(jiān)聽:
1 addTreeWillExpandListener(new TreeWillExpandListener() {
2 @Override
3 public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
4 DefaultMutableTreeNode lastTreeNode =
5 (DefaultMutableTreeNode) event.getPath().getLastPathComponent();
6 FileNode fileNode = (FileNode) lastTreeNode.getUserObject();
7 if (!fileNode.isInit) {
8 File[] files;
9 if (fileNode.isDummyRoot) {
10 files = fileSystemView.getRoots();
11 } else {
12 files = fileSystemView.getFiles(
13 ((FileNode) lastTreeNode.getUserObject()).file,
14 false);
15 }
16 for (int i = 0; i < files.length; i++) {
17 //文件名和圖標(biāo)都是通過fileSystemView得到
18 FileNode childFileNode = new FileNode(
19 fileSystemView.getSystemDisplayName(files[i]),
20 fileSystemView.getSystemIcon(files[i]), files[i],
21 false);
22 DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(childFileNode);
23 lastTreeNode.add(childTreeNode);
24 }
25 //通知模型節(jié)點發(fā)生變化
26 DefaultTreeModel treeModel1 = (DefaultTreeModel) getModel();
27 treeModel1.nodeStructureChanged(lastTreeNode);
28 }
29 //更改標(biāo)識,避免重復(fù)加載
30 fileNode.isInit = true;
31 }
32 @Override
33 public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
34
35 }
36 });
當(dāng)然這種方式需要TableModel的配合,我重載了DefaultTreeModel,在構(gòu)造里初始化根節(jié)點,然后重載isLeaf方法,具體代碼可以下載附件查看。
接下來思考一下鼠標(biāo)滑過改變背景色怎么實現(xiàn)。
寫這段代碼的時候,我想起來了我剛學(xué)Renderer的時候的一個錯誤的認(rèn)識:誤以為每個節(jié)點都有一個Renderer并且妄圖在Renderer上加監(jiān)聽!在此強調(diào)一下:
Renderer只是一個渲染器,要顯示節(jié)點的時候,JTree就調(diào)用它渲染并畫到屏幕上,但是無論有多少節(jié)點,一個JTree只有一個Renderer!
Renderer上加監(jiān)聽行不通,我們就轉(zhuǎn)換一下思路,在JTree上加鼠標(biāo)move監(jiān)聽,然后repaint鼠標(biāo)所在的節(jié)點。
1 addMouseMotionListener(new MouseAdapter() {
2 @Override
3 public void mouseMoved(MouseEvent e) {
4 //獲得鼠標(biāo)所在的TreePath
5 TreePath path=getPathForLocation(e.getX(), e.getY());
6
7 //計算repaint的區(qū)域并repaint JTree
8 if(path!=null){
9 if(mouseInPath!=null){
10 Rectangle oldRect=getPathBounds(mouseInPath);
11 mouseInPath=path;
12 repaint(getPathBounds(path).union(oldRect));
13 }else{
14 mouseInPath=path;
15 Rectangle bounds=getPathBounds(mouseInPath);
16 repaint(bounds);
17 }
18 }else if(mouseInPath!=null){
19 Rectangle oldRect=getPathBounds(mouseInPath);
20 mouseInPath=null;
21 repaint(oldRect);
22 }
23 }
24 });
需要在JTree中保存鼠標(biāo)滑過的TreePath(mouseInPath),這樣在Renderer中才可以改變mouseInPath的背景色
1 FileTree fileTree=(FileTree)tree;
2 JLabel label= (JLabel) super.getTreeCellRendererComponent(tree,value,sel,expanded,leaf,row,hasFocus);
3
4 DefaultMutableTreeNode node=(DefaultMutableTreeNode)value;
5 FileNode fileNode=(FileNode)node.getUserObject();
6 label.setText(fileNode.name);
7 label.setIcon(fileNode.icon);
8
9 label.setOpaque(false);
10 //如果當(dāng)前渲染的節(jié)點就是鼠標(biāo)滑過的節(jié)點,改變背景色
11 if(fileTree.mouseInPath!=null&&
12 fileTree.mouseInPath.getLastPathComponent().equals(value)){
13 label.setOpaque(true);
14 label.setBackground(new Color(255,0,0,90));
15 }
16 return label;
至此這個文件樹就寫完了!全部代碼在附件,有興趣的同學(xué)可以下載看看,也希望看過的同學(xué)有好的想法來交流一下。 附件:
見原文最下方