2015-11-06 281 views
0

在我的项目的GUI中,我使用JTree来显示一个文件系统,我遇到了一个渲染特定的问题:看起来JTree中的一些条目能够正确呈现以及呈现它们自己先前选择的条目。这很难解释,所以我认为包括截图将是最容易理解的。JTree入口呈现问题

JTree with multiple entries duplicated

要做到这一点,我迅速移动从上到下使用箭头键。

我不完全知道这个问题的根源,但我怀疑这可能是我的TreeModelCellRenderer,这两个都包括在下面的问题。

任何帮助,可以帮助我解决这个问题是值得欢迎的!

import java.awt.Component; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.util.Arrays; 
import java.util.Comparator; 
import java.util.Vector; 

import javax.imageio.ImageIO; 
import javax.swing.Icon; 
import javax.swing.ImageIcon; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.JTree; 
import javax.swing.WindowConstants; 
import javax.swing.event.TreeModelListener; 
import javax.swing.tree.DefaultTreeCellRenderer; 
import javax.swing.tree.TreeModel; 
import javax.swing.tree.TreePath; 

public class MCVE { 
    public static void main(String[] args) throws Throwable { 
     JFrame frame = new JFrame(); 
     frame.getContentPane().add(new FileSystemTree(new File(System.getProperty("user.home")))); 
     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     frame.pack(); 
     frame.setVisible(true); 
    } 
} 

class FileSystemTree extends JPanel { 
    private File    root; 
    private FileSystemTreeModel model; 
    private final JTree   fileSystem; 

    public FileSystemTree(File root) throws IOException { 
     this.root = root; 
     model = new FileSystemTreeModel(root); 
     fileSystem = new JTree(model); 
     fileSystem.setEditable(false); 
     fileSystem.setCellRenderer(new FileSystemTreeCellRenderer()); 
     fileSystem.putClientProperty("JTree.lineStyle", "None"); 

     add(new JScrollPane(fileSystem)); 
    } 
} 

class FileSystemTreeCellRenderer extends DefaultTreeCellRenderer { 
    private final Icon folderSpecial; 
    private final Icon folderOther; 
    private final Icon fileSpecial; 
    private final Icon fileOther; 

    public FileSystemTreeCellRenderer() throws IOException { 
     InputStream stream; 
     BufferedImage image; 

     stream = new FileInputStream("folder-special.png"); 
     image = ImageIO.read(stream); 
     folderSpecial = new ImageIcon(image); 
     stream.close(); 

     stream = new FileInputStream("folder-other.png"); 
     image = ImageIO.read(stream); 
     folderOther = new ImageIcon(image); 
     stream.close(); 

     stream = new FileInputStream("file-special.png"); 
     image = ImageIO.read(stream); 
     fileSpecial = new ImageIcon(image); 
     stream.close(); 

     stream = new FileInputStream("file-other.png"); 
     image = ImageIO.read(stream); 
     fileOther = new ImageIcon(image); 
     stream.close(); 
    } 

    @Override 
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { 
     super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); 

     if (value instanceof File) { 
      Icon icon = null; 
      File file = (File) value; 

      if (file.isDirectory()) { 
       if (file.getName().endsWith("-special")) { 
        icon = folderSpecial; 
       } else { 
        icon = folderOther; 
       } 
      } else if (file.isFile()) { 
       if (file.getName().endsWith(".special")) { 
        icon = fileSpecial; 
       } else { 
        icon = fileOther; 
       } 
      } 

      setIcon(icon); 
     } 

     return this; 
    } 
} 

class FileSystemTreeModel implements TreeModel, Comparator<File> { 
    private final File      root; 
    private final Vector<TreeModelListener> listeners = new Vector<>(); 

    public FileSystemTreeModel(File root) { 
     if (!root.isDirectory()) throw new IllegalArgumentException(); 
     this.root = root; 
    } 

    @Override 
    public int compare(File a, File b) { 
     if (a.isDirectory() && !b.isDirectory()) return -1; 
     if (!a.isDirectory() && b.isDirectory()) return 1; 
     return a.getName().compareToIgnoreCase(b.getName()); 
    } 

    @Override 
    public File getRoot() { 
     return root; 
    } 

    @Override 
    public File getChild(Object parent, int index) { 
     File dir = (File) parent; 
     File[] files = dir.listFiles(); 
     String[] children = new String[files.length]; 
     Arrays.sort(files, this::compare); 

     for (int i = 0; i < files.length; i++) { 
      children[i] = files[i].getName(); 
     } 

     return new TreeFile(dir, children[index]); 
    } 

    @Override 
    public int getChildCount(Object parent) { 
     File file = (File) parent; 
     if (file.isDirectory()) { 
      String[] fileList = file.list(); 
      if (fileList != null) return fileList.length; 
     } 
     return 0; 
    } 

    @Override 
    public boolean isLeaf(Object node) { 
     File file = (File) node; 
     return file.isFile(); 
    } 

    @Override 
    public int getIndexOfChild(Object parent, Object child) { 
     File dir = (File) parent; 
     File file = (File) child; 
     File[] children = dir.listFiles(); 
     Arrays.sort(children, this::compare); 

     for (int i = 0; i < children.length; i++) { 
      if (file.equals(children[i])) return i; 
     } 

     return -1; 
    } 

