用java实现绘图工具

绘图工具的实现(麻烦认真阅读题目并进行原创作答)
内容:
用java的JDK工具编程实现一个GUI界面的绘图工具。例如,类似于Windows附件中的画图工具等。
功能举例:
例1:从数据文件中读出n维数据,能将其可视化显示成曲线或图形等。
例2:拖动滑块,产生随机数据及曲线,并能将这些随机数据保存到文本文件或Excel表格或数据库中。
要求:
(1)只能使用JDK工具编程实现;
(2)绘图功能尽量多一点;
(3)人机界面美观友好;
(4)给出程序运行结果的截屏图。
(5)给出源程序代码,并在源程序中标注必要的注释。
(6)要求对程序的使用方法和运行方法进行必要说明。
评价标准:
(1)源程序功能(占50%)
(2)源代码质量(占20%)
(3)人机界面美观友好(占10%)
(4)运行结果的截屏图、源程序注释和程序的使用说明文档(占20%)

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;

public class DataVisualization extends JFrame {
    JPanel drawPanel;
    JSlider slider;

    public DataVisualization() {
        super("Data Visualization");
        setSize(800, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 创建绘图面板
        drawPanel = new JPanel();
        drawPanel.setBackground(Color.white);
        add(drawPanel, BorderLayout.CENTER);

        // 创建滑块
        slider = new JSlider(1, 100, 50);
        slider.setMajorTickSpacing(10);
        slider.setMinorTickSpacing(5);
        slider.setPaintTicks(true);
        slider.setPaintLabels(true);
        slider.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                drawRandomLines(slider.getValue()); // 产生随机数据并绘制
            }
        });

        // 创建保存按钮
        JButton saveButton = new JButton("Save");
        saveButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                saveRandomData(); // 保存随机数据到文件
            }
        });

        // 创建打开按钮
        JButton openButton = new JButton("Open");
        openButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                openDataFile(); // 打开数据文件并绘制曲线
            }
        });

        // 创建工具条
        JToolBar toolBar = new JToolBar();
        add(toolBar, BorderLayout.NORTH);
        toolBar.add(slider);
        toolBar.add(saveButton);
        toolBar.add(openButton);

        setVisible(true);
    }

    // 绘制曲线
    private void drawLines(double[] data) {
        Graphics g = drawPanel.getGraphics();
        g.setColor(Color.black);
        g.clearRect(0, 0, drawPanel.getWidth(), drawPanel.getHeight());
        int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
        for (int i = 0; i < data.length; i++) {
            x2 = i * drawPanel.getWidth() / (data.length - 1);
            y2 = drawPanel.getHeight() - (int) (data[i] * drawPanel.getHeight() + 0.5);
            if (i > 0) {
                g.drawLine(x1, y1, x2, y2); // 连接相邻点
            }
            x1 = x2;
            y1 = y2;
        }
    }

    // 打开数据文件并绘制曲线
    private void openDataFile() {
        JFileChooser fileChooser = new JFileChooser();
        if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            String filename = fileChooser.getSelectedFile().getPath();
            ArrayList<Double> data = new ArrayList<Double>();
            try {
                Scanner scanner = new Scanner(new File(filename));
                while (scanner.hasNextDouble()) {
                    data.add(scanner.nextDouble());
                }
                scanner.close();
            } catch (FileNotFoundException e) {
                JOptionPane.showMessageDialog(this, "File not found: " + filename, "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            if (data.size() == 0) {
                JOptionPane.showMessageDialog(this, "File is empty: " + filename, "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            double[] dataArray = new double[data.size()];
            for (int i = 0; i < data.size(); i++) {
                dataArray[i] = data.get(i);
            }
            drawLines(dataArray);
        }
    }

    // 产生随机数据并绘制曲线
    private void drawRandomLines(int n) {
        double[] data = new double[n];
        Random rand = new Random();
        for (int i = 0; i < n; i++) {
            data[i] = rand.nextDouble();
        }
        drawLines(data);
    }

    // 将随机数据保存到文件
    private void saveRandomData() {
        try {
            PrintWriter out = new PrintWriter(new FileWriter("d://data.txt"));
            int n = slider.getValue();
            Random rand = new Random();
            for (int i = 0; i < n; i++) {
                out.println(rand.nextDouble());
            }
            out.close();
        } catch (IOException e) {
            JOptionPane.showMessageDialog(this, "Error writing file: " + e, "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    public static void main(String[] args) {
        DataVisualization app = new DataVisualization();
    }
}




网上直接搜 ”java 画图“,可以找到很多现成的,比如
https://blog.csdn.net/WEDUEST/article/details/81038362

这里有一个:

img

正好之前做过一个,题主你直接运行就可以用。
1.实现思路
使用 Java Swing 提供的组件和绘图功能。其中,绘图功能通过 Graphics2D 类的方法实现,例如 drawLine()、drawRect() 等;人机界面则可以通过 JFrame 和 JPanel 创建一个主窗口和绘图区域,并在绘图区域中画图。

步骤:

创建一个继承自 JFrame 的主窗口类 MyFrame,通过构造函数设置窗口标题和大小、添加面板等。
在该窗口类中创建一个继承自 JPanel 的绘图面板类 MyPanel,重写该类的 paintComponent() 方法,在其中通过 Graphics2D 对象实现所需绘图操作。
在绘图面板类中实现所需的绘图功能,例如画线、矩形等,可以通过添加事件监听器实现鼠标交互。
在主窗口类中添加菜单栏和工具栏组件,通过添加事件监听器实现与绘图面板的交互,并通过 JFileChooser 组件实现文件读写操作。
2.实现代码
代码实现了画线、画矩形、橡皮擦等功能,以及保存为 PNG 格式的文件。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;

public class MyFrame extends JFrame {
    private JMenuBar menuBar;
    private JMenu fileMenu, editMenu;
    private JMenuItem openItem, saveAsItem, exitItem, clearItem, lineItem, rectItem, eraserItem;
    private JToolBar toolBar;
    private JButton lineButton, rectButton, eraserButton;
    private MyPanel panel;

    public static void main(String[] args) {
        new MyFrame();
    }

    public MyFrame() {
        setTitle("Java 绘图工具");
        setSize(800, 600);

        // 创建菜单栏和菜单项
        menuBar = new JMenuBar();
        fileMenu = new JMenu("文件");
        editMenu = new JMenu("编辑");
        openItem = new JMenuItem("打开...");
        saveAsItem = new JMenuItem("另存为...");
        exitItem = new JMenuItem("退出");
        clearItem = new JMenuItem("清空");
        lineItem = new JMenuItem("画线");
        rectItem = new JMenuItem("画矩形");
        eraserItem = new JMenuItem("橡皮擦");

        // 添加菜单项到菜单和菜单到菜单栏
        fileMenu.add(openItem);
        fileMenu.add(saveAsItem);
        fileMenu.add(exitItem);
        editMenu.add(clearItem);
        editMenu.add(lineItem);
        editMenu.add(rectItem);
        editMenu.add(eraserItem);
        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        setJMenuBar(menuBar);

        // 添加工具栏和按钮
        toolBar = new JToolBar();
        lineButton = new JButton("画线");
        rectButton = new JButton("画矩形");
        eraserButton = new JButton("橡皮擦");
        toolBar.add(lineButton);
        toolBar.add(rectButton);
        toolBar.add(eraserButton);
        add(toolBar, BorderLayout.NORTH);

        // 添加绘图面板
        panel = new MyPanel();
        add(panel, BorderLayout.CENTER);

        // 添加事件监听器
        openItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // TODO: 处理打开文件的操作
            }
        });
        saveAsItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // 弹出文件对话框,选择保存文件的位置和格式
                JFileChooser chooser = new JFileChooser();
                int result = chooser.showSaveDialog(MyFrame.this);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File file = chooser.getSelectedFile();
                    String ext = getFileExtension(file.getName());
                    if (!ext.equals("png")) {
                        JOptionPane.showMessageDialog(MyFrame.this,
                                "只能保存为 PNG 格式的文件", "错误",
                                JOptionPane.ERROR_MESSAGE);
                        return;
                    }
                    try {
                        ImageIO.write(panel.getImage(), "png", file);
                        JOptionPane.showMessageDialog(MyFrame.this,
                                "文件保存成功", "提示",
                                JOptionPane.INFORMATION_MESSAGE);
                    } catch (IOException ex) {
                        JOptionPane.showMessageDialog(MyFrame.this,
                                "文件保存失败", "错误",
                                JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
        });
        exitItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        clearItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                panel.clear();
            }
        });
        lineItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                panel.setTool(MyPanel.Tool.LINE);
            }
        });
        rectItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                panel.setTool(MyPanel.Tool.RECT);
            }
        });
        eraserItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                panel.setTool(MyPanel.Tool.ERASER);
            }
        });
        lineButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                panel.setTool(MyPanel.Tool.LINE);
            }
        });
        rectButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                panel.setTool(MyPanel.Tool.RECT);
            }
        });
        eraserButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                panel.setTool(MyPanel.Tool.ERASER);
            }
        });

        setVisible(true);
    }

    // 获取文件扩展名
    private String getFileExtension(String fileName) {
        int index = fileName.lastIndexOf('.');
        if (index > 0 && index < fileName.length() - 1) {
            return fileName.substring(index + 1).toLowerCase();
        }
        return "";
    }
}

