注释很详细,直接上代码
新增内容(源码细节知识点巨多,建议细看)
1.设置JTree可编辑
2.使用JTree关联的数据模型实现节点的增删改
3.鼠标拖动节点事件设计及处理方法
4.手动刷新视图与自动刷新的方法区别
5.自定位节点视图方法
源码 :
package swing41_50;import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;public class swing_test_42 {JFrame jFrame ;//定义JFrame对象JTree tree;//定义JTree对象DefaultTreeModel model;//JTree关联的数据模型对象//定义几个初始结点DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");DefaultMutableTreeNode guangxi = new DefaultMutableTreeNode("广西");DefaultMutableTreeNode foshan = new DefaultMutableTreeNode("佛山");DefaultMutableTreeNode shantou = new DefaultMutableTreeNode("汕头");DefaultMutableTreeNode guilin = new DefaultMutableTreeNode("桂林");DefaultMutableTreeNode nanning = new DefaultMutableTreeNode("南宁");//定义需要被拖动的TreePathTreePath movePath;//定义按钮JButton addSiblingBtn = new JButton("添加兄弟结点");JButton addChildBtn = new JButton("添加子结点");JButton deleteBtn = new JButton("删除结点");JButton editBtn = new JButton("编辑当前结点");//初始化操作public void init(){//通过add()方法建立父子层级关系guangdong.add(foshan);guangdong.add(shantou);guangxi.add(guilin);guangxi.add(nanning);root.add(guangdong);root.add(guangxi);jFrame = new JFrame("可编辑结点的树");//创建JFrame对象tree = new JTree(root);//创建JTree对象//获取JTree关联的数据模型TreeModel对象model = (DefaultTreeModel) tree.getModel();//设置JTree可编辑(对一个节点三击中可以编辑)tree.setEditable(true);//创建鼠标事件监听器MouseListener mouseListener = new MouseAdapter() {//按下鼠标时,获得被拖动的结点路径@Overridepublic void mousePressed(MouseEvent e) {//如果需要唯一确定某个结点,则必须通过TreePath来获取//并且我们后面需要判断目标节点为移动节点的祖先节点,这种情况是不能移动的TreePath treePath = tree.getPathForLocation(e.getX(), e.getY());//获取当前点击的结点路径if (treePath!=null){//如果点击的结点路径存在movePath = treePath;//将当前点击结点的TreePath保存下来}}//松开树表示可以确定即将被拖入到的父结点@Overridepublic void mouseReleased(MouseEvent e) {TreePath treePath = tree.getPathForLocation(e.getX(), e.getY());//获取当前点击的结点路径if (treePath!=null && movePath!=null){//如果移动节点路径和目标节点路径都存在//判断目标路径是否是原路径的子代,如果是则目标路径比原路径短,目标节点是原节点的父代(不包括相等的情况)//成立则说明目标结点是被移动结点的子结点,也就无法移动if (movePath.isDescendant(treePath) && movePath!=treePath){//弹出警告框,提示无法移动JOptionPane.showMessageDialog(jFrame,"目标结点是被移动结点的子结点,无法移动!","非法移动",JOptionPane.WARNING_MESSAGE);}//判断目标节点路径和子节点路径是否为同一个节点//如果成立则说明并非相同节点if (movePath!=treePath){//add方法内部,先将该结点从原父结点删除,然后再把该结点添加到新结点中//获取目标节点和被移动节点//获取路径的最后一个结点,即上一次点击(选中)的节点DefaultMutableTreeNode tartParentNode = (DefaultMutableTreeNode) treePath.getLastPathComponent();DefaultMutableTreeNode moveNode = (DefaultMutableTreeNode) movePath.getLastPathComponent();tartParentNode.add(moveNode);//添加子结点(将移动节点作为子节点添加到目标节点中)movePath=null;//清空移动节点tree.updateUI();//更新UI}}}};//为JTree添加鼠标监听器tree.addMouseListener(mouseListener);//创建JPanel对象JPanel panel = new JPanel();addSiblingBtn.addActionListener(e -> {//添加兄弟结点//获取选中结点DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();//如果结点为空,则直接返回if (selectedNode==null){return;}//获取该选中结点的父结点DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectedNode.getParent();//如果父结点为空,则直接返回if (parent==null){return;}//创建一个新结点DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");//获取选中结点在父节点中的索引//因为我们需要插在选中节点的前面int selectedIndex = parent.getIndex(selectedNode);//在选中位置前面插入新结点(如果想要后面则将selectedIndex+1即可)model.insertNodeInto(newNode,parent,selectedIndex);//----------显示新结点---------------// 这里的显示新节点并非是刷新界面,因为model方法会自动刷新//而是为了自动滚动以显示新结点//获取从根结点到新结点的所有结点TreeNode[] pathToRoot = model.getPathToRoot(newNode);//使用指定的结点数组创建TreePathTreePath treePath = new TreePath(pathToRoot);//显示指定的treePath//这个方法的作用是JTree 组件会自动滚动以确保指定路径的节点可见(如果树比较长视图显示不下的情况下有效果)tree.scrollPathToVisible(treePath);});//添加兄弟结点panel.add(addSiblingBtn);addChildBtn.addActionListener(e -> {//获取选中结点DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();if (selectedNode==null){//如果结点为空,则直接返回return ;}//创建新结点DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");//使用TreeModel的方法添加,不需要手动刷新UI//model.insertNodeInto(newNode,selectedNode,selectedNode.getChildCount());//使用TreeNode的方法添加,需要手动刷新UIselectedNode.add(newNode);//显示新结点TreeNode[] pathToRoot = model.getPathToRoot(newNode);TreePath treePath = new TreePath(pathToRoot);tree.scrollPathToVisible(treePath);//手动刷新UItree.updateUI();});panel.add(addChildBtn);//添加子结点deleteBtn.addActionListener(e -> {//获取选中结点DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();//如果结点存在且存在父结点//有小伙伴要问了,为什么还要判断有没有父节点,因为根节点不能删,否则会报错if (selectedNode!=null && selectedNode.getParent()!=null){//删除选中结点model.removeNodeFromParent(selectedNode);}});//删除结点panel.add(deleteBtn);//实现编辑结点的监听器editBtn.addActionListener(e -> {//获取选中结点的路径TreePath selectionPath = tree.getSelectionPath();if (selectionPath!=null){//如果选中结点不为空//编辑选中结点tree.startEditingAtPath(selectionPath);}});panel.add(editBtn);//编辑结点jFrame.add(new JScrollPane(tree));//给树添加滚动条jFrame.add(panel, BorderLayout.SOUTH);//添加按钮在南侧jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口关闭时退出程序jFrame.pack();//自动调整窗口大小jFrame.setVisible(true);//显示窗口}public static void main(String[] args) {//启动程序new swing_test_42().init();}
}
演示效果: