swing快速入门(四十四)拖动、编辑JTree结点

news2024/10/7 20:25:03

注释很详细,直接上代码

新增内容(源码细节知识点巨多,建议细看)

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();
    }
}

演示效果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1385153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

zepplin记录1

zepplin记录1 文章目录 zepplin记录1前言一、配置python环境二、测试可用性1.配置interpreter2.测试代码 总结 前言 Apache Zeppelin是一个开源的数据分析和可视化的交互式笔记本,类似于Jupyter Notebook。它支持多种编程语言(如Scala、Python、R、SQL等…

多线程——阻塞队列

什么是阻塞队列 相比于一般的队列,有两个特点 1.线程安全 2.带有阻塞功能 1)队伍为空时,出队列就会出现阻塞,阻塞到其他线程入队列为止 2)队伍为满时,入队列就会出现阻塞,阻塞到其他线程出队列…

pandas查看数据常用方法(以excel为例)

目录 1.查看指定行数的数据head() 2. 查看数据表头columns 3.查看索引index 4.指定索引列index_col 5.按照索引排序 6.按照数据列排序sort_values() 7.查看每列数据类型dtypes 8.查看指定行列数据loc 9.查看数据是否为空isnull() 1.查看指定行数的数据head() &#xff…

【教3妹学编程-算法题】最大频率元素计数

2哥 : 3妹,最近有个电视剧《繁花》非常火🔥,你听说了吗? 3妹:没有,最近一直在忙着找工作,哪有时间看电视啊 2哥 : 啊?大周末还不休息一下啊,这么辛苦。 3妹:当…

CAN/CANFD数据记录仪汽车电子售后神器

CAN数据记录仪是一种用于采集和存储CAN总线数据的工具,广泛应用于汽车、轨道车辆、工业控制等大数据量且不易排查故障的系统中。它可以实时存储总线上的数据,方便后续的研究和分析。解决工程师售后难点。 在选择CAN数据记录仪时,需要根据实…

Jenkins-自动化

定时构建 使用Cron表达式指定执行时间。 # 格式 # ┌──分(0 - 59) # │ ┌──时(0 - 23) # │ │ ┌──日(1 - 31) # │ │ │ ┌─月(1 - 12) # │ │ │ │ ┌─星期&#…

CSC8021_computer network_The Application Layer

The Role of the Application layer The Application layer is the interface between the network and its users › It contains network services (e.g. DNS) › It contains user applications (e.g. email, web browsing) Domain Name System (DNS) › The …

python 字符串的详细处理方法

当前版本: Python 3.8.4 简介 字符串是由字符组成的序列,可以用单引号、双引号或三引号(单引号或双引号的连续使用)括起来。一般用来表示和处理文本信息,可以是字母、数字、标点符号以及其他特殊字符,用于…

将PDF发票转换为excel、xml结构化数据的完美解决方案

随着电子发票的普及,越来越多的企业和个人开始使用PDF格式的电子发票。然而,有时我们需要将电子发票转换为XML格式以便于处理和分析。本文将介绍如何将收到的PDF发票下载为excel、xml文件。首先,我们需要明确一点,PDF是一种基于图…

微服务概述之微服务特性

前言 既然系统采用了微服务架构,就需要了解一些微服务的特性,这样在进行微服务开发时,脑海中才会有一些指导方向。微服务具有以下特性。 1. 服务组件化 组件是独立、可替换、可升级的软件的单元。将整体应用拆分成独立的服务组件后&#xff…

【算法分析与设计】和为k的子数组

目录 问题 示例 方案一: 思路: 算法设计 代码实现 运行结果: 方案二(调优) 思路(前缀和) 算法设计 示意图 代码实现 运行结果 问题 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数…

Python提取PDF中部分页面的实战代码

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【小沐学GIS】基于OpenSceneGraph(OSG)绘制三维数字地球Earth

🍺三维数字地球系列相关文章如下🍺:1【小沐学GIS】基于C绘制三维数字地球Earth(OpenGL、glfw、glut)第一期2【小沐学GIS】基于C绘制三维数字地球Earth(OpenGL、glfw、glut)第二期3【小沐学GIS】…

CSS 一行三列布局,可换行(含grid网格布局、flex弹性布局/inline-block布局 + 伪类选择器)

效果 一、HTML <div class"num-wrap"><div class"num-item" v-for"num in 8" :key"num">{{ num }}</div></div> 二、CSS 1、grid网格布局&#xff08;推荐&#xff09; .num-wrap {// grid网格布局display…

Python轻松实现炫酷的手势检测

大家好&#xff0c;今天分享一个非常有意思且十分简单的python库——mediapipe库。该库集成了大量的深度学习模型&#xff0c;短短几行代码&#xff0c;就可以快速实现一个炫酷的实例&#xff0c;本文就以手势检测为例&#xff0c;展示一下这个强大的开源库。 mediapipe由Goog…

书生·浦语大模型实战营作业(四)

基础作业&#xff1a; 构建数据集&#xff0c;使用 XTuner 微调 InternLM-Chat-7B 模型, 让模型学习到它是你的智能小助手&#xff0c;效果如下图所示&#xff0c;本作业训练出来的模型的输出需要将不要葱姜蒜大佬替换成自己名字或昵称&#xff01; 数据集 回答结果 进阶作…

抽象类--java学习笔记

什麽是抽象类&#xff1f; 在java中有一个关键字叫&#xff1a;abstract&#xff0c;它就是抽象的意思&#xff0c;可以用它修饰类、成员方法abstract修饰类&#xff0c;这个类就是抽象类&#xff1b;修饰方法&#xff0c;这个方法就是抽象方法 认识抽象类 抽象类的注意事项…

2024.1.15每日一题

LeetCode 82.删除排序链表中的重复元素 II 82. 删除排序链表中的重复元素 II - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例…

Mac M1 Parallels CentOS7.9 Rancher + K8S + Gitlab + Jenkins +Harbor CICD

一、资源清单 机器名称IP地址角色k8srancher高可用部署: https://blog.csdn.net/qq_41594280/article/details/135312148rancher10.211.55.200管理K8S集群k8svip10.211.55.199K8S VIPmaster0110.211.55.201K8S集群主节点master0210.211.55.202K8S集群主节点master0310.211.55.…

AC修炼计划(AtCoder Beginner Contest 335)E-F

传送门&#xff1a; AtCoder Beginner Contest 335 (Sponsored by Mynavi) - AtCoder A&#xff0c;B&#xff0c;C&#xff0c;D还算比较基础&#xff0c;没有什么思路&#xff0c;纯暴力就可以过。 这里来总结一下E和F E - Non-Decreasing Colorful Path 最开始以为是树形…