`
zeyuphoenix
  • 浏览: 57880 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JTree--树(节点渲染和资源管理器加载)(二)

 
阅读更多
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="ProgId" content="Word.Document"> <meta name="Generator" content="Microsoft Word 12"> <meta name="Originator" content="Microsoft Word 12"> <link rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cphoenix%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> <link rel="Edit-Time-Data" href="file:///C:%5CDOCUME%7E1%5Cphoenix%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_editdata.mso"> <!--[if !mso]> <style> v":* {behavior:url(#default#VML);} o":* {behavior:url(#default#VML);} w":* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} </style> <![endif]--><link rel="themeData" href="file:///C:%5CDOCUME%7E1%5Cphoenix%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> <link rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5Cphoenix%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml"> <!--[if gte mso 9]><xml> Normal 0 false 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE </xml><![endif]--><!--[if gte mso 9]><![endif]--><style> <!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4;} @font-face {font-family:Calibri; panose-1:0 0 0 0 0 0 0 0 0 0; mso-font-alt:"Times New Roman";} @font-face {font-family:""@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal { mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Calibri","serif"; mso-bidi-font-family:"Times New Roman";} p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph { margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; text-indent:21.0pt; font-size:10.5pt; font-family:"Calibri","serif"; mso-bidi-font-family:"Times New Roman";} .MsoChpDefault { mso-bidi-font-family:"Times New Roman";} /* Page Definitions */ @page {} @page Section1 {size:595.3pt 841.9pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; layout-grid:15.6pt;} div.Section1 {page:Section1;} /* List Definitions */ @list l0 {} @list l0:level1 { margin-left:18.0pt; text-indent:-18.0pt;} ol {margin-bottom:0cm;} ul {margin-bottom:0cm;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.5pt; font-family:"Calibri","serif";} </style> <![endif]-->

这次我们用树实现一个比较综合的例子,做一个类似Windows的资源管理器,先看Windows,如下图:

<!--[if gte vml 1]> <![endif]-->

接着就是我们的实现了,路是一步一步走的,先看最基础的实现,这个例子是我在网上看到的,虽然简单,起码是一种思路:

效果如下图:

<!--[if gte vml 1]> <![endif]-->

先说说这个实现的思路吧,首先创建树,再创建一个根节点,然后取得盘符,把盘符放置在根节点下面,然后增加树的监听,当树的节点展开时,取得当前节点下的所有文件和文件夹,加入到节点下,刷新树,展开到指定位置就可以了.

先是创建根节点的:

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; 

    }

然后是增加监听,这里我们监听展开事件:

this.tree.addTreeExpansionListener(this); 

然后是处理事件:

    @Override

publicvoid treeExpanded(TreeExpansionEvent event) { 

先取得选择的节点对象:

this.selectNode

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

然后取得节点的绝对路径:

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

然后根据路径在节点下添加子节点:

    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) { 

最后把树放置在JScrollPane上就可以了.

this.jscroolpane.setViewportView(this.tree); 

这样一个简单的资源管理树就完成了,下面我们说说它的问题:

     图片和外观和Windows相差太大

这个我们可以通过设置L&F和通过前面写的Renderer那样设置新的图片解决,不是大问题.

     文件夹里文件多时展开会很慢,会导致界面假死

这个我们可以自己写一个缓加载的TreeNode,让它继承于DefaultMutableTreeNode,在它里面定义加载标示,然后使用SwingWorker或者多线程方式使Tree平稳加载,虽然麻烦,但是也可以解决.

     Tree点击假死时,用户会以为出现问题,胡乱点击会加载多个事件

这个问题其实是Swing事件机制的问题,其实是没办法解决的,因为总会存在耗时的操作的,不等待是不可能的.但我们可以做更好的用户体验来避免这个问题,这里我想到的解决办法是在Tree上绘制一层GlassPane,屏蔽所有事件,提示用户,等加载完成后,取消GlassPane界面.

     只有我的电脑的基本文件,没有网上邻居之类的

这个问题很难解决,涉及到网上邻居就存在网络的问题了,还需要网络连接和扫描,开始我的思路是使用Apachecommons-client,后来发现有人给出了更好的办法,使用JavaJFileChooser,Java已经实现了很多我们需要实现的.

     取得的资源管理树的子目录是乱序的

这个很好解决,使我们的TreeNode实现Comparable接口就可以了.

为了解决这五个问题我们做的改进版:

首先我们解决问题一,看看我们的代码:

节点的图片的样式问题我们可以设置Renderer,又因为这些图片可以在JFileChooserUI中取得,我们先参照JFileChooserUI做一个FileView:

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

    // * FileView operations *

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

    protectedclass BasicFileView extends FileView {

复写它的方法:

       @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;

       }

这个是显示名字.

       @Override

       public String getDescription(File f) {

           return f.getName();

       }

这个是描述

       @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;

       }

这个是文件类别

       @Override

       public Icon getIcon(File f) {

这个是图片表示.

这样我们构建这个FileView之后我们需要的图片和名字就都可以取得了.

然后是我们的Renderer了:

privateclass FileSystemTreeRenderer extends DefaultTreeCellRenderer {

复写它的方法,设置我们从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()));

然后设置到树上:

     tree.setCellRenderer(new FileSystemTreeRenderer());

看看效果:

<!--[if gte vml 1]> <![endif]-->

是不是和Windows的很接近了,设置L&F,如下图:

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

<!--[if gte vml 1]> <![endif]-->

然后解决问题二,我们不能用树的原始节点了,用我们自己构造的,继承于它:

publicabstractclass LazyMutableTreeNode extends DefaultMutableTreeNode {

增加一个属性:

    /** is node load. */

    privatebooleanloaded = false;

提供一个虚方法给子类实现:

    protectedabstractvoid loadChildren();

然后是我们的实现:

privateclass FileTreeNode extends LazyMutableTreeNode {

复写它的方法,load不允许加载:

        @Override

       publicboolean isLeaf() {

           if (!isLoaded()) {

              returnfalse;

           } else {

              returnsuper.isLeaf();

           }

       }

还有它的现实名字:

       @Override

       public String toString() {

           returnchooser.getFileSystemView().getSystemDisplayName(

                  (File) getUserObject());

        }

实现虚方法:

    @Override

       protectedvoid loadChildren() {

           FileTreeNode[] nodes = getChildren();

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

              add(nodes[i]);

           }

       }

这样问题二就解决了,同时也可以在这里解决我们的问题五,使我们的TreeNode实现Comparable接口:

    privateclass FileTreeNode extends LazyMutableTreeNode implements

           Comparable<Object> {

然后实现方法:

    @Override

       publicint compareTo(Object o) {

           if (!(o instanceof FileTreeNode)) {

              return 1;

           }

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

       }

最后在我们使用时:

       // sort directories, FileTreeNode implements Comparable

       FileTreeNode[] result = (FileTreeNode[]) nodes

              .toArray(new FileTreeNode[0]);

       Arrays.sort(result);

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

这样我们加入的节点文件夹就都是排序的了.

然后我们解决问题四,三比较麻烦留在最后:

构建这个组件时,我们先构建JFileChooser

JFileChooser chooser = new JFileChooser();

增加监听:

    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());

    }

在监听中展开树时,使用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);

           }

       }

    }

这样我们所有的目录结构就不需要自己去循环构建了,使用JFileChooser为我们提供好的就可以了,如下图,网上邻居也有了,问题四完成了:

<!--[if gte vml 1]> <![endif]-->

最后我们来解决问题三,为什么会假死,是因为文件夹多或者网速慢导致的,解决办法当然是多线程,但是多线程在Swing里容易出现线程不安全,因为它不在ADT,这里我们使用SwingWorker,监听树的展开事件:

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();

然后看这个类:

    privatestaticfinalclass WorkerQueue extends

           SwingWorker<Void, FileTreeNode> {

复写它的方法,处理我们的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();

    }

然后是处理我们在展开节点时屏蔽所有的鼠标点击并给以用户提示,这里我们自己绘制一个Component,把它设置为GlassPane,屏蔽所有事件:

/**

 */

publicclass GlassPane extends JComponent {

屏蔽所有事件,只能获得焦点:

    // 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);

然后绘制一张提示图片,本来想绘制一个滚动的等待图标<!--[if gte vml 1]> <![endif]-->,实在是没心情写了,随便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);

通过设置画面的GlassPane就可以了

    Component glassPane = new GlassPane();

    frame.getRootPane().setGlassPane(glassPane);

最终效果如下图:

<!--[if gte vml 1]> <![endif]-->

到此为止,关于树的操作基本就完成了,下面再开个专题讲下有CheckBoxJTree,至于JTree的拖拽,因为和其它Component基本是一致的,就留在以后拖拽专题一起写了,总之,JTree还是不算复杂的一个组件.

分享到:
评论

相关推荐

    JTree2.0 目录树

    - **TreeModel**:`javax.swing.tree.TreeModel`接口定义了树数据的模型,负责管理节点数据和结构。 - **TreeCellRenderer**:渲染树节点的外观,可以通过自定义实现来改变节点的显示样式。 - **...

    JTree2.0 目录树demo源码

    - `JTree2.0 目录树demo`:这个文件很可能是包含`JTree`示例代码的Java源文件或者包含相关配置和资源的目录。要运行此Demo,你需要具备Java开发环境,并根据`readme.md`的指示进行编译和执行。 4. **源码解析** -...

    JTree组件资料

    - 在`MHT`文件中可能包含具体的示例代码,如创建一个简单的`JTree`,添加监听器,或者展示如何自定义节点渲染。 通过理解以上概念并结合提供的资源,你可以深入了解`JTree`组件的使用,并将其有效地应用于Java ...

    JTree2.0

    6. **性能优化**:讨论懒加载策略,仅在需要时加载子节点以提高大型树的性能。 7. **定制外观**:介绍如何使用`TreeCellRenderer`改变节点的显示方式,包括文字颜色、字体和背景色。 **index.htm** 和 **懒人图库....

    jTree添加图片

    总的来说,添加图片到jTree节点是提升界面美观度和用户体验的有效手段。通过自定义渲染器,我们可以根据业务需求定制节点的显示方式,使树形结构更加直观易懂。这不仅增加了程序的交互性,也提高了用户的满意度。在...

    Swing展示的文件系统树

    总结起来,使用`JTree`组件在Swing中展示文件系统树,不仅涉及`JTree`、`DefaultTreeModel`、`TreeNode`和`ImageIcon`的使用,还涵盖了事件监听、自定义渲染和编辑行为等多个方面。通过熟练掌握这些知识,开发者可以...

    IconNode.zip_jtree_zip

    5. **渲染器**:为了在JTree中正确显示图标,我们需要自定义一个`TreeCellRenderer`,它决定了节点在JTree中如何被呈现。自定义的渲染器会覆盖`getTreeCellRendererComponent()`方法,根据节点类型返回带有图标的...

    java实现"我的电脑"资源管理器(附有代码以及导出程序)

    在这个资源管理器中,我们需要使用`File`类来获取文件和目录的信息,以及`FileInputStream`和`FileOutputStream`来进行文件读写操作。 3. **目录遍历**:为了动态加载本地硬盘文件目录,我们需要遍历文件系统。这...

    CSDN树形控件-静态

    本资源“CSDN树形控件-静态”聚焦于Java编程语言中的静态树形菜单实现,这意味着它不支持动态地从数据库加载数据,而是预先设定好的结构。 在Java中,实现树形控件通常涉及javax.swing.JTree类。JTree是Java Swing...

    Java仿XP资源浏览器左边的树

    在Java编程领域,"Java仿XP资源浏览器左边的树"是一个常见的项目,旨在实现一个类似于Windows XP操作系统中资源管理器左侧的文件系统树形结构。这个项目主要涉及到`JTree`组件的使用,它是Java Swing库中用于展示...

    java树形菜单源码

    9. **Swing布局管理**:将`JTree`组件放入容器时,需要考虑容器的布局管理器,如`BorderLayout`或`GridBagLayout`,以正确地定位和显示树形菜单。 通过分析并理解以上知识点,开发者可以利用给定的源码学习如何在...

    Java目录树控件

    7. **自定义渲染器**: 如果需要自定义节点的显示样式,比如改变文件和目录的图标,可以实现TreeCellRenderer接口。 8. **性能优化**: 对于大型文件系统,遍历和构建整个树可能会消耗大量资源。可以考虑只加载可见...

    常用树形控件

    1. 文件系统浏览:在操作系统中,如Windows资源管理器,树形控件常用于展示文件夹和文件的层次关系。 2. 菜单系统:软件应用的主菜单通常采用树形结构,方便用户按类别选择功能。 3. 组织结构:企业组织架构、项目...

    java用的动态树形菜单

    项目名为“树型控件lv2.0”,可能意味着这是一个升级版的树形菜单控件,具有更好的性能或更多的功能,比如支持动态加载数据,即只有在用户滚动到可视区域时才加载子节点,以提高程序响应速度和降低内存消耗。...

    treetable所需的包.zip

    - 为了自定义数据显示,我们需要为表格单元格和树节点创建自定义渲染器,通过实现TableCellRenderer和TreeCellRenderer接口。 8. **性能优化** - TreeTable可能会因为大量的数据而变得性能低下,因此,理解并应用...

    树型控件

    - 文件管理器:在Windows资源管理器中,左侧的目录树就是典型的树型控件,用户可以逐级浏览文件夹。 - 软件菜单:许多软件的设置菜单采用树型结构,如IDE(集成开发环境)的项目结构。 - 网站导航:网站的层级...

    树状视图演示程序ExTree

    在Windows操作系统中,例如资源管理器,用户经常可以看到这种布局,其中每个节点可以有零个或多个子节点,形成一个层次结构。这种视觉表示方式便于用户理解和操作复杂的数据结构。 "ExTree"是一个专门用于演示树状...

Global site tag (gtag.js) - Google Analytics