Posted on 2012-11-02 15:20
TWaver 閱讀(2481)
評論(0) 編輯 收藏
這篇文章介紹的內容其實跟TWaver的組件沒有任何關系,但是個人覺得還是挺有意思的,所以發出來共享一下。這個文件樹(如下圖)完全基于swing的JTree組件實現,先看一下最后我們完成的效果:
運行截圖:
功能介紹:
- 樹狀呈現文件系統結構
- 文件的圖標應該使用系統圖標
- 鼠標滑過時改變當前節點的背景色(如圖中"Windows"節點的磚紅色背景)
先看一下類結構:
- FileTreeTest 程序入口
- FileTree 文件樹,從JTree繼承
- FileNode 封裝的文件節點,包括文件名,文件圖標和實際的File類及其它標識
- FileTreeRenderer 定制的節點渲染器,從DefaultTreeCellRenderer繼承
- FileTreeModel 定制的TreeModel,從DefaultTreeModel繼承
考慮到系統文件會很多,在程序開始就初始化整個樹是不現實的,所以我采取一種延遲加載的策略,只有當要展開某個節點的時候,才去初始化它的子節點,在FileTree的構造里加一個監聽:
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 //文件名和圖標都是通過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 //通知模型節點發生變化
26 DefaultTreeModel treeModel1 = (DefaultTreeModel) getModel();
27 treeModel1.nodeStructureChanged(lastTreeNode);
28 }
29 //更改標識,避免重復加載
30 fileNode.isInit = true;
31 }
32 @Override
33 public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
34
35 }
36 });
當然這種方式需要TableModel的配合,我重載了DefaultTreeModel,在構造里初始化根節點,然后重載isLeaf方法,具體代碼可以下載附件查看。
接下來思考一下鼠標滑過改變背景色怎么實現。
寫這段代碼的時候,我想起來了我剛學Renderer的時候的一個錯誤的認識:誤以為每個節點都有一個Renderer并且妄圖在Renderer上加監聽!在此強調一下:
Renderer只是一個渲染器,要顯示節點的時候,JTree就調用它渲染并畫到屏幕上,但是無論有多少節點,一個JTree只有一個Renderer!
Renderer上加監聽行不通,我們就轉換一下思路,在JTree上加鼠標move監聽,然后repaint鼠標所在的節點。
1 addMouseMotionListener(new MouseAdapter() {
2 @Override
3 public void mouseMoved(MouseEvent e) {
4 //獲得鼠標所在的TreePath
5 TreePath path=getPathForLocation(e.getX(), e.getY());
6
7 //計算repaint的區域并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中保存鼠標滑過的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 //如果當前渲染的節點就是鼠標滑過的節點,改變背景色
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;
至此這個文件樹就寫完了!全部代碼在附件,有興趣的同學可以下載看看,也希望看過的同學有好的想法來交流一下。 附件:
見原文最下方