什么导致任意JTree节点错误地显示他们的名字,我怎么能阻止它发生?

时间:2014-10-22 10:59:33

标签: java jtree

我已经从XML数据中构建了一个相当大的JTree,实际上有数千个节点,其中大多数都正确显示。但是由于某些原因,某些节点的userData字符串没有完全显示,而是在名称末尾附加...的情况下缩短或完全切断。

enter image description here

受影响的节点似乎是随机的,每次从XML刷新或重新创建树时都会有所不同。

我排除的事情:

没有足够的空间来显示全名。

  1. JTree位于一个有足够水平空间的JScrollPane里面,JScrollPane甚至没有显示水平滚动条来表示空间不足。

  2. 即使是最短的名字也会受到影响。

  3. 所有缩短的节点名称的截止点并不一致。

  4. 从XML加载的名称不完整

    1. 如果受影响的节点不是叶子,然后展开或折叠,则在重新呈现时会正确显示全名,在此过程中根本不会查询XML。
    2. 编辑:

      根据要求对JTree背后的代码进行了一些解释:

      TreeModel的创建和树的种群:

      public XMLDialogTree(Document doc)
          {
              DefaultTreeModel treeModel = new DefaultTreeModel(buildTreeNode(doc.getElementsByTagName("Dialogs").item(0)));
              setModel(treeModel);
          }
      
      // A recursive function to build the tree
      private DefaultMutableTreeNode buildTreeNode(Node xmlNode)
          {
              // Make sure the node's name is a description of what it is, as opposed to a generic XML tag
              XMLDialogTreeNode = new XMLDialogTreeNode(xmlNode.getAttributes().getNamedItem("name").getNodeValue());
              treeNode.controlName = xmlNode.getNodeName();
      
              // Add children to the treeNode based on the xmlNode's children
              NodeList nodeList = xmlNode.getChildNodes();
              for (int i = 0; i < nodeList.getLength(); i++)
                  {
                      Node tempNode = nodeList.item(i);
                      if (tempNode.getNodeType() == Node.ELEMENT_NODE)
                          {
                              // loop again if has child nodes
                              treeNode.add(buildTreeNode(tempNode));
                          }
                  }
              return treeNode;
          }
      

      其中doc是包含已解析XML的org.w3c.dom.Document,而XMLDialogTreeNode实际上只是javax.swing.tree.DefaultMutableTreeNode扩展为包含String controlName = "CustomNodeName"

      自定义CellRenderer如下所示,代码几乎与加载自定义图标有关,不会影响显示的名称。

      public class XMLDialogTreeCellRenderer extends DefaultTreeCellRenderer
      {
          private static final long serialVersionUID = 1L;
      
          // Icons used in the JTree, loaded once on an as needed basis once, and stored here.
          private static final Map<String, Icon> icons = new HashMap<String, Icon>();
      
          static
              {
                  // Make sure the default icon is loaded
                  loadIcon("_not_found");
              }
      
          @Override
          public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean exp, boolean leaf, int row, boolean hasFocus)
              {
                  XMLDialogTreeNode node = (XMLDialogTreeNode) value;
      
                  setIcons(node.controlName);
      
                  super.getTreeCellRendererComponent(tree, value, sel, exp, false/* leaf */, row, hasFocus);
                  return this;
              }
      
          /**
           * Assigns both the open and close icons, which are specific to a certain control. Loads them if necessary.
           * 
           * @param controlName
           *            - The name of the control in the XML and the name of the image in the resources.treeIcons package.
           */
          private final void setIcons(String controlName)
              {
                  // Make sure the Node has a controlName set
                  if (controlName != null)
                      {
                          // Try and get a pre-loaded icon
                          Icon controlIcon = icons.get(controlName);
                          // It wasn't there, try loading it
                          if (controlIcon == null)
                              loadIcon(controlName);
      
                          // If the icon doesn't exist, it is set to a default so this code is safe
                          setOpenIcon(controlIcon);
                          setClosedIcon(controlIcon);
                          // Stop here so we don't just set the default icons again
                          return;
                      }
                  setOpenIcon(getDefaultOpenIcon());
                  setClosedIcon(getDefaultClosedIcon());
              }
      
          /**
           * 
           * 
           * @param iconName
           * @return
           */
          private static final void loadIcon(String iconName)
              {
                  URL url = XMLDialogTreeCellRenderer.class.getResource("/com/phabrix/resources/dialogTreeIcons/" + iconName + ".png");
                  if (url == null)
                      {
                          // Tell the developer that they need to make a new icon for a new control type
                          if (Main.DEBUG && !iconName.equals("Dialogs"))
                              System.out.println("There is no icon for the control named: " + iconName);
                          url = XMLDialogTreeCellRenderer.class.getResource("/com/phabrix/resources/dialogTreeIcons/_not_found.png");
                      }
      
                  icons.put(iconName, new ImageIcon(Toolkit.getDefaultToolkit().getImage(url)));
              }
      }
      

1 个答案:

答案 0 :(得分:1)

这是一个简单的答案;这段代码:

// Try and get a pre-loaded icon
Icon controlIcon = icons.get(controlName);
// It wasn't there, try loading it
if (controlIcon == null)
    loadIcon(controlName);

成了这段代码:

// Try and get a pre-loaded icon
Icon controlIcon = icons.get(controlName);
// It wasn't there, try loading it
if (controlIcon == null)
{
    loadIcon(controlName);
    controlIcon = icons.get(controlName);
}

请注意,在检查我们的Icon变量是否为null,然后确保已将Icon加载到程序中时,我现在实际上确保更新了Icon变量,使其不再为null。

我树中受影响的节点并非随机或波动,正如我之前所想。它始终是每个图标绘制的第一个节点(根据我打开树的顺序而不同)。我正在加载每个图标一次,然后缓存它(树很大并且多次重新加载相同的图标是一个很大的性能影响)。本质上,节点是用文本创建的,然后在显示后设置了一个图标,这会分散文本并导致结束被切断。如上所述,解决方案是在第一次加载图标后不要忘记设置图标。一个更好的解决方案是将icons.get()包裹起来,以保证返回一个图标并封装缓存优化。