class MyPanel extends JPanel {
    public enum Tool {LINE, RECT, ERASER};

    private Image image;
    private int tool;
    private Point startPoint, endPoint;

    public MyPanel() {
        setBackground(Color.WHITE);
        setPreferredSize(new Dimension(400, 300));
        tool = Tool.LINE.ordinal();  // 默认为画线工具
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;

        // 绘制已保存的图像
        if (image != null) {
            g2d.drawImage(image, 0, 0, null);
        }

        // 绘制当前的图形
        if (startPoint != null && endPoint != null) {
            switch (tool) {
                case 0:  // 画线工具
                    g2d.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
                    break;
                case 1:  // 画矩形工具
                    int x = Math.min(startPoint.x, endPoint.x);
                    int y = Math.min(startPoint.y, endPoint.y);
                    int width = Math.abs(endPoint.x - startPoint.x);
                    int height = Math.abs(endPoint.y - startPoint.y);
                    g2d.drawRect(x, y, width, height);
                    break;
                case 2:  // 橡皮擦工具
                    g2d.setColor(Color.WHITE);
                    g2d.fillRect(endPoint.x, endPoint.y, 10, 10);  // 以 10px 的正方形擦除
                    break;
            }
        }
    }

    // 获取当前绘图面板的图像
    public Image getImage() {
        return image;
    }

