JTree:删除节点后更新树

时间:2014-01-14 10:38:00

标签: java swing jtree swingx jxtreetable

我正在使用我自己的JXTree自定义模型,该模型从AbstractTreeTableModel延伸。所以reload / removeNodeFromParent的选项不存在。

我尝试使用TreeModelListenertreeModelListener.treeNodesRemoved(event)调用每个输入选项。我的树的GUI永远不会更新。除非我调用tree.updateUI(),否则更改的结构不会被反映。(但是该调用正在更新整个树,我只想刷新已删除的节点)。我正在使用自定义树编辑器和树渲染器。我没有写任何自定义树监听器。

所以我问的问题是: listener.treeNodesRemoved()隐式调用一些与tree.updateUI()具有相同结果的代码。或者我是否需要自己编写一些代码来刷新删除子项的特定父节点。侦听器调用是否无效,因为我使用的是自定义树编辑器和渲染器。

编辑:

我发布的是SSCCE。在这种情况下,我没有使用任何树编辑器或报告器,但也可以使用此代码看到问题。

    public class TestListener  extends JFrame{

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {        
                new TestListener();
            }
        });
    }

    public TestListener(){
        Departement dept1 = new Departement("1ST DEPARTMENT");
        Departement dept2 = new Departement("2ND DEPARTMENT");
        Employee emp1 = new Employee("1ST Employee");
        Employee emp2 =  new Employee("2ND Employee");
        Employee emp3 = new Employee("3rd Employee");
        final Employee emp4 =  new Employee("4th Employee");
        ArrayList<Employee> empList1 = new ArrayList<Employee>();
        empList1.add(emp1);
        empList1.add(emp2);
        final ArrayList<Employee> empList2 = new ArrayList<Employee>();
        empList2.add(emp3);
        empList2.add(emp4);
        dept1.setEmpList(empList1);
        dept2.setEmpList(empList2);
        ArrayList<Departement> deptList = new ArrayList<Departement>();
        deptList.add(dept1);
        deptList.add(dept2);
        TestModel model = new TestModel(deptList);
            final JXTree rootTree = new JXTree(model);
        rootTree.setShowsRootHandles(true); // to show collapse and expand icons
        rootTree.setEditable(true);
        rootTree.setRootVisible(false); //not to show the top root
        rootTree.setVisible(true);
        JButton button = new JButton("Delete Node");
            button.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {

                    JXTree rootNew = rootTree;
                    empList2.remove(emp4);
                    TestModel model = (TestModel) rootNew.getModel();
                    TreeModelEvent event = new TreeModelEvent(this, 
                            new Object[] {rootNew.getPathForRow(3)}, // harcoding because i know i am deleting from dept2
                            new int[]{1}, //hardcoding as i am removing emp4
                            new Object[] {emp4});
                    TreeModelListener[] listeners = model.getTreeModelListeners();
                    for (TreeModelListener listener : listeners) {

                        listener.treeNodesRemoved(event);
                    }

                }
            });   

        this.setLayout(new GridLayout(0, 1));
        this.getContentPane().add(new JScrollPane(rootTree));
        this.getContentPane().add(button);
        this.setSize(new java.awt.Dimension(400, 400));
        this.setLocation(280, 50);
        this.setVisible(true);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();


    }
}

TestModel Class

public class TestModel extends AbstractTreeTableModel {

    private final static String[] COLUMN_NAMES = { "LABEL" };
    public TestModel(ArrayList<Departement> depList) {
        super(depList);
        this.depList = depList;
    }

    private ArrayList<Departement> depList;
    /* (non-Javadoc)
     * @see org.jdesktop.swingx.treetable.TreeTableModel#getColumnCount()
     */
    @Override
    public int getColumnCount() {

        return COLUMN_NAMES.length;
    }

    /* (non-Javadoc)
     * @see org.jdesktop.swingx.treetable.TreeTableModel#getValueAt(java.lang.Object, int)
     */
    @Override
    public Object getValueAt(Object arg0, int arg1) {
        if (arg0 instanceof Employee) {
            Employee emp = (Employee) arg0;

            JLabel newLabel =  new JLabel();
            newLabel.setText(emp.getName());
            return (JLabel)newLabel;

        } else if (arg0 instanceof Departement) {
            Departement dept = (Departement) arg0;

            JLabel newLabel =  new JLabel();
            newLabel.setText(dept.getName());
            return (JLabel)newLabel;

        }

        return null;
    }