    @Override 
    public void valueForPathChanged(TreePath path, Object value) {} 

    @Override 
    public void addTreeModelListener(TreeModelListener listener) { 
     listeners.add(listener); 
    } 

    @Override 
    public void removeTreeModelListener(TreeModelListener listener) { 
     listeners.remove(listener); 
    } 

    private static class TreeFile extends File { 
     public TreeFile(File parent, String path) { 
      super(parent, path); 
     } 

     @Override 
     public String toString() { 
      return getName(); 
     } 
    } 
} 
+1

1)为了更好地提供帮助,请发布[MCVE]或[简短,独立,正确的示例](http://www.sscce.org/)。 2)[文件浏览器GUI](http://codereview.stackexchange.com/q/4446/7784)是否显示类似的工件? *“(代码)这两个可用”*我(和大多数人)不会跟随代码的链接。制作一个MCVE,在问题中发布。 (投票结束。) –

+0

@AndrewThompson我只是认为添加一个代码链接,而不是让这个问题不合理地长,会使它更容易阅读。而且,因为我不明白这个问题的本质,我认为创建一个MCVE可能会相当困难。但是,哦,我想我会创建一个。可能需要一段时间,直到我更新问题,但是,因为我很快会开会。 – mezzodrinker

+0

我添加了一个演示我的问题的MCVE。 CC @AndrewThompson – mezzodrinker

回答

0

我能够通过使自定义TreeCellRenderer实现TreeCellRenderer而不是扩展DefaultTreeCellRenderer来解决此呈现问题。看起来这个问题连接到DefaultTreeCellRenderer延伸JLabel和编辑自己的值,以显示条目。通过使用将值映射到JLabelHashMap,内存消耗增加了很多,但解决了这个问题。

public class FileSystemTreeCellRenderer implements TreeCellRenderer { 
    protected Color foregroundColor = null; 
    protected Color backgroundColor = null; 
    protected Color selectionForegroundColor = null; 
    protected Color selectionBackgroundColor = null; 
    protected Map<Object, JLabel> labels = new HashMap<>(); 
    protected final Icon folderSpecial; 
    protected final Icon folderOther; 
    protected final Icon fileSpecial; 
    protected final Icon fileOther; 

    public FileSystemTreeCellRenderer() throws IOException { 
     InputStream stream; 
     BufferedImage image; 

     stream = new FileInputStream("folder-special.png"); 
     image = ImageIO.read(stream); 
     folderSpecial = new ImageIcon(image); 
     stream.close(); 

     stream = new FileInputStream("folder-other.png"); 
     image = ImageIO.read(stream); 
     folderOther = new ImageIcon(image); 
     stream.close(); 

     stream = new FileInputStream("file-special.png"); 
     image = ImageIO.read(stream); 
     fileSpecial = new ImageIcon(image); 
     stream.close(); 

     stream = new FileInputStream("file-other.png"); 
     image = ImageIO.read(stream); 
     fileOther = new ImageIcon(image); 
     stream.close(); 
    } 

    protected JLabel getLabelFor(Object object) { 
     JLabel label = labels.get(object); 
     if(label == null) { 
      label = new JLabel(); 
      labels.put(object, label); 
     } 
     return label; 
    } 

    public Color getForegroundColor() { 
     if (foregroundColor == null) return UIManager.getColor("Tree.textForeground"); 
     return foregroundColor; 
    } 

    public Color getBackgroundColor() { 
     if (backgroundColor == null) return UIManager.getColor("Tree.textBackground"); 
     return backgroundColor; 
    } 

    public Color getSelectionForegroundColor() { 
     if (selectionForegroundColor == null) return UIManager.getColor("Tree.selectionForeground"); 
     return selectionForegroundColor; 
    } 

    public Color getSelectionBackgroundColor() { 
     if (selectionBackgroundColor == null) return UIManager.getColor("Tree.selectionBackground"); 
     return selectionBackgroundColor; 
    } 

    @Override 
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { 
     JLabel label = getLabelFor(value); 
     label.setText(Objects.toString(value, "")); 
     label.setOpaque(true); 
     label.setBackground(selected ? getSelectionBackgroundColor() : getBackgroundColor()); 
     label.setForeground(selected ? getSelectionForegroundColor() : getForegroundColor()); 
     label.setEnabled(tree.isEnabled()); 
     label.setComponentOrientation(tree.getComponentOrientation()); 

     if (value instanceof File) { 
      Icon icon = null; 
      File file = (File) value; 

      if (file.isDirectory()) { 
       if (file.getName().endsWith("-special")) { 
        icon = folderSpecial; 
       } else { 
        icon = folderOther; 
       } 
      } else if (file.isFile()) { 
       if (file.getName().endsWith(".special")) { 
        icon = fileSpecial; 
       } else { 
        icon = fileOther; 
       } 
      } 

      label.setIcon(icon); 
     } 

     return label; 
    } 
} 

如果有人有一个更好的,更少的内存消耗的方式来做到这一点,请随时张贴另一个答案!

0

也许这只是我昨天遇到的那个。当我选择一个节点时,节点后面的节点与选定的节点在相同的前景下呈现。我在这个问题上找到了一个规范:UIManagers目前不在正常工作:密钥查找返回空值。我们正在回落默认值。 也许你应该使用setForeground()重置未选中的条目。