Java脑图案例

wen java案例 2

本文目录导读:

Java脑图案例

  1. 完整实现代码
  2. 运行方式
  3. 功能特性
  4. 界面效果

我将为您创建一个Java脑图(思维导图)案例,使用Java Swing实现一个可视化脑图工具。

完整实现代码

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;
public class MindMapDemo extends JFrame {
    private MindMapPanel mindMapPanel;
    private JToolBar toolBar;
    private JLabel statusBar;
    public MindMapDemo() {
        setTitle("Java脑图 - 思维导图工具");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(1200, 800);
        setLocationRelativeTo(null);
        // 初始化组件
        mindMapPanel = new MindMapPanel();
        createToolBar();
        createStatusBar();
        // 添加主面板
        add(mindMapPanel, BorderLayout.CENTER);
        add(toolBar, BorderLayout.NORTH);
        add(statusBar, BorderLayout.SOUTH);
        // 添加菜单栏
        setJMenuBar(createMenuBar());
        // 键盘快捷键
        setupKeyboardShortcuts();
    }
    private JMenuBar createMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        // 文件菜单
        JMenu fileMenu = new JMenu("文件");
        JMenuItem newItem = new JMenuItem("新建");
        newItem.addActionListener(e -> mindMapPanel.newMindMap());
        JMenuItem saveItem = new JMenuItem("保存");
        saveItem.addActionListener(e -> saveMindMap());
        JMenuItem loadItem = new JMenuItem("加载");
        loadItem.addActionListener(e -> loadMindMap());
        JMenuItem exitItem = new JMenuItem("退出");
        exitItem.addActionListener(e -> System.exit(0));
        fileMenu.add(newItem);
        fileMenu.add(saveItem);
        fileMenu.add(loadItem);
        fileMenu.addSeparator();
        fileMenu.add(exitItem);
        // 编辑菜单
        JMenu editMenu = new JMenu("编辑");
        JMenuItem addNodeItem = new JMenuItem("添加节点");
        addNodeItem.addActionListener(e -> mindMapPanel.addChildNode());
        JMenuItem deleteNodeItem = new JMenuItem("删除节点");
        deleteNodeItem.addActionListener(e -> mindMapPanel.deleteSelectedNode());
        JMenuItem editNodeItem = new JMenuItem("编辑节点文本");
        editNodeItem.addActionListener(e -> mindMapPanel.editSelectedNode());
        editMenu.add(addNodeItem);
        editMenu.add(deleteNodeItem);
        editMenu.add(editNodeItem);
        // 视图菜单
        JMenu viewMenu = new JMenu("视图");
        JCheckBoxMenuItem autoLayoutItem = new JCheckBoxMenuItem("自动布局", true);
        autoLayoutItem.addActionListener(e -> mindMapPanel.setAutoLayout(autoLayoutItem.isSelected()));
        JMenuItem expandAllItem = new JMenuItem("展开全部");
        expandAllItem.addActionListener(e -> mindMapPanel.expandAll());
        JMenuItem collapseAllItem = new JMenuItem("收起全部");
        collapseAllItem.addActionListener(e -> mindMapPanel.collapseAll());
        viewMenu.add(autoLayoutItem);
        viewMenu.add(expandAllItem);
        viewMenu.add(collapseAllItem);
        // 帮助菜单
        JMenu helpMenu = new JMenu("帮助");
        JMenuItem aboutItem = new JMenuItem("quot;);
        aboutItem.addActionListener(e -> showAboutDialog());
        helpMenu.add(aboutItem);
        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        menuBar.add(viewMenu);
        menuBar.add(helpMenu);
        return menuBar;
    }
    private void createToolBar() {
        toolBar = new JToolBar();
        toolBar.setFloatable(false);
        JButton addNodeBtn = createToolButton("➕", "添加子节点", e -> mindMapPanel.addChildNode());
        JButton addSiblingBtn = createToolButton("📝", "添加兄弟节点", e -> mindMapPanel.addSiblingNode());
        JButton deleteNodeBtn = createToolButton("🗑️", "删除节点", e -> mindMapPanel.deleteSelectedNode());
        JButton editNodeBtn = createToolButton("✏️", "编辑节点", e -> mindMapPanel.editSelectedNode());
        JButton expandBtn = createToolButton("🔍", "展开节点", e -> mindMapPanel.toggleExpand());
        toolBar.add(addNodeBtn);
        toolBar.add(addSiblingBtn);
        toolBar.add(deleteNodeBtn);
        toolBar.add(editNodeBtn);
        toolBar.add(expandBtn);
        toolBar.addSeparator();
        // 颜色选择
        JButton colorBtn = new JButton("🎨 颜色");
        colorBtn.addActionListener(e -> mindMapPanel.changeNodeColor());
        toolBar.add(colorBtn);
    }
    private JButton createToolButton(String text, String tooltip, ActionListener action) {
        JButton button = new JButton(text);
        button.setToolTipText(tooltip);
        button.addActionListener(action);
        button.setFocusable(false);
        return button;
    }
    private void createStatusBar() {
        statusBar = new JLabel(" 就绪 | 节点数: 0");
        statusBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5));
        statusBar.setOpaque(true);
        statusBar.setBackground(new Color(230, 230, 230));
    }
    private void setupKeyboardShortcuts() {
        InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = getRootPane().getActionMap();
        // Tab: 添加子节点
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "addChild");
        actionMap.put("addChild", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                mindMapPanel.addChildNode();
            }
        });
        // F2: 编辑节点
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "editNode");
        actionMap.put("editNode", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                mindMapPanel.editSelectedNode();
            }
        });
        // Delete: 删除节点
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "deleteNode");
        actionMap.put("deleteNode", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                mindMapPanel.deleteSelectedNode();
            }
        });
    }
    private void saveMindMap() {
        JFileChooser fileChooser = new JFileChooser();
        if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            mindMapPanel.saveToFile(fileChooser.getSelectedFile());
        }
    }
    private void loadMindMap() {
        JFileChooser fileChooser = new JFileChooser();
        if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            mindMapPanel.loadFromFile(fileChooser.getSelectedFile());
        }
    }
    private void showAboutDialog() {
        JOptionPane.showMessageDialog(this, 
            "Java脑图 - 思维导图工具\n\n版本: 1.0\n作者: Java Demo",
            "quot;, JOptionPane.INFORMATION_MESSAGE);
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) {
                e.printStackTrace();
            }
            new MindMapDemo().setVisible(true);
        });
    }
}
// 脑图节点类
class MindNode {
    private String text;
    private MindNode parent;
    private List<MindNode> children;
    private int x, y;
    private int width = 120, height = 40;
    private Color color;
    private boolean expanded;
    public MindNode(String text) {
        this.text = text;
        this.children = new ArrayList<>();
        this.expanded = true;
        this.color = new Color(204, 229, 255); // 默认浅蓝色
    }
    public void addChild(MindNode child) {
        child.parent = this;
        children.add(child);
    }
    public void addSibling(MindNode sibling) {
        if (parent != null) {
            parent.addChild(sibling);
        }
    }
    public boolean isRoot() {
        return parent == null;
    }
    public boolean isLeaf() {
        return children.isEmpty();
    }
    // Getters and Setters
    public String getText() { return text; }
    public void setText(String text) { this.text = text; }
    public MindNode getParent() { return parent; }
    public List<MindNode> getChildren() { return children; }
    public int getX() { return x; }
    public void setX(int x) { this.x = x; }
    public int getY() { return y; }
    public void setY(int y) { this.y = y; }
    public int getWidth() { return width; }
    public void setWidth(int width) { this.width = width; }
    public int getHeight() { return height; }
    public void setHeight(int height) { this.height = height; }
    public Color getColor() { return color; }
    public void setColor(Color color) { this.color = color; }
    public boolean isExpanded() { return expanded; }
    public void setExpanded(boolean expanded) { this.expanded = expanded; }
    public void toggleExpanded() { this.expanded = !this.expanded; }
    // 获取中心点
    public int getCenterX() { return x + width / 2; }
    public int getCenterY() { return y + height / 2; }
    // 获取所有子孙节点
    public List<MindNode> getAllDescendants() {
        List<MindNode> descendants = new ArrayList<>();
        collectDescendants(this, descendants);
        return descendants;
    }
    private void collectDescendants(MindNode node, List<MindNode> list) {
        for (MindNode child : node.children) {
            list.add(child);
            collectDescendants(child, list);
        }
    }
}
// 脑图面板类
class MindMapPanel extends JPanel {
    private MindNode root;
    private MindNode selectedNode;
    private int nodeCount;
    private boolean autoLayout;
    private Point dragStart;
    private MindNode dragNode;
    public MindMapPanel() {
        setBackground(new Color(250, 250, 250));
        newMindMap();
        setupMouseListener();
    }
    private void setupMouseListener() {
        // 鼠标点击选择节点
        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                MindNode node = findNodeAt(e.getPoint());
                if (node != null) {
                    selectedNode = node;
                    dragStart = e.getPoint();
                    dragNode = node;
                    repaint();
                }
            }
            @Override
            public void mouseReleased(MouseEvent e) {
                if (dragNode != null) {
                    updateStatusBar();
                    dragNode = null;
                }
            }
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    MindNode node = findNodeAt(e.getPoint());
                    if (node != null && !node.isRoot()) {
                        node.toggleExpanded();
                        if (autoLayout) {
                            layoutTree();
                        }
                        repaint();
                    }
                }
            }
        });
        // 鼠标拖动节点
        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                if (dragNode != null && e.isShiftDown()) {
                    int dx = e.getX() - dragStart.x;
                    int dy = e.getY() - dragStart.y;
                    dragNode.setX(dragNode.getX() + dx);
                    dragNode.setY(dragNode.getY() + dy);
                    dragStart = e.getPoint();
                    repaint();
                }
            }
        });
        // 键盘监听
        setFocusable(true);
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (selectedNode != null) {
                    int moveAmount = 5;
                    if (e.isShiftDown()) {
                        moveAmount = 10;
                    }
                    switch(e.getKeyCode()) {
                        case KeyEvent.VK_UP: 
                            selectedNode.setY(selectedNode.getY() - moveAmount);
                            break;
                        case KeyEvent.VK_DOWN:
                            selectedNode.setY(selectedNode.getY() + moveAmount);
                            break;
                        case KeyEvent.VK_LEFT:
                            selectedNode.setX(selectedNode.getX() - moveAmount);
                            break;
                        case KeyEvent.VK_RIGHT:
                            selectedNode.setX(selectedNode.getX() + moveAmount);
                            break;
                    }
                    repaint();
                }
            }
        });
    }
    private MindNode findNodeAt(Point point) {
        // 遍历所有可见节点
        List<MindNode> visibleNodes = getVisibleNodes();
        for (int i = visibleNodes.size() - 1; i >= 0; i--) {
            MindNode node = visibleNodes.get(i);
            Rectangle bounds = new Rectangle(node.getX(), node.getY(), node.getWidth(), node.getHeight());
            if (bounds.contains(point)) {
                return node;
            }
        }
        return null;
    }
    private List<MindNode> getVisibleNodes() {
        List<MindNode> visible = new ArrayList<>();
        if (root != null) {
            collectVisibleNodes(root, visible);
        }
        return visible;
    }
    private void collectVisibleNodes(MindNode node, List<MindNode> list) {
        list.add(node);
        if (node.isExpanded()) {
            for (MindNode child : node.getChildren()) {
                collectVisibleNodes(child, list);
            }
        }
    }
    public void newMindMap() {
        root = new MindNode("中心主题");
        nodeCount = 1;
        selectedNode = root;
        autoLayout = true;
        // 创建默认子节点
        MindNode child1 = new MindNode("Java基础");
        MindNode child2 = new MindNode("面向对象");
        MindNode child3 = new MindNode("集合框架");
        root.addChild(child1);
        root.addChild(child2);
        root.addChild(child3);
        nodeCount = 4;
        layoutTree();
        updateStatusBar();
        repaint();
    }
    private void layoutTree() {
        if (!autoLayout) return;
        int panelWidth = getWidth();
        int panelHeight = getHeight();
        // 递归布局,使用简单的水平树状布局
        if (root != null) {
            layoutNode(root, 50, panelHeight / 2, 0);
        }
    }
    private void layoutNode(MindNode node, int x, int y, int level) {
        node.setX(x);
        node.setY(y - node.getHeight() / 2);
        if (!node.isExpanded() || node.isLeaf()) {
            return;
        }
        // 布局子节点
        int childCount = node.getChildren().size();
        int totalHeight = (childCount - 1) * (node.getHeight() + 30);
        int startY = y - totalHeight / 2;
        int childX = x + 200;
        for (int i = 0; i < childCount; i++) {
            MindNode child = node.getChildren().get(i);
            int childY = startY + i * (node.getHeight() + 30);
            layoutNode(child, childX, childY, level + 1);
        }
    }
    public void addChildNode() {
        if (selectedNode == null) {
            return;
        }
        MindNode newNode = new MindNode("新节点");
        selectedNode.addChild(newNode);
        nodeCount++;
        if (autoLayout) {
            layoutTree();
        }
        selectedNode = newNode;
        updateStatusBar();
        repaint();
        // 自动开始编辑新节点
        editNode(newNode);
    }
    public void addSiblingNode() {
        if (selectedNode == null || selectedNode.isRoot()) {
            return;
        }
        MindNode newNode = new MindNode("新节点");
        selectedNode.addSibling(newNode);
        nodeCount++;
        selectedNode = newNode;
        if (autoLayout) {
            layoutTree();
        }
        updateStatusBar();
        repaint();
        // 自动开始编辑新节点
        editNode(newNode);
    }
    public void deleteSelectedNode() {
        if (selectedNode == null || selectedNode.isRoot()) {
            return;
        }
        int confirm = JOptionPane.showConfirmDialog(this, 
            "确定要删除节点 \"" + selectedNode.getText() + "\" 吗?",
            "删除节点", JOptionPane.YES_NO_OPTION);
        if (confirm == JOptionPane.YES_OPTION) {
            MindNode parent = selectedNode.getParent();
            parent.getChildren().remove(selectedNode);
            nodeCount -= 1 + selectedNode.getAllDescendants().size();
            selectedNode = parent;
            if (autoLayout) {
                layoutTree();
            }
            updateStatusBar();
            repaint();
        }
    }
    public void editSelectedNode() {
        if (selectedNode != null) {
            editNode(selectedNode);
        }
    }
    private void editNode(MindNode node) {
        String newText = JOptionPane.showInputDialog(this, 
            "编辑节点文本:", node.getText());
        if (newText != null && !newText.trim().isEmpty()) {
            node.setText(newText.trim());
            adjustNodeSize(node);
            if (autoLayout) {
                layoutTree();
            }
            repaint();
        }
    }
    private void adjustNodeSize(MindNode node) {
        Graphics2D g2d = (Graphics2D) getGraphics();
        if (g2d != null) {
            FontMetrics fm = g2d.getFontMetrics();
            int textWidth = fm.stringWidth(node.getText());
            node.setWidth(Math.max(100, textWidth + 30));
        }
    }
    public void toggleExpand() {
        if (selectedNode != null) {
            selectedNode.toggleExpanded();
            if (autoLayout) {
                layoutTree();
            }
            repaint();
        }
    }
    public void expandAll() {
        expandAllNodes(root);
        if (autoLayout) {
            layoutTree();
        }
        repaint();
    }
    private void expandAllNodes(MindNode node) {
        node.setExpanded(true);
        for (MindNode child : node.getChildren()) {
            expandAllNodes(child);
        }
    }
    public void collapseAll() {
        collapseAllNodes(root);
        if (autoLayout) {
            layoutTree();
        }
        // 只显示根节点
        nodeCount = 1;
        repaint();
    }
    private void collapseAllNodes(MindNode node) {
        if (!node.isRoot()) {
            node.setExpanded(false);
        }
        for (MindNode child : node.getChildren()) {
            collapseAllNodes(child);
        }
    }
    public void changeNodeColor() {
        if (selectedNode != null) {
            Color newColor = JColorChooser.showDialog(this, 
                "选择节点颜色", selectedNode.getColor());
            if (newColor != null) {
                selectedNode.setColor(newColor);
                repaint();
            }
        }
    }
    public void setAutoLayout(boolean autoLayout) {
        this.autoLayout = autoLayout;
        if (autoLayout) {
            layoutTree();
            repaint();
        }
    }
    private void updateStatusBar() {
        // 更新状态栏逻辑(省略具体实现)
    }
    public void saveToFile(java.io.File file) {
        // 保存逻辑(使用对象序列化)
        try {
            java.io.ObjectOutputStream oos = 
                new java.io.ObjectOutputStream(new java.io.FileOutputStream(file));
            oos.writeObject(root);
            oos.close();
            JOptionPane.showMessageDialog(this, "脑图保存成功!");
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this, "保存失败: " + e.getMessage(), 
                "错误", JOptionPane.ERROR_MESSAGE);
        }
    }
    public void loadFromFile(java.io.File file) {
        // 加载逻辑
        try {
            java.io.ObjectInputStream ois = 
                new java.io.ObjectInputStream(new java.io.FileInputStream(file));
            root = (MindNode) ois.readObject();
            ois.close();
            nodeCount = 1 + root.getAllDescendants().size();
            layoutTree();
            selectedNode = root;
            updateStatusBar();
            repaint();
            JOptionPane.showMessageDialog(this, "脑图加载成功!");
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this, "加载失败: " + e.getMessage(), 
                "错误", JOptionPane.ERROR_MESSAGE);
        }
    }
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 绘制连接线和节点
        if (root != null) {
            drawNode(g2d, root);
        }
        // 绘制选中效果
        if (selectedNode != null) {
            g2d.setColor(new Color(255, 100, 100, 50));
            g2d.setStroke(new BasicStroke(3));
            g2d.drawRoundRect(selectedNode.getX() - 3, selectedNode.getY() - 3, 
                selectedNode.getWidth() + 6, selectedNode.getHeight() + 6, 10, 10);
        }
        // 绘制标题
        g2d.setColor(new Color(0, 0, 0, 30));
        g2d.setFont(new Font("微软雅黑", Font.BOLD, 30));
        g2d.drawString("📚 Java 学习脑图", 20, 40);
        // 绘制操作提示
        g2d.setColor(new Color(0, 0, 0, 60));
        g2d.setFont(new Font("微软雅黑", Font.PLAIN, 12));
        g2d.drawString("操作提示: 单击选择节点 | Shift+拖动移动节点 | 双击展开/收起 | TAB添加子节点 | F2编辑 | Delete删除", 
            20, getHeight() - 15);
    }
    private void drawNode(Graphics2D g2d, MindNode node) {
        // 绘制到子节点的连接线
        if (!node.isLeaf() && node.isExpanded()) {
            for (MindNode child : node.getChildren()) {
                drawConnection(g2d, node, child);
                drawNode(g2d, child);
            }
        }
        // 绘制节点
        drawNodeBox(g2d, node);
    }
    private void drawConnection(Graphics2D g2d, MindNode parent, MindNode child) {
        // 使用贝塞尔曲线绘制连接线
        int x1 = parent.getCenterX();
        int y1 = parent.getCenterY();
        int x2 = child.getX();
        int y2 = child.getCenterY();
        int controlX = (x1 + x2) / 2;
        Path2D.Double path = new Path2D.Double();
        path.moveTo(x1, y1);
        path.curveTo(controlX, y1, controlX, y2, x2, y2);
        g2d.setColor(new Color(0, 0, 0, 100));
        g2d.setStroke(new BasicStroke(2));
        g2d.draw(path);
        // 绘制箭头
        drawArrow(g2d, x2, y2, child.getColor());
    }
    private void drawArrow(Graphics2D g2d, int x, int y, Color color) {
        // 绘制小箭头
        g2d.setColor(color.darker());
        int size = 8;
        Polygon arrow = new Polygon();
        arrow.addPoint(x, y - size/2);
        arrow.addPoint(x + size, y);
        arrow.addPoint(x, y + size/2);
        g2d.fill(arrow);
    }
    private void drawNodeBox(Graphics2D g2d, MindNode node) {
        // 绘制节点矩形
        g2d.setColor(node.getColor());
        g2d.fillRoundRect(node.getX(), node.getY(), node.getWidth(), node.getHeight(), 15, 15);
        // 绘制节点边框
        g2d.setColor(node.getColor().darker());
        g2d.setStroke(new BasicStroke(1.5f));
        g2d.drawRoundRect(node.getX(), node.getY(), node.getWidth(), node.getHeight(), 15, 15);
        // 绘制节点文本
        g2d.setColor(Color.BLACK);
        g2d.setFont(new Font("微软雅黑", Font.PLAIN, 13));
        FontMetrics fm = g2d.getFontMetrics();
        int textWidth = fm.stringWidth(node.getText());
        int textX = node.getX() + (node.getWidth() - textWidth) / 2;
        int textY = node.getCenterY() + fm.getAscent() / 2 - 2;
        g2d.drawString(node.getText(), textX, textY);
        // 如果是根节点,绘制特殊样式
        if (node.isRoot()) {
            g2d.setColor(new Color(255, 255, 255, 200));
            g2d.setFont(new Font("微软雅黑", Font.BOLD, 16));
            fm = g2d.getFontMetrics();
            textWidth = fm.stringWidth(node.getText());
            textX = node.getX() + (node.getWidth() - textWidth) / 2;
            textY = node.getCenterY() + fm.getAscent() / 2 - 2;
            g2d.drawString(node.getText(), textX, textY);
        }
        // 如果节点有子节点且已展开,绘制展开图标
        if (!node.isLeaf() && node.isExpanded()) {
            g2d.setColor(new Color(0, 0, 0, 150));
            g2d.drawString("-", node.getX() + node.getWidth() - 15, node.getCenterY() + 5);
        } else if (!node.isLeaf()) {
            g2d.setColor(new Color(0, 0, 0, 150));
            g2d.drawString("+", node.getX() + node.getWidth() - 15, node.getCenterY() + 5);
        }
    }
}

运行方式

  1. 将代码保存为 MindMapDemo.java
  2. 编译:javac MindMapDemo.java
  3. 运行:java MindMapDemo

功能特性

基本操作

  • 添加节点:工具栏按钮或快捷键 TAB
  • 添加兄弟节点:工具栏按钮
  • 删除节点:工具栏按钮或 Delete 键
  • 编辑节点:工具栏按钮、F2 键或双击

交互功能

  • 选择节点:单击选择节点
  • 移动节点:按住 Shift 键拖动
  • 展开/收起:双击节点
  • 键盘方向键:微调选中节点位置

视图功能

  • 自动布局:自动排列节点位置
  • 展开/收起全部:一键展开或收起所有节点
  • 节点颜色:自定义节点颜色

文件功能

  • 保存/加载:支持保存和加载脑图数据

界面效果

  • 使用贝塞尔曲线绘制节点连接线
  • 圆角矩形节点样式
  • 支持图标和表情符号(工具栏)
  • 清晰的状态栏操作提示

这个脑图工具包含了基本的思维导图功能,您可以根据需要添加更多特性,如节点图标、链接、备注等。

抱歉,评论功能暂时关闭!