    /* (non-Javadoc)
     * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int)
     */
    @Override
    public Object getChild(Object arg0, int arg1) {
        if (arg0 instanceof Departement) {
            Departement dept = (Departement) arg0;
            return dept.getEmpList().get(arg1);
        }

        return depList.get(arg1);
    }

    /* (non-Javadoc)
     * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object)
     */
    @Override
    public int getChildCount(Object arg0) {
        if (arg0 instanceof Departement) {
            Departement dept = (Departement) arg0;
            return dept.getEmpList().size();
        }

        if (arg0 instanceof Employee) {
            return 0;
        }

        return this.depList.size();
    }

    /* (non-Javadoc)
     * @see javax.swing.tree.TreeModel#getIndexOfChild(java.lang.Object, java.lang.Object)
     */
    @Override
    public int getIndexOfChild(Object arg0, Object arg1) {
        Departement dept = (Departement) arg0;
        Employee emp = (Employee) arg1;
        return dept.getEmpList().indexOf(emp);
    }

}

使用构造函数和一些getter / setter,Department和Employee类相当简单。

因此,当我点击删除节点按钮时,树上没有任何动作。用户界面根本没有得到更新。我使用的是swingx 1.6.4版本。

public class Departement {

    private String name;
    public String getName() {
        return name;
    }

    private ArrayList<Employee> empList;
    public ArrayList<Employee> getEmpList() {
        return empList;
    }
    public void setEmpList(ArrayList<Employee> empList) {
        this.empList = empList;
    }
    public Departement(String name){

        this.name = name;
    }
}

员工

public class Employee {

    private String name;
    public String getName() {
        return name;
    }
    public Employee(String name){
        this.name =name;
    }
}

2 个答案:

答案 0 :(得分:2)

您可以使用reload()版本作为指南,在自定义模型中添加DefaultTreeTableModel方法。有一个示例here调用fireTreeStructureChanged()

答案 1 :(得分:2)

从技术上讲,原因归结为不正确的事件(您在操作中手动创建)。除了(次要的)不正确的源之外,它的路径参数是错误的:有一个TreeEvent的构造函数,你混合了

// one taking a TreePath
public TreeModelEvent(Object source, TreePath path, int[] childIndices,
          Object[] children)

// the other taking an array of nodes to the root
public TreeModelEvent(Object source, Object[] path, int[] childIndices,
          Object[] children)

// mixture (**WRONG**)
TreeModelEvent event = new TreeModelEvent(this, 
    // this is an array with the path as single element
    new Object[] {rootNew.getPathForRow(3)}, // harcoding because i know i am deleting from dept2
    new int[]{1}, //hardcoding as i am removing emp4
    new Object[] {emp4});

// technically correct (but **don't** - TreeModelSupport does it for you :-) 
TreeModelEvent event = new TreeModelEvent(this, 
    // this is an array with the path as single element
    rootNew.getPathForRow(3), // harcoding because i know i am deleting from dept2
    new int[]{1}, //hardcoding as i am removing emp4
    new Object[] {emp4});

真正的问题是模型脚下的手动射击:它是模型通知其听众的固有责任,因此最好提供api来移除员工。即使这样,也不要手动创建可以错误地完成的事件,这就是为什么SwingX有TreeModelSupport来缓解痛苦的原因。

// public api in your custom model
public void removeEmployee(Departement dept, Employee emp) {
    TreePath path = new TreePath(new Object[] {depList, dept});
    int index = dept.empList.indexOf(emp);
    dept.empList.remove(emp);
    modelSupport.fireChildRemoved(path, index, emp);
}

// its usage in application code
TestModel model = (TestModel) rootTree.getModel();
model.removeEmployee(dept2, emp4);

与通知问题无关,不要从模型方法返回 view ,正确的getValueAt将类似于

@Override
public Object getValueAt(Object arg0, int arg1) {
    if (arg0 instanceof Employee) {

        Employee emp = (Employee) arg0;
        return emp.getName();
    } else if (arg0 instanceof Departement) {
        Departement dept = (Departement) arg0;
        return dept.getName();
    }

    return null;
}