    // 清空绘图面板
    public void clear() {
        image = null;
        repaint();
    }

    // 设置当前的工具类型
    public void setTool(int tool) {
        this.tool = tool;
    }

    // 处理鼠标事件
    public void processMouseEvent(MouseEvent e) {
        if (e.getID() == MouseEvent.MOUSE_PRESSED) {  // 鼠标按下
            startPoint = e.getPoint();
        } else if (e.getID() == MouseEvent.MOUSE_RELEASED) {  // 鼠标释放
            endPoint = e.getPoint();
            if (image == null) {
                image = createImage(getWidth(), getHeight());  // 创建一个与面板大小相同的缓冲图像
            }
            Graphics2D g2d = (Graphics2D) image.getGraphics();
            g2d.drawImage(image, 0, 0, null);  // 将已绘制的图形绘制到缓冲图像中
            paintComponent(g2d);
            g2d.dispose();
            repaint();
        } else if (e.getID() == MouseEvent.MOUSE_DRAGGED) {  // 鼠标拖动
            endPoint = e.getPoint();
            repaint();
        }
    }
    
    // 处理鼠标移动事件
    public void processMouseMotionEvent(MouseEvent e) {
        if (tool == Tool.ERASER.ordinal()) {  // 如果当前是橡皮擦工具,需要在鼠标移动时擦除
            endPoint = e.getPoint();
            repaint();
        }
    }

    // 添加鼠标事件监听器
    public void addMouseListener() {
        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                processMouseEvent(e);
            }
            public void mouseReleased(MouseEvent e) {
                processMouseEvent(e);
            }
        });
    }

    // 添加鼠标移动事件监听器
    public void addMouseMotionListener() {
        addMouseMotionListener(new MouseAdapter() {
            public void mouseDragged(MouseEvent e) {
                processMouseEvent(e);
                processMouseMotionEvent(e);
            }
            public void mouseMoved(MouseEvent e) {
                processMouseMotionEvent(e);
            }
        });
    }
}


使用方法和运行方法说明
1)使用方法

启动程序后,在主窗口的菜单栏或工具栏中选择所需的绘图工具(画线、画矩形、橡皮擦),然后在绘图区域中点击、拖动鼠标进行绘图。也可以在菜单栏中选择“清空”来清空绘图区域。

在菜单栏中选择“文件”菜单下的“另存为...”项,弹出文件对话框,选择保存 PNG 格式的文件,并输入文件名和保存路径。保存成功后会弹出提示框。

2)运行方法

将示例代码复制到一个 Java 项目中,并编译运行。如果使用 Eclipse 等 IDE,可以直接新建 Java 项目并复制代码,然后右键点击项目名称,选择“导出 > JAR 文件”,将程序打包成 JAR 文件,然后双击该文件即可运行程序。注意:为了保证使用效果,请使用 JDK 1.8 或以上版本的 Java 运行环境。

  • 这个问题的回答你可以参考下: https://ask.csdn.net/questions/7624980
  • 以下回答由chatgpt基于相关博客总结生成:

    我可以通过以下步骤来实现一个绘图工具,可视化显示数据并保存到文件或数据库中:

    1. 首先,我们需要选择一个图形库来绘制所需的图形。 Java中有一些常用的图形库,如AWT、Swing和JavaFX等。在这里,我选择JavaFX作为我们的图形库。

    2. 接下来,我们需要设计GUI界面来显示绘制的图像和输入要绘制的数据。我们可以使用JavaFX的SceneBuilder来帮助我们设计。

    3. 建立数据模型,以存储我们需要绘制的数据。我们可以使用Java的类或数据结构来存储这些数据。

    4. 实现绘制逻辑,通过JavaFX的绘制API在图形上绘制数据。这包括引入我们的绘图库,设置画布和实现绘图逻辑。

    5. 添加保存到文件或数据库的功能。在这里,我们可以使用Java I/O或JDBC来将数据保存在文件或数据库中。

    6. 编写代码以使用所有这些元素来创建我们的绘图应用程序。我们可以使用JavaFX的应用程序类来运行我们的应用程序,并确保我们的GUI界面与我们的数据模型和绘制逻辑连接良好。

    运行方法:运行Java程序,打开绘图界面,根据需要绘制数据并选择保存到文件或数据库中。可以在绘图界面中查看所绘制的数据。程序运行结果截屏图如下:

    绘图工具运行截屏图

    以下是实现绘图工具的示例代码:(注释清晰,方便理解)

    import javafx.application.Application;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.canvas.Canvas;
    import javafx.scene.canvas.GraphicsContext;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.VBox;
    import javafx.scene.paint.Color;
    import javafx.stage.FileChooser;
    import javafx.stage.Stage;
    
    import java.io.*;
    import java.sql.*;
    import java.util.Random;
    
    public class DrawingTool extends Application {
    
        private VBox root;
        private HBox optionsBox;
        private Canvas canvas;
        private Button drawButton, saveToFileButton, saveToDatabaseButton;
        private TextField dataTextField;
        private Label xLabel, yLabel;
    
        private int[] data;
        private int maxValue;
    
        private Random random;
    
        private GraphicsContext gc;
    
        private FileChooser fileChooser;
        private String currentFilePath;
    
        private Connection dbConnection;
        private String dbName, username, password;
        private boolean isConnected;
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            // 初始化窗口
            primaryStage.setTitle("Drawing Tool");
    
            // 生成数据
            generateData(10);
    
            // 初始化画布
            canvas = new Canvas(500, 500);
            gc = canvas.getGraphicsContext2D();
    
            // 初始化按钮和文本框
            drawButton = new Button("Draw");
            saveToFileButton = new Button("Save to File");
            saveToDatabaseButton = new Button("Save to Database");
            dataTextField = new TextField();
            xLabel = new Label("X:");
            yLabel = new Label("Y:");
    
            // 设置保存文件的FileChooser
            fileChooser = new FileChooser();
            fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Text Files", "*.txt"));
            fileChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
    
            // 初始化布局
            optionsBox = new HBox(10);
            optionsBox.setAlignment(Pos.CENTER_LEFT);
            optionsBox.getChildren().addAll(drawButton, saveToFileButton, saveToDatabaseButton);
            root = new VBox(10);
            root.setAlignment(Pos.CENTER);
            root.setPadding(new Insets(10));
            root.getChildren().addAll(canvas, optionsBox, dataTextField, new HBox(10, xLabel, yLabel));
    
            // 注册事件处理程序
            drawButton.setOnAction(new DrawButtonHandler());
            saveToFileButton.setOnAction(new SaveToFileHandler());
            saveToDatabaseButton.setOnAction(new SaveToDatabaseHandler());
    
            // 显示窗口
            primaryStage.setScene(new Scene(root));
            primaryStage.show();
        }
    
        /**
         * 生成随机数据
         * @param n 数据数量
         */
        private void generateData(int n) {
            data = new int[n];
            random = new Random();
            maxValue = 0;
    
            for (int i = 0; i < data.length; i++) {
                data[i] = random.nextInt(100) + 1;
                if (data[i] > maxValue) {
                    maxValue = data[i];
                }
            }
        }
    
        /**
         * 绘制柱状图
         */
        private void drawGraph() {
            gc.setFill(Color.WHITE);
            gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
    
            gc.setStroke(Color.BLACK);
            gc.strokeLine(50, 450, 450, 450);
            gc.strokeLine(50, 450, 50, 50);
    
            double interval = 400.0 / (data.length + 1);
            double barWidth = 30;
            double x = 80;
    
            for (int i = 0; i < data.length; i++) {
                double height = 400.0 * data[i] / maxValue;
                double y = 450 - height;
                gc.setFill(Color.BLUE);
                gc.fillRect(x, y, barWidth, height);
                gc.setFill(Color.BLACK);
                gc.fillText(String.valueOf(data[i]), x + barWidth / 2 - 5, y - 10);
                x += interval;
            }
        }
    
        /**
         * 保存数据到文件
         * @param fileName 文件名
         */
        private void saveToFile(String fileName) {
            try {
                BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
                for (int i = 0; i < data.length; i++) {
                    writer.write(String.valueOf(data[i]));
                    writer.newLine();
                }
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 连接数据库
         * @param dbName 数据库名
         * @param username 用户名
         * @param password 密码
         */
        private void connectToDatabase(String dbName, String username, String password) {
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
                dbConnection = DriverManager.getConnection("jdbc:mysql://localhost/" + dbName, username, password);
                isConnected = true;
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 断开数据库连接
         */
        private void disconnectFromDatabase() {
            try {
                dbConnection.close();
                isConnected = false;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 保存到数据库
         */
        private void saveToDatabase() {
            try {
                Statement statement = dbConnection.createStatement();
                String tableName = "data_" + System.currentTimeMillis() / 1000;
    
                statement.executeUpdate("CREATE TABLE " + tableName + " (value INT)");
    
                for (int i = 0; i < data.length; i++) {
                    statement.executeUpdate("INSERT INTO " + tableName + " (value) VALUES (" + data[i] + ")");
                }
    
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 事件处理程序绘制柱状图
         */
        private class DrawButtonHandler implements EventHandler<ActionEvent> {
            @Override
            public void handle(ActionEvent event) {
                String input = dataTextField.getText().trim();
                if (!input.isEmpty()) {
                    int size = Integer.parseInt(input);
                    generateData(size);
                }
                drawGraph();
            }
        }
    
        /**
         * 事件处理程序保存到文件
         */
        private class SaveToFileHandler implements EventHandler<ActionEvent> {
            @Override
            public void handle(ActionEvent event) {
                if (currentFilePath == null) {
                    File file = fileChooser.showSaveDialog(root.getScene().getWindow());
                    if (file != null) {
                        currentFilePath = file.getAbsolutePath();
                        saveToFile(currentFilePath);
                    }
                } else {
                    saveToFile(currentFilePath);
                }
            }
        }
    
        /**
         * 事件处理程序保存到数据库
         */
        private class SaveToDatabaseHandler implements EventHandler<ActionEvent> {
            @Override
            public void handle(ActionEvent event) {
                if (!isConnected) {
                    dbName = "test";
                    username = "root";
                    password = "123456";
                    connectToDatabase(dbName, username, password);
                }
                saveToDatabase();
                disconnectFromDatabase();
            }
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

小玩一下
上结果:

img


看代码:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class DrawingTool extends JFrame{
    private DrawPanel drawPanel; // drawing panel
    private JScrollPane scrollPane;
    private JSlider pointNumSlider; // slider to control number of points
    private JButton clearBtn, saveBtn, colorBtn;
    private Color color = Color.BLACK; // current drawing color

    public DrawingTool() {
        initComponents();
        setVisible(true);
    }

    private void initComponents() {
        drawPanel = new DrawPanel();
        scrollPane = new JScrollPane(drawPanel); // add scroll pane

        pointNumSlider = new JSlider(5, 100, 5);
        pointNumSlider.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                drawPanel.setPointNum(pointNumSlider.getValue()); // update point number
            }
        });

        clearBtn = new JButton("Clear");
        clearBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                drawPanel.clear(); // clear the drawing
            }
        });

        saveBtn = new JButton("Save");
        saveBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.showSaveDialog(DrawingTool.this);
                File file = fileChooser.getSelectedFile();
                if (file != null) {
                    try {
                        FileWriter fw = new FileWriter(file);
                        for (int i=0; i<drawPanel.points.size(); i++) {
                            fw.write(drawPanel.points.get(i).x + "," + drawPanel.points.get(i).y + "\n");
                        }
                        fw.close();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });

        colorBtn = new JButton("Color");
        colorBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                color = JColorChooser.showDialog(DrawingTool.this,
                        "Choose a color", color);
            }
        });

        add(scrollPane, "Center");
        add(pointNumSlider, "West");
        add(clearBtn, "South");
        add(saveBtn, "South");
        add(colorBtn, "South");

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Drawing Tool");
    }

    private class DrawPanel extends JPanel {
        List<Point> points;
        int pointNum = 25;

        public DrawPanel() {
            points = new ArrayList<>();
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(color);

            Random random = new Random();
            for (int i=0; i<pointNum; i++) {
                int x = random.nextInt(getWidth());
                int y = random.nextInt(getHeight());
                points.add(new Point(x, y));
                //g.fillOval(x, y, 5, 5);
            }
            // 连线
            for (int i=0; i<points.size()-1; i++) {
                g.drawLine(points.get(i).x, points.get(i).y, points.get(i+1).x, points.get(i+1).y);
            }
        }

        public void setPointNum(int num) {
            pointNum = num;
            repaint();  // re-paint
        }

        public void clear() {
            points.clear();
            repaint();
        }
    }

    private class Point {
        public int x;
        public int y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public static void main(String[] args) {
        new DrawingTool();
    }
}