zeyuphoenix

          愿我愛(ài)的人快樂(lè),愿愛(ài)我的人快樂(lè),為了這些,我愿意不快樂(lè).

          JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二)

          這次我們用樹(shù)實(shí)現(xiàn)一個(gè)比較綜合的例子,做一個(gè)類似Windows的資源管理器,先看Windows,如下圖:

          接著就是我們的實(shí)現(xiàn)了,路是一步一步走的,先看最基礎(chǔ)的實(shí)現(xiàn),這個(gè)例子是我在網(wǎng)上看到的,雖然簡(jiǎn)單,起碼是一種思路:

          效果如下圖:

          先說(shuō)說(shuō)這個(gè)實(shí)現(xiàn)的思路吧,首先創(chuàng)建樹(shù),再創(chuàng)建一個(gè)根節(jié)點(diǎn),然后取得盤符,把盤符放置在根節(jié)點(diǎn)下面,然后增加樹(shù)的監(jiān)聽(tīng),當(dāng)樹(shù)的節(jié)點(diǎn)展開(kāi)時(shí),取得當(dāng)前節(jié)點(diǎn)下的所有文件和文件夾,加入到節(jié)點(diǎn)下,刷新樹(shù),展開(kāi)到指定位置就可以了.

          先是創(chuàng)建根節(jié)點(diǎn)的:

          public DefaultMutableTreeNode createRootNode(){ 

                File dir = new File("."); 

                DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(ROOT_NAME); 

                for(int i = 0; i < dir.listRoots().length; i++){ 

                    if(dir.listRoots()[i].isDirectory()){ 

                        String rootPath = dir.listRoots()[i].getPath(); 

                        this.treeNode = new DefaultMutableTreeNode(rootPath); 

                        rootNode.add(this.treeNode); 

                        this.treeNode = null

                    } 

                } 

                  return rootNode; 

              }

          然后是增加監(jiān)聽(tīng),這里我們監(jiān)聽(tīng)展開(kāi)事件:

          this.tree.addTreeExpansionListener(this); 

          然后是處理事件:

              @Override

          publicvoid treeExpanded(TreeExpansionEvent event) { 

          先取得選擇的節(jié)點(diǎn)對(duì)象:

          this.selectNode

          =(DefaultMutableTreeNode)event.getPath().getLastPathComponent();

          然后取得節(jié)點(diǎn)的絕對(duì)路徑:

              String path = event.getPath().toString(); 

          然后根據(jù)路徑在節(jié)點(diǎn)下添加子節(jié)點(diǎn):

              publicvoid addTreeNode(DefaultMutableTreeNode node, File dir) {

                 if (node == null || dir == null) {

                     return;

                 }

                 if (!dir.isDirectory()) {

                     return;

                 }

                 if (!node.isRoot()) {

                     // get all files in node

                     File file[] = dir.listFiles();

                     for (int i = 0; i < file.length; i++) {

                        // hidden is not show

                        if (file[i].isDirectory() && !file[i].isHidden()) {

                            // create node

                            this.treeNode = new DefaultMutableTreeNode(dir.list()[i]);

                            // add to tree

                            ((DefaultTreeModel) this.jt.getModel()).insertNodeInto(

                                   treeNode, node, node.getChildCount());

                            this.treeNode = null;

                        }

                     }

                 }

              }

          同樣的收起事件也要處理:

              @Override

          publicvoid treeCollapsed(TreeExpansionEvent event) { 

          最后把樹(shù)放置在JScrollPane上就可以了.

          this.jscroolpane.setViewportView(this.tree); 

          這樣一個(gè)簡(jiǎn)單的資源管理樹(shù)就完成了,下面我們說(shuō)說(shuō)它的問(wèn)題:

               圖片和外觀和Windows相差太大

          這個(gè)我們可以通過(guò)設(shè)置L&F和通過(guò)前面寫(xiě)的Renderer那樣設(shè)置新的圖片解決,不是大問(wèn)題.

               文件夾里文件多時(shí)展開(kāi)會(huì)很慢,會(huì)導(dǎo)致界面假死

          這個(gè)我們可以自己寫(xiě)一個(gè)緩加載的TreeNode,讓它繼承于DefaultMutableTreeNode,在它里面定義加載標(biāo)示,然后使用SwingWorker或者多線程方式使Tree平穩(wěn)加載,雖然麻煩,但是也可以解決.

               Tree點(diǎn)擊假死時(shí),用戶會(huì)以為出現(xiàn)問(wèn)題,胡亂點(diǎn)擊會(huì)加載多個(gè)事件

          這個(gè)問(wèn)題其實(shí)是Swing事件機(jī)制的問(wèn)題,其實(shí)是沒(méi)辦法解決的,因?yàn)榭倳?huì)存在耗時(shí)的操作的,不等待是不可能的.但我們可以做更好的用戶體驗(yàn)來(lái)避免這個(gè)問(wèn)題,這里我想到的解決辦法是在Tree上繪制一層GlassPane,屏蔽所有事件,提示用戶,等加載完成后,取消GlassPane界面.

               只有我的電腦的基本文件,沒(méi)有網(wǎng)上鄰居之類的

          這個(gè)問(wèn)題很難解決,涉及到網(wǎng)上鄰居就存在網(wǎng)絡(luò)的問(wèn)題了,還需要網(wǎng)絡(luò)連接和掃描,開(kāi)始我的思路是使用Apachecommons-client,后來(lái)發(fā)現(xiàn)有人給出了更好的辦法,使用JavaJFileChooser,Java已經(jīng)實(shí)現(xiàn)了很多我們需要實(shí)現(xiàn)的.

               取得的資源管理樹(shù)的子目錄是亂序的

          這個(gè)很好解決,使我們的TreeNode實(shí)現(xiàn)Comparable接口就可以了.

          為了解決這五個(gè)問(wèn)題我們做的改進(jìn)版:

          首先我們解決問(wèn)題一,看看我們的代碼:

          節(jié)點(diǎn)的圖片的樣式問(wèn)題我們可以設(shè)置Renderer,又因?yàn)檫@些圖片可以在JFileChooserUI中取得,我們先參照JFileChooserUI做一個(gè)FileView:

              // ***********************

              // * FileView operations *

              // ***********************

              protectedclass BasicFileView extends FileView {

          復(fù)寫(xiě)它的方法:

                 @Override

                 public String getName(File f) {

                     // Note: Returns display name rather than file name

                     String fileName = null;

                     if (f != null) {

                        fileName = chooser.getFileSystemView().getSystemDisplayName(f);

                     }

                     return fileName;

                 }

          這個(gè)是顯示名字.

                 @Override

                 public String getDescription(File f) {

                     return f.getName();

                 }

          這個(gè)是描述

                 @Override

                 public String getTypeDescription(File f) {

                     String type = chooser.getFileSystemView().getSystemTypeDescription(

                            f);

                     if (type == null) {

                        if (f.isDirectory()) {

                            type = directoryDescriptionText;

                        } else {

                            type = fileDescriptionText;

                        }

                     }

                     return type;

                 }

          這個(gè)是文件類別

                 @Override

                 public Icon getIcon(File f) {

          這個(gè)是圖片表示.

          這樣我們構(gòu)建這個(gè)FileView之后我們需要的圖片和名字就都可以取得了.

          然后是我們的Renderer了:

          privateclass FileSystemTreeRenderer extends DefaultTreeCellRenderer {

          復(fù)寫(xiě)它的方法,設(shè)置我們從FileView取得圖片和名字:

          @Override

          public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row,

               boolean hasFocus) {

               setText(getFileView(chooser).getName(node.getFile()));

               setIcon(getFileView(chooser).getIcon(node.getFile()));

          然后設(shè)置到樹(shù)上:

               tree.setCellRenderer(new FileSystemTreeRenderer());

          看看效果:

          是不是和Windows的很接近了,設(shè)置L&F,如下圖:

          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

          然后解決問(wèn)題二,我們不能用樹(shù)的原始節(jié)點(diǎn)了,用我們自己構(gòu)造的,繼承于它:

          publicabstractclass LazyMutableTreeNode extends DefaultMutableTreeNode {

          增加一個(gè)屬性:

              /** is node load. */

              privatebooleanloaded = false;

          提供一個(gè)虛方法給子類實(shí)現(xiàn):

              protectedabstractvoid loadChildren();

          然后是我們的實(shí)現(xiàn):

          privateclass FileTreeNode extends LazyMutableTreeNode {

          復(fù)寫(xiě)它的方法,load不允許加載:

                  @Override

                 publicboolean isLeaf() {

                     if (!isLoaded()) {

                        returnfalse;

                     } else {

                        returnsuper.isLeaf();

                     }

                 }

          還有它的現(xiàn)實(shí)名字:

                 @Override

                 public String toString() {

                     returnchooser.getFileSystemView().getSystemDisplayName(

                            (File) getUserObject());

                  }

          實(shí)現(xiàn)虛方法:

              @Override

                 protectedvoid loadChildren() {

                     FileTreeNode[] nodes = getChildren();

                     for (int i = 0, c = nodes.length; i < c; i++) {

                        add(nodes[i]);

                     }

                 }

          這樣問(wèn)題二就解決了,同時(shí)也可以在這里解決我們的問(wèn)題五,使我們的TreeNode實(shí)現(xiàn)Comparable接口:

              privateclass FileTreeNode extends LazyMutableTreeNode implements

                     Comparable<Object> {

          然后實(shí)現(xiàn)方法:

              @Override

                 publicint compareTo(Object o) {

                     if (!(o instanceof FileTreeNode)) {

                        return 1;

                     }

                     return getFile().compareTo(((FileTreeNode) o).getFile());

                 }

          最后在我們使用時(shí):

                 // sort directories, FileTreeNode implements Comparable

                 FileTreeNode[] result = (FileTreeNode[]) nodes

                        .toArray(new FileTreeNode[0]);

                 Arrays.sort(result);

          nodes.add(new FileTreeNode(result[i]));

          這樣我們加入的節(jié)點(diǎn)文件夾就都是排序的了.

          然后我們解決問(wèn)題四,三比較麻煩留在最后:

          構(gòu)建這個(gè)組件時(shí),我們先構(gòu)建JFileChooser

          JFileChooser chooser = new JFileChooser();

          增加監(jiān)聽(tīng):

              protectedvoid installListeners() {

                 tree.addTreeSelectionListener(new SelectionListener());

                 chooser.getActionMap().put("refreshTree", new UpdateAction());

                  chooser.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(

                        KeyStroke.getKeyStroke("F5"), "refreshTree");

                 chooser.addPropertyChangeListener(new ChangeListener());

              }

          在監(jiān)聽(tīng)中展開(kāi)樹(shù)時(shí),使用JFileChooser的方法:

              /**

               * tree node select change.

               */

              privateclass SelectionListener implements TreeSelectionListener {

                 @Override

                 publicvoid valueChanged(TreeSelectionEvent e) {

                     getApproveSelectionAction()

                            .setEnabled(tree.getSelectionCount() > 0);

                     setSelectedFiles();

                     // the current directory is the one currently selected

                     TreePath currentDirectoryPath = tree.getSelectionPath();

                     if (currentDirectoryPath != null) {

                        File currentDirectory = ((FileTreeNode) currentDirectoryPath

                               .getLastPathComponent()).getFile();

                        chooser.setCurrentDirectory(currentDirectory);

                     }

                 }

              }

          這樣我們所有的目錄結(jié)構(gòu)就不需要自己去循環(huán)構(gòu)建了,使用JFileChooser為我們提供好的就可以了,如下圖,網(wǎng)上鄰居也有了,問(wèn)題四完成了:

          最后我們來(lái)解決問(wèn)題三,為什么會(huì)假死,是因?yàn)槲募A多或者網(wǎng)速慢導(dǎo)致的,解決辦法當(dāng)然是多線程,但是多線程在Swing里容易出現(xiàn)線程不安全,因?yàn)樗辉?/span>ADT,這里我們使用SwingWorker,監(jiān)聽(tīng)樹(shù)的展開(kāi)事件:

          tree.addTreeExpansionListener(new TreeExpansion());

          處理它:

              privateclass TreeExpansion implements TreeExpansionListener {

                 @Override

                 publicvoid treeCollapsed(TreeExpansionEvent event) {

                 }

                 @Override

                 publicvoid treeExpanded(TreeExpansionEvent event) {

                     // ensure children gets expanded later

                     if (event.getPath() != null) {

                     Object lastElement = event.getPath().getLastPathComponent();

                        if (lastElement instanceof FileTreeNode && useNodeQueue)

                            if (((FileTreeNode) lastElement).isLoaded()) {

          慢主要是在這里的處理,我們把它放在SwingWorker里面:

          new WorkerQueue(node, tree, glassPane).execute();

          然后看這個(gè)類:

              privatestaticfinalclass WorkerQueue extends

                     SwingWorker<Void, FileTreeNode> {

          復(fù)寫(xiě)它的方法,處理我們的TreeNode添加事件:

              @Override

              protected Void doInBackground() throws Exception {

                 glassPanel.setVisible(true);

                 for (Enumeration<?> e = node.children(); e.hasMoreElements();) {

                     publish((FileTreeNode) e.nextElement());

                 }

                 returnnull;

              }

              @Override

              protectedvoid process(List<FileTreeNode> chunks) {

                 for (FileTreeNode fileTreeNode : chunks) {

                     fileTreeNode.getChildCount();

                 }

              }

              @Override

              protectedvoid done() {

                 glassPanel.setVisible(false);

                 tree.repaint();

              }

          然后是處理我們?cè)谡归_(kāi)節(jié)點(diǎn)時(shí)屏蔽所有的鼠標(biāo)點(diǎn)擊并給以用戶提示,這里我們自己繪制一個(gè)Component,把它設(shè)置為GlassPane,屏蔽所有事件:

          /**

           */

          publicclass GlassPane extends JComponent {

          屏蔽所有事件,只能獲得焦點(diǎn):

              // blocks all user input

              addMouseListener(new MouseAdapter() {

              });

              addMouseMotionListener(new MouseMotionAdapter() {

              });

              addKeyListener(new KeyAdapter() {

              });

              setFocusTraversalKeysEnabled(false);

              addComponentListener(new ComponentAdapter() {

                 publicvoid componentShown(ComponentEvent evt) {

                     requestFocusInWindow();

                 }

              });

          然后是繪制:

              @Override

              protectedvoid paintComponent(Graphics g) {

          先繪制整體背景:

                 // gets the current clipping area

                 Rectangle clip = g.getClipBounds();

                 // sets a 65% translucent composite

                 AlphaComposite alpha = AlphaComposite.SrcOver.derive(0.65f);

                 Composite composite = g2.getComposite();

                 g2.setComposite(alpha);

                 // fills the background

                 g2.setColor(getBackground());

                 g2.fillRect(clip.x, clip.y, clip.width, clip.height);

                 g2.setComposite(composite);

          然后繪制一張?zhí)崾緢D片,本來(lái)想繪制一個(gè)滾動(dòng)的等待圖標(biāo),實(shí)在是沒(méi)心情寫(xiě)了,隨便Google了張圖片放上去了.

              if (image == null) {

                 try {

                    image = ImageIO.read(getClass().getResource("wait2.jpg"));

                 } catch (IOException ex) {

                     ex.printStackTrace();

                 }

              }

              g.drawImage(image, getWidth() / 2 - 40, getHeight() / 2

                 - 80, 120, 120, null);

          通過(guò)設(shè)置畫(huà)面的GlassPane就可以了

              Component glassPane = new GlassPane();

              frame.getRootPane().setGlassPane(glassPane);

          最終效果如下圖:

          到此為止,關(guān)于樹(shù)的操作基本就完成了,下面再開(kāi)個(gè)專題講下有CheckBoxJTree,至于JTree的拖拽,因?yàn)楹推渌?/span>Component基本是一致的,就留在以后拖拽專題一起寫(xiě)了,總之,JTree還是不算復(fù)雜的一個(gè)組件.

          posted on 2010-04-22 22:51 zeyuphoenix 閱讀(5431) 評(píng)論(11)  編輯  收藏 所屬分類: JTree的使用

          評(píng)論

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2010-04-23 08:57 股海e程

          好文章!  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2010-04-24 09:20 羅萊家紡

          你們呢是明年的南方  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2010-04-24 14:18 fivesmallq

          樓主的文章一直在訂閱拜讀。寫(xiě)的很好  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2010-06-11 15:27 xdelxia

          老大能給我份源碼么,我看得不是很懂啊,763823729@qq.com這是我的郵箱,謝謝老大,請(qǐng)一定要給我份啊,我最近做的項(xiàng)目遇到了個(gè)關(guān)于圖片的加載問(wèn)題,我現(xiàn)在解決不了!  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2011-10-01 09:17 bug

          真的很厲害.但由于本人java水平不高.請(qǐng)問(wèn)能否給分原代碼讓我參考學(xué)習(xí)呢?我的郵箱1280074964@qq.com 謝謝~  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2011-10-11 15:42 allenxiaomai

          寫(xiě)的很好呢。能不能發(fā)給我一份源代碼讓我仔細(xì)學(xué)習(xí)一下呢?郵箱:476847992@qq.com,謝謝啦。  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2011-10-14 12:29 1788455@qq.com

          你好 能給我一份Swing的源碼么  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二)[未登錄](méi) 2011-10-22 16:46 wei

          寫(xiě)得很好啊,可以給我一份源碼嗎?我研究了很久,都不知道怎么寫(xiě)。。謝謝
          jidi123698745@tom.com  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2012-12-06 18:22 真惡心

          你這樣寫(xiě),別人拿去怎么用,下載源碼的地方又不能用?什么意思。如果僅僅是自己看的話,那就隱藏起來(lái)吧。  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二) 2015-04-05 16:25 張森洋

          請(qǐng)問(wèn)參考JFileChooser的UI做的那個(gè)FileView類怎么寫(xiě)?  回復(fù)  更多評(píng)論   

          # re: JTree--樹(shù)(節(jié)點(diǎn)渲染和資源管理器加載)(二)[未登錄](méi) 2015-11-12 23:03 ch

          能給份 Jtable 、以及Swing 其他組件的代碼 258440352@qq.com  回復(fù)  更多評(píng)論   

          導(dǎo)航

          <2015年4月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          統(tǒng)計(jì)

          常用鏈接

          留言簿(52)

          隨筆分類

          隨筆檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 连南| 华安县| 房产| 仙居县| 云龙县| 长汀县| 固原市| 青阳县| 迭部县| 沙湾县| 集贤县| 延寿县| 嫩江县| 卢氏县| 云浮市| 聂拉木县| 赤城县| 高清| 赤峰市| 恭城| 屏边| 横山县| 黄石市| 安吉县| 商南县| 峨眉山市| 林芝县| 莱州市| 浪卡子县| 井冈山市| 东乡| 平顶山市| 黑河市| 铜川市| 鞍山市| 民县| 蒙自县| 观塘区| 永清县| 延津县| 团风县|