1

由于 TreePath 用于识别数据节点的方法,我无法让 Outline 控件(替代 TreeTable)与数据对象树一起工作。

关键问题是 TreePath 的 equals() 方法使用数据节点的 equals() 方法来识别数据树中的两个节点对象是相同的。TreeModel.java 甚至评论了这个问题:

“一些实现可能假设如果两个 TreePaths 相等 [由 equals() 确定],它们标识相同的节点。如果不满足此条件,可能会导致绘制问题和其他奇怪的问题。” 示例数据:

  • 一个
      • C
      • D
      • F
    • H
    • ķ

这里,两个“B”节点作为单独的节点可能被认为具有相等的值(因此 equals() 返回 true),但它们肯定不代表树中的相同节点。

好的,所以如果源数据对象已经实现了 equals() 以指示仅考虑节点本身的相等值,如果在特定父节点下出现多个相同值的节点,则会破坏 TreePath。在这种情况下,Outline 无法展开/折叠正确的相同值节点之一。

如果 TreePath.equals() 使用“==”而不是数据对象的 equals() 方法,这个问题将得到解决。然而,由于股票 TreePath 与 TreeModel 等紧密相连,因此如何在没有大量中断的情况下修复这种行为并不明显。

有没有一些优雅的方法来获得正确的效果?

谢谢!

4

1 回答 1

4

Actually, I think that the problem comes from the way you are implementing equals() in your TreeNode's. Two TreeNode's, in your case, should be considered equals if they represent the same visual nodes. Two TreeNode's can represent the same model object (for example Model Object B) but remain different nodes..

Here is a simple demo based on DefaultMutableTreeNode (equals() uses the Object.equals(Object) default implementation ==). Every 2 seconds it toggles selection from node B1 to B2:

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

public class TestTreeNodes {

    public static class SomeModelNode {
        private String value;

        public SomeModelNode(String value) {
            super();
            this.value = value;
        }

        public String getValue() {
            return value;
        }

    }

    public class MyTreeCellRenderer extends DefaultTreeCellRenderer {

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row,
                boolean hasFocus) {
            Component cell = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            if (value instanceof DefaultMutableTreeNode) {
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
                if (treeNode.getUserObject() instanceof SomeModelNode) {
                    setText(((SomeModelNode) treeNode.getUserObject()).getValue());
                }
            }
            return cell;
        }
    }

    private JFrame f;
    private JTree tree;
    private DefaultMutableTreeNode nodeA;
    private DefaultMutableTreeNode nodeB1;
    private DefaultMutableTreeNode nodeB2;
    private DefaultMutableTreeNode nodeC;
    private DefaultMutableTreeNode nodeD;
    private DefaultMutableTreeNode nodeE;
    private DefaultMutableTreeNode nodeF;
    private DefaultMutableTreeNode nodeH;
    private DefaultMutableTreeNode nodeK;

    private boolean showingB1 = false;

    protected void initUI() {
        tree = new JTree(getModel());
        for (int i = 0; i < tree.getRowCount(); i++) {
            tree.expandRow(i);
        }
        ToolTipManager.sharedInstance().registerComponent(tree);
        tree.setCellRenderer(new MyTreeCellRenderer());
        f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationRelativeTo(null);
        f.add(new JScrollPane(tree));
        f.pack();
        f.setVisible(true);
        Timer t = new Timer(2000, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (!showingB1) {
                    tree.getSelectionModel().setSelectionPath(getPathForNode(nodeB1));
                } else {
                    tree.getSelectionModel().setSelectionPath(getPathForNode(nodeB2));
                }
                showingB1 = !showingB1;
            }
        });
        t.start();

    }

    private TreePath getPathForNode(TreeNode node) {
        List<TreeNode> nodes = new ArrayList<TreeNode>();
        TreeNode current = node;
        while (current != null) {
            nodes.add(current);
            current = current.getParent();
        }
        Collections.reverse(nodes);
        return new TreePath(nodes.toArray(new Object[nodes.size()]));
    }

    private TreeModel getModel() {
        SomeModelNode a = new SomeModelNode("A");
        SomeModelNode b = new SomeModelNode("B");
        SomeModelNode c = new SomeModelNode("C");
        SomeModelNode d = new SomeModelNode("D");
        SomeModelNode e = new SomeModelNode("E");
        SomeModelNode f = new SomeModelNode("F");
        SomeModelNode h = new SomeModelNode("H");
        SomeModelNode k = new SomeModelNode("K");
        nodeA = new DefaultMutableTreeNode(a);
        nodeB1 = new DefaultMutableTreeNode(b);
        nodeB2 = new DefaultMutableTreeNode(b);
        nodeC = new DefaultMutableTreeNode(c);
        nodeD = new DefaultMutableTreeNode(d);
        nodeE = new DefaultMutableTreeNode(e);
        nodeF = new DefaultMutableTreeNode(f);
        nodeH = new DefaultMutableTreeNode(h);
        nodeK = new DefaultMutableTreeNode(k);
        // Children of A
        nodeA.add(nodeB1);
        nodeA.add(nodeB2);
        nodeA.add(nodeH);
        nodeA.add(nodeK);
        // Children of B1
        nodeB1.add(nodeC);
        nodeB1.add(nodeD);
        // Children of B2
        nodeB2.add(nodeE);
        nodeB2.add(nodeF);
        DefaultTreeModel model = new DefaultTreeModel(nodeA);
        return model;
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestTreeNodes().initUI();
            }
        });
    }

}
于 2012-11-26T09:43:34.750 回答