注释很详细,直接上代码
新增内容(源码细节知识点巨多,建议细看)
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("南宁");
//定义需要被拖动的TreePath
TreePath 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() {
//按下鼠标时,获得被拖动的结点路径
@Override
public void mousePressed(MouseEvent e) {
//如果需要唯一确定某个结点,则必须通过TreePath来获取
//并且我们后面需要判断目标节点为移动节点的祖先节点,这种情况是不能移动的
TreePath treePath = tree.getPathForLocation(e.getX(), e.getY());//获取当前点击的结点路径
if (treePath!=null){//如果点击的结点路径存在
movePath = treePath;//将当前点击结点的TreePath保存下来
}
}
//松开树表示可以确定即将被拖入到的父结点
@Override
public 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);
//使用指定的结点数组创建TreePath
TreePath 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的方法添加,需要手动刷新UI
selectedNode.add(newNode);
//显示新结点
TreeNode[] pathToRoot = model.getPathToRoot(newNode);
TreePath treePath = new TreePath(pathToRoot);
tree.scrollPathToVisible(treePath);
//手动刷新UI
tree.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();
}
}
演示效果: