在此节中,我们将详细介绍JTree两个常用的事件与处理,分别是TreeModeEvent与TreeSelectionEvent. 10-7-1:处理TreeModeEvent事件:
当树的结构上有任何改变时,例如节点值改变了、新增节点、删除节点等,都会TreeModelEvent事件,要处理这样的事件必须实 作TreeModelListener界面,此界面定义了4个方法,如下所示:
TreeModelEvent类本身提供了5个方法,帮我们取得事件的信息,如下所示:
由TreeModelEvent的getTreePath()方法就可以得到TreePath对象,此对象就能够让我们知道用户目前正选哪一个节点, TreePath类最常用的方法为:
我们来看下面这个例子,用户可以在Tree上编辑节点,按下[Enter]键后就可以改变原有的值,并将改变的值显示在下面的 JLabel中:
import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.*; import com.incors.plaf.alloy.*; import com.incors.plaf.alloy.themes.bedouin.*; public class TreeDemo5 implements TreeModelListener { JLabel label = null; String nodeName = null; // 原有节点名称 public TreeDemo5() { try { AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true"); AlloyTheme theme = new BedouinTheme();// 设置界面的外观,手册中共有5种样式 LookAndFeel alloyLnF = new AlloyLookAndFeel(theme); UIManager.setLookAndFeel(alloyLnF); } catch (UnsupportedLookAndFeelException ex) { // You may handle the exception here } // this line needs to be implemented in order to make JWS work properly UIManager.getLookAndFeelDefaults().put("ClassLoader", getClass().getClassLoader()); JFrame f = new JFrame("TreeDemo"); Container contentPane = f.getContentPane(); DefaultMutableTreeNode root = new DefaultMutableTreeNode("资源管理器"); DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("文件夹"); DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的电脑"); DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夹"); DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme"); root.add(node1); root.add(node2); root.add(node3); root.add(node4); DefaultMutableTreeNode leafnode = new DefaultMutableTreeNode("公司文件"); node1.add(leafnode); leafnode = new DefaultMutableTreeNode("个人信件"); node1.add(leafnode); leafnode = new DefaultMutableTreeNode("私人文件"); node1.add(leafnode); leafnode = new DefaultMutableTreeNode("本机磁盘(C:)"); node2.add(leafnode); leafnode = new DefaultMutableTreeNode("本机磁盘(D:)"); node2.add(leafnode); leafnode = new DefaultMutableTreeNode("本机磁盘(E:)"); node2.add(leafnode); DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("网站列表"); node3.add(node31); leafnode = new DefaultMutableTreeNode("天勤网站"); node31.add(leafnode); leafnode = new DefaultMutableTreeNode("足球消息"); node31.add(leafnode); leafnode = new DefaultMutableTreeNode("网络书店"); node31.add(leafnode); JTree tree = new JTree(root); tree.setEditable(true);// 设置JTree为可编辑的 tree.addMouseListener(new MouseHandle());// 使Tree加入检测Mouse事件,以便取得节点名称 // 下面两行取得DefaultTreeModel,并检测是否有TreeModelEvent事件. DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel(); treeModel.addTreeModelListener(this); JScrollPane scrollPane = new JScrollPane(); scrollPane.setViewportView(tree); label = new JLabel("更改数据为: "); contentPane.add(scrollPane, BorderLayout.CENTER); contentPane.add(label, BorderLayout.SOUTH); f.pack(); f.setVisible(true); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } /* * 本方法实作TreeModelListener接口,本接口共定义四个方法,分别是TreeNodesChanged() * treeNodesInserted()、treeNodesRemoved()、treeNodesRemoved()、 * treeStructureChanged().在此范例中我们只针对更改节点值的部份,因此只实作treeNodesChanged()方法. */ public void treeNodesChanged(TreeModelEvent e) { TreePath treePath = e.getTreePath(); System.out.println(treePath); // 下面这行由TreeModelEvent取得的DefaultMutableTreeNode为节点的父节点,而不是用户点选 // 的节点,这点读者要特别注意。要取得真正的节点需要再加写下面6行代码. DefaultMutableTreeNode node = (DefaultMutableTreeNode) treePath .getLastPathComponent(); try { // getChildIndices()方法会返回目前修改节点的索引值。由于我们只修改一个节点,因此节点索引值就放在index[0] // 的位置,若点选的节点为root // node,则getChildIndices()的返回值为null,程序下面的第二行就在处理点选root // node产生的NullPointerException问题. int[] index = e.getChildIndices(); // 由DefaultMutableTreeNode类的getChildAt()方法取得修改的节点对象. node = (DefaultMutableTreeNode) node.getChildAt(index[0]); } catch (NullPointerException exc) { } // 由DefaultMutableTreeNode类getUserObject()方法取得节点的内容,或是写成node.toString()亦相同. label.setText(nodeName + "更改数据为: " + (String) node.getUserObject()); } public void treeNodesInserted(TreeModelEvent e) { } public void treeNodesRemoved(TreeModelEvent e) { } public void treeStructureChanged(TreeModelEvent e) { } public static void main(String args[]) { new TreeDemo5(); } // 处理Mouse点选事件 class MouseHandle extends MouseAdapter { public void mousePressed(MouseEvent e) { try { JTree tree = (JTree) e.getSource(); // JTree的getRowForLocation()方法会返回节点的列索引值。例如本例中,“本机磁盘(D:)”的列索引值为4,此索引值 // 会随着其他数据夹的打开或收起而变支,但“资源管理器”的列索引值恒为0. int rowLocation = tree.getRowForLocation(e.getX(), e.getY()); /* * JTree的getPathForRow()方法会取得从root * node到点选节点的一条path,此path为一条直线,如程序运行的图示若你点选“本机磁盘(E:)”,则Tree * Path为"资源管理器"-->"我的电脑"-->"本机磁盘(E:)",因此利用TreePath * 的getLastPathComponent()方法就可以取得所点选的节点. */ TreePath treepath = tree.getPathForRow(rowLocation); TreeNode treenode = (TreeNode) treepath.getLastPathComponent(); nodeName = treenode.toString(); } catch (NullPointerException ne) { } } } }
注:上面的程序MouseHandle中:
int rowLocation = tree.getRowForLocation(e.getX(), e.getY()); TreePath treepath = tree.getPathForRow(rowLocation);
与:TreePath treepath=tree.getSelectionPath(); 等价,可互换。
我们将“我的电脑”改成“网上领居”:
我们再来看一个TreeModelEvent的例子,下面这个例子我们可以让用户自行增加、删除与修改节点:
import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.*; import com.incors.plaf.alloy.*; import com.incors.plaf.alloy.themes.bedouin.*; public class TreeDemo6 implements ActionListener, TreeModelListener { JLabel label = null; JTree tree = null; DefaultTreeModel treeModel = null; String nodeName = null;// 原有节点名称 public TreeDemo6() { try { AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true"); AlloyTheme theme = new BedouinTheme(); LookAndFeel alloyLnF = new AlloyLookAndFeel(theme); UIManager.setLookAndFeel(alloyLnF); } catch (UnsupportedLookAndFeelException ex) { // You may handle the exception here } // this line needs to be implemented in order to make JWS work properly UIManager.getLookAndFeelDefaults().put("ClassLoader", getClass().getClassLoader()); JFrame f = new JFrame("TreeDemo6"); Container contentPane = f.getContentPane(); DefaultMutableTreeNode root = new DefaultMutableTreeNode("资源管理器"); tree = new JTree(root); tree.setEditable(true); tree.addMouseListener(new MouseHandle()); treeModel = (DefaultTreeModel) tree.getModel(); treeModel.addTreeModelListener(this); JScrollPane scrollPane = new JScrollPane(); scrollPane.setViewportView(tree); JPanel panel = new JPanel(); JButton b = new JButton("新增节点"); b.addActionListener(this); panel.add(b); b = new JButton("删除节点"); b.addActionListener(this); panel.add(b); b = new JButton("清除所有节点"); b.addActionListener(this); panel.add(b); label = new JLabel("Action"); contentPane.add(panel, BorderLayout.NORTH); contentPane.add(scrollPane, BorderLayout.CENTER); contentPane.add(label, BorderLayout.SOUTH); f.pack(); f.setVisible(true); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } // 本方法运行新增、删除、清除所有节点的程序代码. public void actionPerformed(ActionEvent ae) { if (ae.getActionCommand().equals("新增节点")) { DefaultMutableTreeNode parentNode = null; DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新节点"); newNode.setAllowsChildren(true); TreePath parentPath = tree.getSelectionPath(); // 取得新节点的父节点 parentNode = (DefaultMutableTreeNode) (parentPath .getLastPathComponent()); // 由DefaultTreeModel的insertNodeInto()方法增加新节点 treeModel.insertNodeInto(newNode, parentNode, parentNode .getChildCount()); // tree的scrollPathToVisible()方法在使Tree会自动展开文件夹以便显示所加入的新节点。若没加这行则加入的新节点 // 会被 包在文件夹中,你必须自行展开文件夹才看得到。 tree.scrollPathToVisible(new TreePath(newNode.getPath())); label.setText("新增节点成功"); } if (ae.getActionCommand().equals("删除节点")) { TreePath treepath = tree.getSelectionPath(); if (treepath != null) { // 下面两行取得选取节点的父节点. DefaultMutableTreeNode selectionNode = (DefaultMutableTreeNode) treepath .getLastPathComponent(); TreeNode parent = (TreeNode) selectionNode.getParent(); if (parent != null) { // 由DefaultTreeModel的removeNodeFromParent()方法删除节点,包含它的子节点。 treeModel.removeNodeFromParent(selectionNode); label.setText("删除节点成功"); } } } if (ae.getActionCommand().equals("清除所有节点")) { // 下面一行,由DefaultTreeModel的getRoot()方法取得根节点. DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) treeModel .getRoot(); // 下面一行删除所有子节点. rootNode.removeAllChildren(); // 删除完后务必运行DefaultTreeModel的reload()操作,整个Tree的节点才会真正被删除. treeModel.reload(); label.setText("清除所有节点成功"); } } public void treeNodesChanged(TreeModelEvent e) { TreePath treePath = e.getTreePath(); DefaultMutableTreeNode node = (DefaultMutableTreeNode) treePath .getLastPathComponent(); try { int[] index = e.getChildIndices(); node = (DefaultMutableTreeNode) node.getChildAt(index[0]); } catch (NullPointerException exc) { } label.setText(nodeName + "更改数据为:" + (String) node.getUserObject()); } public void treeNodesInserted(TreeModelEvent e) { System.out.println("new node insered"); } public void treeNodesRemoved(TreeModelEvent e) { System.out.println("node deleted"); } public void treeStructureChanged(TreeModelEvent e) { System.out.println("Structrue changed"); } public static void main(String[] args) { new TreeDemo6(); } class MouseHandle extends MouseAdapter { public void mousePressed(MouseEvent e) { try { JTree tree = (JTree) e.getSource(); int rowLocation = tree.getRowForLocation(e.getX(), e.getY()); TreePath treepath = tree.getPathForRow(rowLocation); TreeNode treenode = (TreeNode) treepath.getLastPathComponent(); nodeName = treenode.toString(); } catch (NullPointerException ne) { } } } }