zoukankan      html  css  js  c++  java
  • Jtree(节点的渲染+资源管理器)(2)

    上一次我们建立一个比较简单的资源管理器,这次我们说一下上面的资源管理器的问题,并且得尽量的贴近windows的资源管理器。

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

    ① 图片和外观和Windows有差距

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

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

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

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

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

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

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

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

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

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

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

    节点的图片的样式问题我们可以设置Renderer,又因为这些图片可以在JFileChooser的UI中取得,我们先参照JFileChooser的UI做一个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());

    看看效果:

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

    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

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

    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为我们提供好的就可以了,如下图,网上邻居也有了,问题四完成了:

    最后我们来解决问题三,为什么会假死,是因为文件夹多或者网速慢导致的,解决办法当然是多线程,但是多线程在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);

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

     

    ------没有完成,后面还有。留个尾巴

  • 相关阅读:
    【Linux笔记】Linux目录结构
    《Effective C#》快速笔记(五)-
    《Effective C#》快速笔记(四)- 使用框架
    《Effective C#》快速笔记(三)- 使用 C# 表达设计
    《Effective C#》快速笔记(二)- .NET 资源托管
    《Effective C#》快速笔记(一)- C# 语言习惯
    Visual Studio 数据库架构比较
    C# 反射与dynamic最佳组合
    C# 调用WebApi
    基于微软开发平台构建和使用私有NuGet托管库
  • 原文地址:https://www.cnblogs.com/zhailzh/p/4074892.html
Copyright © 2011-2022 走看看