Java项目——画图软件

简介

作者简介:青铜码农,和大多数同学一样从零开始一步步学习,一步步积累。期待您的关注,让我们一起成长~注:本人学疏才浅,文章如有错误之处,敬请指正~

本章节内容简介:仿Windows系统自带的画图工具。此项目包含了设置画笔粗细、选择画笔颜色、选择背景颜色、绘制图像、清除图像、使用橡皮擦、简笔画窗口等功能。

所需技术如下图:

功能预览:
 

画图完整版视频

功能结构:

搭建项目

01导入资源

02创建DrawPictureFrame窗体类

03创建DrawPictureCanvas画板类

04添加鼠标画笔功能

05添加工具栏

06添加菜单栏

07实现添加水印功能

08添加鼠标效果

09添加简笔画对照窗口

实现过程

一、导入外部Jar包

DrawUtil.mr.Jar包中的类说明:

com.mr.util.BackgroundPanel:可以添加背景图片的面板类

com.mr.util.DrawImageUtil:绘图工具类,提供保存图片的方法

com.mr.util.FrameGetShape:兼容图形选择组件接口

com.mr.util.Shapes:图形选择组件返回的图形类

com.mr.util.ShapeWindow:图形选择组件类

二、创建可视窗体

创建DrawPictureFrame.java这个类,并将下面代码输入到类中:

​
/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
​
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
        ImageIcon imageIcon = new ImageIcon("src/img/picture/themeicon.png");
        Image image = imageIcon.getImage();
        setIconImage(image);// 设置窗体图标
        setResizable(false);// 窗体不能改变大小
        setTitle("画图(水印内容:[\" + shuiyin + \"])");// 设置标题,添加水印内容提示
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(500, 100, 574, 460);
    }
​
    /**
     * 启动程序
     */
    public static void main(String[] args) {
        DrawPictureFrame frame = new DrawPictureFrame();
        frame.setVisible(true);
    }
} 

三、创建画板

import java.awt.*;
​
/**
 * @author WayLon
 * @create 2021-07-28 14:39
 * 备注:创建面板
 */
public class DrawPictureCanvas extends Canvas {
    private Image image = null;
​
    /**
     * 设置画板中的图片
     *
     * @param image - 画板中展示的图片对象
     */
    public void setImage(Image image) {
        this.image = image;
    }
​
    /**
     * 重写paint()方法,在画布上绘制图像
     *
     * @param g
     */
    public void paint(Graphics g) {
        g.drawImage(image, 0, 0, null);
    }
​
    /**
     * 重写update()方法,这样可以解决屏幕闪烁的问题。
     *
     * @param g
     */
    public void update(Graphics g) {
        paint(g);
    }
​
}

并在DrawPictureFrame类中原有代码上新增一下代码:

第5-10、22、25-34行代码为新增代码

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
    BufferedImage image = new BufferedImage(570, 390, BufferedImage.TYPE_INT_BGR);// 创建一个8位BGR颜色分量的图像
    Graphics gs = image.getGraphics();// 获得图像的绘图对象
    Graphics2D g = (Graphics2D) gs;// 将绘图图像转换为Graphics2D类型
    DrawPictureCanvas canvas = new DrawPictureCanvas();//创建画布对象
    Color forecolor = Color.BLACK;// 定义前景色,在这里可以把前景色理解为画笔颜色
    Color bacgroundColor = Color.WHITE;// 定义背景色
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
        ImageIcon imageIcon = new ImageIcon("src/img/picture/themeicon.png");
        Image image = imageIcon.getImage();
        setIconImage(image);// 设置窗体图标
        setResizable(false);// 窗体不能改变大小
        setTitle("画图(水印内容:[\" + shuiyin + \"])");// 设置标题,添加水印内容提示
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(500, 100, 574, 460);
        init();// 组件初始化
    }
​
    /**
     * 组件初始化
     */
    private void init() {
        g.setColor(bacgroundColor);
        g.fillRect(0, 0, 570, 390);// 用背景色填充整个画布
        g.setColor(forecolor);
        canvas.setImage(image);
        getContentPane().add(canvas);// 将画布添加到窗体容器默认布局的中部位置
    }
    /**
     * 启动程序
     */
    public static void main(String[] args) {
        DrawPictureFrame frame = new DrawPictureFrame();
        frame.setVisible(true);
    }
}

四、添加鼠标画笔功能

下面的代码就是在DrawPictureFrame类已有代码的基础上,添加了鼠标监听功能,以便在拖动鼠标时,能够在窗体上绘制线条。

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
/*此处省略相同代码*/
    
    int x = -1, y = -1;// 上一次鼠标绘制点的横、纵坐标
    boolean rubber = false;// 橡皮标识变量
    boolean drawShape = false;// 画图形标识变量
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
/*此处省略相同代码*/
        init();// 组件初始化
        addListener();// 添加组件监听
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
        // 画板添加鼠标移动事件监听
        canvas.addMouseMotionListener(new MouseMotionListener() {
            @Override
            public void mouseDragged(final MouseEvent e) {// 当鼠标拖拽时
                if (x > 0 && y > 0) {// 如果X和Y存在·鼠标记录
                    if (rubber) {// 如果橡皮标识为true,表示使用橡皮
                        g.setColor(bacgroundColor);
                        g.fillRect(x, y, 10, 10);// 在鼠标滑过的位置画填充的正方形
                    } else {// 如果橡皮标识为false,表示用画笔画图
                        g.drawLine(x, y, e.getX(), e.getY());// 在鼠标滑过的位置画直线
                    }
                }
                x = e.getX();
                y = e.getY();
                canvas.repaint();// 更新画布
            }
        canvas.addMouseListener(new MouseAdapter() {
                // 当按键松开时
                @Override
                public void mouseReleased (MouseEvent e){
                    // 将记录上一次鼠标绘制点的横纵坐标恢复成-1
                    x = -1;
                    y = -1;
                }
            }
        }
    /**
     * 组件初始化
     */
    private void init() {
/*此处省略相同代码*/
    }
    /**
     * 启动程序
     */
    public static void main(String[] args) {
/*此处省略相同代码*/
    }
}

补充完这些代码之后,再运行main方法,就可以使用鼠标在空白区域画画了。

五、添加工具栏

1.添加工具栏组件

下面的代码就是在DrawPictureFrame类已有代码的基础上,添加以下代码:

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
/*此处省略未发生变化的代码*/
    boolean drawShape = false;// 画图形标识变量
​
    private JToolBar toolBar;// 工具栏
    private JButton eraserButton;// 橡皮按钮
    private JToggleButton strokeButton1;// 细线按钮
    private JToggleButton strokeButton2;// 粗线按钮
    private JToggleButton strokeButton3;// 较粗按钮
    private JButton backgroundButton;// 背景色按钮
    private JButton foregroundButton;// 前景色按钮
    private JButton clearButton;// 清除按钮
    private JButton saveButton;// 保存按钮
    private JButton shapeButton;// 图形按钮
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
/*此处省略未发生变化的代码*/
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
       /*此处省略未发生变化的代码*/
        }
    /**
     * 组件初始化
     */
    private void init() {
/*此处省略未发生变化的代码*/
        getContentPane().add(canvas);// 将画布添加到窗体容器默认布局的中部位置
​
        toolBar = new JToolBar();// 初始化工具栏
        toolBar.setFloatable(false);
        getContentPane().add(toolBar, BorderLayout.NORTH);// 工具栏添加到窗体最北位置
​
        saveButton = new JButton("保存");
        toolBar.add(saveButton);
        toolBar.addSeparator();// 添加分割条
        //初始化按钮对象,并添加文本内容
        strokeButton1 = new JToggleButton("细线");
        strokeButton1.setSelected(true);// 细线按钮处于被选中状态
        toolBar.add(strokeButton1);
        strokeButton2 = new JToggleButton("粗线");
        toolBar.add(strokeButton2);
        strokeButton3 = new JToggleButton("较粗");
​
        ButtonGroup strokeGroup = new ButtonGroup();// 画笔粗细按钮组,保证同时只有一个按钮被选中
        strokeGroup.add(strokeButton1);
        strokeGroup.add(strokeButton2);
        strokeGroup.add(strokeButton3);
        toolBar.add(strokeButton3);
        toolBar.addSeparator();
        backgroundButton = new JButton("背景颜色");
        toolBar.add(backgroundButton);
        foregroundButton = new JButton("前景颜色");
        toolBar.add(foregroundButton);
        toolBar.addSeparator();
​
        clearButton = new JButton("清除");
        toolBar.add(clearButton);
​
        eraserButton = new JButton("橡皮擦");
        toolBar.add(eraserButton);
}
    /**
     * 启动程序
     */
    public static void main(String[] args) {
/*此处省略未发生变化的代码*/
    }
}

运行一下就可以看到工具栏

2.实现调整画笔粗细功能

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
/*此处省略未发生变化的代码*/
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
/*此处省略未发生变化的代码*/
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
    /*此处省略未发生变化的代码*/
        // 细线按钮添加动作监听
        strokeButton1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_stroke1_actionPerformed();
            }
        });
​
        // 粗线按钮添加动作监听
        strokeButton2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_stroke2_actionPerformed();
            }
        });
​
        // 较粗线按钮添加动作监听
        strokeButton3.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_stroke3_actionPerformed();
            }
        });
    }
    /**
     * 组件初始化
     */
    private void init() {
       /*此处省略未发生变化的代码*/
    }
    
        // 细线按钮及其菜单项所触发的动作事件
    private void do_stroke1_actionPerformed() {
        //  声明画笔的属性粗细为1像素,线条末端无修饰,折线处呈尖角
        BasicStroke bs = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
        g.setStroke(bs);
        strokeButton1.setSelected(true);//这行的选中代码显得有点多余,但是后头的菜单项需要使用到
    }
​
    // 粗线按钮及其菜单项所触发的动作事件
    private void do_stroke2_actionPerformed() {
        //  声明画笔的属性粗细为2像素,线条末端无修饰,折线处呈尖角
        BasicStroke bs = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
        g.setStroke(bs);
        strokeButton2.setSelected(true);//这行的选中代码显得有点多余,但是后头的菜单项需要使用到
    }
​
    // 较粗按钮及其菜单项所触发的动作事件
    private void do_stroke3_actionPerformed() {
        //  声明画笔的属性粗细为4像素,线条末端无修饰,折线处呈尖角
        BasicStroke bs = new BasicStroke(4, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
        g.setStroke(bs);
        strokeButton3.setSelected(true);//这行的选中代码显得有点多余,但是后头的菜单项需要使用到
    }
    
    /**
     * 启动程序
     */
    public static void main(String[] args) {
/*此处省略未发生变化的代码*/
    }
}

自己运行一下看下效果叭、

3.实现添加颜色功能

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
/*此处省略未发生变化的代码*/
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
/*此处省略未发生变化的代码*/
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
/*此处省略未发生变化的代码*/
        // 背景颜色按钮添加动作监听
        backgroundButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_background_actionPerformed();
            }
        });
​
        // 前景色按钮添加动作监听
        foregroundButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_foreground_actionPerformed();
            }
        });
    }
    /**
     * 组件初始化
     */
    private void init() {
 /*此处省略未发生变化的代码*/
    }
    
      // 背景色按钮及其菜单项所触发的动作事件
    private void do_background_actionPerformed() {
        // 打开选择颜色对话框,参数依次为父窗体,标题、默认选中的颜色青色
        Color bgColor = JColorChooser.showDialog(DrawPictureFrame.this, "选择颜色对话框", Color.CYAN);
        if (bgColor != null) {
            bacgroundColor = bgColor;// 将选中的颜色赋给背景色变量
        }
        // 背景色按钮也更换为这种背景颜色
        backgroundButton.setBackground(bacgroundColor);
        g.setColor(bacgroundColor);// 绘图工具使用背景色
        g.fillRect(0, 0, 570, 390);// 画一个背景颜色的方形,填满整个画布
        g.setColor(forecolor);// 绘图工具使用前景色
        canvas.repaint();
    }
​
    // 前景色按钮及其菜单项所触发的动作事件
    private void do_foreground_actionPerformed() {
        Color fColor = JColorChooser.showDialog(DrawPictureFrame.this, "选择颜色对话框", Color.CYAN);
        if (fColor != null) {
            forecolor = fColor;// 将选择的颜色赋值给前景色变量
        }
        // 前景色按钮的文字也更换为这种颜色
        foregroundButton.setForeground(forecolor);
        g.setColor(forecolor);// 绘图工具使用前景色
    }
    
    /**
     * 启动程序
     */
    public static void main(String[] args) {
/*此处省略未发生变化的代码*/
    }
}

运行结果:

4.实现清除图像功能
在DrawPictureFrame类中的addListener()方法中补充以下代码:

        // 清除按钮添加动作监听
        clearButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                g.setColor(bacgroundColor);// 绘图工具使用背景色
                g.fillRect(0, 0, 570, 390);// 画一个背景色的方形,填满整个画布
                g.setColor(forecolor);// 绘图工具使用前景色
                canvas.repaint();
            }
        });
​
        eraserButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (eraserButton.getText().equals("橡皮擦")) {
                    rubber = true;
                    eraserButton.setText("画图");
                } else {
                    rubber = false;
                    eraserButton.setText("橡皮擦");
                    g.setColor(forecolor);
                }
            }
        });
​

运行结果:

5.实现绘制图形功能

首先让DrawPictureFrame类实现接口:FrameGetShape接口

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
/*此处省略未发生变化的代码*/
    boolean rubber = false;// 橡皮标识变量
    // 上为原有代码,为了方便下面新代码寻找位置插入
    boolean drawShape = false;// 画图形标识变量
    Shapes shapes;// 绘画的图形
​
/*此处省略未发生变化的代码*/
​
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
/*此处省略未发生变化的代码*/
    }
​
    /**
     * 启动程序
     */
    public static void main(String[] args) {
/*此处省略未发生变化的代码*/
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
/*此处省略未发生变化的代码*/
        // 当按键按下时
        @Override
        public void mousePressed(MouseEvent e) {
            if (drawShape) {// 如果此时鼠标画的是图形
                switch (shapes.getType()) {// 判断图形的种类
                    case Shapes.YUAN:// 如果是圆形
                        // 计算坐标,让鼠标处于图形中的中心位置
                        int yuanX = e.getX() - shapes.getWidth() / 2;
                        int yuanY = e.getY() - shapes.getHeigth() / 2;
                        // 创建圆形图形并指定坐标和宽高
                        Ellipse2D yuan = new Ellipse2D.Double(yuanX, yuanY, shapes.getWidth(), shapes.getHeigth());
                        g.draw(yuan);//画图工具画此圆形
                        break;
                    case Shapes.FANG:
                        int fangX = e.getX() - shapes.getWidth() / 2;
                        int fangY = e.getY() - shapes.getHeigth() / 2;
                        // 创建方形图形并指定坐标和宽高
                        Rectangle2D fang = new Rectangle2D.Double(fangX, fangY, shapes.getWidth(), shapes.getHeigth());
                        g.draw(fang);// 画图工具画此方形
                        break;
                }
                canvas.repaint();
                // 画图形标识变量为false,说明现在鼠标画的是图形而不是线条
                drawShape = false;
            }
        }
​
        // 图形按钮添加动作监听
        shapeButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 创建图形选择组件
                ShapeWindow shapeWindow = new ShapeWindow(DrawPictureFrame.this);
                int shapeButtonWidth = shapeButton.getWidth();// 获取图形按钮宽度
                int shapeWindowWidth = shapeWindow.getWidth();// 获取图形按钮高度
                int shapeButtonX = shapeButton.getX();// 获取图形按钮,横坐标
                int shapeButtonY = shapeButton.getY();// 获取图形按钮,纵坐标
                // 计算图形组件横坐标,让组件与图形按钮居中对齐
                int shapeWindowX = getX() + shapeButtonX - (shapeWindowWidth - shapeButtonWidth) / 2;
                // 计算图形,组件纵坐标,让组件显示在图形按钮下方
                int shapeWindowY = getY() + shapeButtonY + 80;//
                //设置图形组件坐标位置
                shapeWindow.setLocation(shapeWindowX, shapeWindowY);
                shapeWindow.setVisible(true);// 图形组件可见
            }
        });
​
        /**
         * FrameGetShape接口实现类,用于获得图形空间返回的被选中的图形
         */
        @Override
        public void getShape(Shapes shapes) {
            this.shapes = shapes;// 将返回的图形对象赋给类的全局变量
            drawShape = true;// 画图形标识变量为true,说明现在鼠标画的是图形而不是线条
        }
    }
​
    /**
     * 组件初始化
     */
    private void init() {
     /*此处省略未发生变化的代码*/
        eraserButton = new JButton("橡皮擦");
        toolBar.add(eraserButton);
        //上两行为原有代码,,为了方便下面新代码寻找位置插入
        shapeButton = new JButton("图形");
        toolBar.add(shapeButton);
    }
}

运行结果:

5.实现保存图片功能

将绘制好的图像保存成图片文件,需要用到一个外部的jar文件,就是最开始已经导入的DrawUtil.mr.jar文件。
在DrawPictureFrame类中的addListener()方法中补充以下代码:

        // 保存按钮添加动作监听
        saveButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_save_actionPerformed();
            }
        });

单击保存按钮之后,弹出保存图片对话框:

六、添加菜单栏

1.添加菜单栏组件

新增代码如下:

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
     /*此处省略未发生变化的代码*/
​
    private JMenuItem strokeMenuItem1;// 细线菜单
    private JMenuItem strokeMenuItem2;// 粗线菜单
    private JMenuItem strokeMenuItem3;// 较粗菜单
    private JMenuItem clearMenuItem;// 清除菜单
    private JMenuItem helpMenuItem;// 帮助菜单
    private JMenuItem foregroundMenuItem;// 前景色菜单
    private JMenuItem backgroundMenuItem;// 背景色菜单
    private JMenuItem eraserMenuItem;// 橡皮擦菜单
    private JMenuItem exitMenuItem;// 退出菜单
    private JMenuItem saveMenuItem;// 保存菜单
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
     /*此处省略未发生变化的代码*/
    }
​
    /**
     * 启动程序
     */
    public static void main(String[] args) {
     /*此处省略未发生变化的代码*/
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
            /*此处省略未发生变化的代码*/
    }
​
    /**
     * 组件初始化
     */
    private void init() {
            /*此处省略未发生变化的代码*/
        shapeButton = new JButton("图形");
        toolBar.add(shapeButton);
        //上两行为原有代码,,为了方便下面新代码寻找位置插入
        JMenuBar menuBar = new JMenuBar();// 创建菜单栏
        setJMenuBar(menuBar);// 窗体载入菜单栏
​
        JMenu systemMenu = new JMenu("系统");// 初始化菜单
        menuBar.add(systemMenu);// 菜单栏添加菜单对象
​
        saveMenuItem = new JMenuItem("保存");// 初始化菜单项
        systemMenu.add(saveMenuItem);// 菜单添加菜单项
        systemMenu.addSeparator();// 添加分割条
        exitMenuItem = new JMenuItem("退出");
        systemMenu.add(exitMenuItem);
​
        JMenu strokeMenu = new JMenu("线型");
        menuBar.add(strokeMenu);
        strokeMenuItem1 = new JMenuItem("细线");
        strokeMenu.add(strokeMenuItem1);
        strokeMenuItem2 = new JMenuItem("粗线");
        strokeMenu.add(strokeMenuItem2);
        strokeMenuItem3 = new JMenuItem("较粗");
        strokeMenu.add(strokeMenuItem3);
​
        JMenu colorMenu = new JMenu("颜色");
        menuBar.add(colorMenu);
        backgroundMenuItem = new JMenuItem("背景颜色");
        colorMenu.add(backgroundMenuItem);
        foregroundMenuItem = new JMenuItem("前景颜色");
        colorMenu.add(foregroundMenuItem);
​
​
        JMenu editMenu = new JMenu("编辑");
        menuBar.add(editMenu);
        clearMenuItem = new JMenuItem("清除");
        editMenu.add(clearMenuItem);
        eraserMenuItem = new JMenuItem("橡皮擦");
        editMenu.add(eraserMenuItem);
​
​
        JMenu helpMenu=new JMenu("帮助");
        menuBar.add(helpMenu);
        helpMenuItem =new JMenuItem("说明");
        helpMenu.add(helpMenuItem);
    }
}

​效果如下:

2.给菜单项添加点击事件

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
            /*此处省略未发生变化的代码*/
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
            /*此处省略未发生变化的代码*/
    }
​
    /**
     * 启动程序
     */
    public static void main(String[] args) {
            /*此处省略未发生变化的代码*/
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
    
            /*此处省略未发生变化的代码*/
​
        // 退出菜单栏添加动作监听
        exitMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);// 程序关闭
            }
        });
​
        // 橡皮擦菜单栏添加动作监听
        eraserMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (eraserMenuItem.getText().equals("橡皮擦")) {
                    rubber = true;
                    eraserMenuItem.setText("画图");
                    eraserButton.setText("画图");
                } else {
                    rubber = false;
                    eraserMenuItem.setText("橡皮擦");
                    eraserButton.setText("橡皮擦");
                    g.setColor(forecolor);
                }
            }
        });
​
        //清除菜单添加动作监听
        clearMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                g.setColor(bacgroundColor);
                g.fillRect(0, 0, 570, 390);
                g.setColor(forecolor);
                canvas.repaint();
            }
        });
​
        //细线菜单添加动作监听
        strokeMenuItem1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_stroke1_actionPerformed();
            }
        });
​
        // 粗线菜单添加动作监听
        strokeMenuItem2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_stroke2_actionPerformed();
            }
        });
​
        // 较粗线菜单添加动作监听
        strokeMenuItem3.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_stroke3_actionPerformed();
            }
        });
​
        // 前景色菜单添加动作监听
        foregroundMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_foreground_actionPerformed();
            }
        });
​
        // 背景色菜单添加动作监听
        backgroundMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_background_actionPerformed();
            }
        });
​
        //保存菜单添加动作监听
        saveMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                do_save_actionPerformed();
            }
        });
​
        helpMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(null, "" + "##################\r\n" + "#画图软件使用说明书#\r\n"
                        + "####################\r\n" + "1.本软件可以实现以下功能:\r\n" + "(1)在画布上绘制直线、矩形、圆形等图形\r\n"
                        + "(2)设置画笔的颜色和粗细\r\n" + "(3)添加水印\r\n" + "(4)依据鼠标轨迹绘制曲线\r\n" + "(5)橡皮擦、保存图片\r\n"
                        + "2.本软件主要分为四个模块:菜单、工具栏和画布\r\n" + "(1)菜单栏的文件子菜单包括打开、新建、保存图片以及退出程序\r\n"
                        + "  菜单栏的设置子菜单包括设置画笔的粗细和颜色;\r\n" + "(2)工具栏主要包括保存文件、清空画板、撤回操作、图形绘制;\r\n"
                        + "(3)画布用于图形绘制,使用鼠标选中要绘制的图形即可进行绘制。", "使用说明", JOptionPane.PLAIN_MESSAGE);
            }
        });
        
    }
​
    /**
     * 组件初始化
     */
    private void init() {
                  /*此处省略未发生变化的代码*/
    }
}

3.实现添加水印功能

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
   /*此处省略未发生变化的代码*/
​
    private JMenuItem shuiyinMenuItem;// 水印菜单
    private String shuiyin = "";// 水印字符内容
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
    /*此处省略未发生变化的代码*/
    }
​
    /**
     * 启动程序
     */
    public static void main(String[] args) {
      /*此处省略未发生变化的代码*/
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
       /*此处省略未发生变化的代码*/
​
        // 水印菜单项添加动作监听
        shuiyinMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e)
 {
                shuiyin = JOptionPane.showInputDialog(DrawPictureFrame.this, "你想添加什么水印?");
                if (null == shuiyin) {
                    shuiyin = "";
                } else {
                    setTitle("画图(水印内容:[" + shuiyin + "])");// 设置标题,添加水印内容提示
                }
            }
        });
​
    }
​
     /*此处省略未发生变化的代码*/
    private void addWatermark() {
        if (!"".equals(shuiyin.trim())) {
            g.rotate(Math.toRadians(-30));// 将图片旋转-30度
            Font font = new Font("楷体", Font.BOLD, 32);
            g.setFont(font);
            g.setColor(Color.GRAY);
            AlphaComposite alphaComposite = AlphaComposite.SrcOver.derive(0.4f);// 设置透明效果
            g.setComposite(alphaComposite);// 使用透明效果
            g.drawString(shuiyin, 150, 500);// 绘制文字
            canvas.repaint();
            g.rotate(Math.toRadians(30));// 将旋转的图片再转回来
            alphaComposite = AlphaComposite.SrcOver.derive(1f);// 不透明效果
            g.setComposite(alphaComposite);
            g.setColor(forecolor);// 画笔恢复之前颜色
        }
    }
    /**
     * 组件初始化
     */
    private void init() {
      /*此处省略未发生变化的代码*/
    }
}

效果图如下:

 

4.添加鼠标图标效果

鼠标光标如果一直保持一个样式,会给操作带来了诸多不便,会很难分清当前拿的是画笔还是橡皮。

下面的代码就是在DrawPictureFrame类已有代码的基础上,添加以下代码:

 @Override
            public void mouseMoved(MouseEvent e) {
                if (rubber) {
                    // 设置鼠标指针的形状为图片
                    Toolkit kit = Toolkit.getDefaultToolkit();// 获得系统默认的组件工具包
                    // 利用工具包获取图片
                    Image img = kit.createImage("src/img/鼠标橡皮.png");
                    // 利用工具包创建一个自定义的光标对象
                    // 参数为图片、光标热点和光标描述字符串
                    Cursor c = kit.createCustomCursor(img, new Point(0, 0), "clear");
                    setCursor(c);// 使用自定义的光标
                } else {
                    setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
                }
            }
        });
        // 工具栏添加鼠标移动监听
        toolBar.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                // 设置鼠标指针的形状为默认光标
                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            }
        });

效果图如下:

七、添加简笔画对照窗口

做一个小窗口来展示已经画好的简笔画,可以让读者模仿画出来。

1.创建显示简笔画的窗体

首先创建PictureWindow.java这个类:

import com.mr.util.BackgroundPanel;
​
import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
​
/**
 * @author WayLon
 * @create 2021-07-28 21:08
 * 备注:简笔画展示窗体
 */
public class PictureWindow extends JWindow {
    File list[];// 图片文件数组
    int index;// 当前选中图片的索引
    DrawPictureFrame frame;// 父窗体
    private JButton changeButton;// 更换图片按钮
    private JButton hiddenButton;// 隐藏按钮
    private BackgroundPanel centerPanel;// 展示图片的带背景图面板
​
    /**
     * 构造方法
     *
     * @param frame - 父窗体
     */
    public PictureWindow(DrawPictureFrame frame) {
        this.frame = frame;
        setSize(400, 460);
        init();// 初始化窗体组件
        addListener();// 给组件添加加监听
    }
​
    /**
     * 添加监听
     */
    private void addListener() {
        // 隐藏按钮添加动作监听
        hiddenButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setVisible(false);
                frame.initShowPicButton();// 父类窗体还原简笔画按钮的文本内容
            }
        });
​
        // 更换图片按钮添加动作监听
        changeButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                centerPanel.setImage(getListImage());
            }
        });
    }
​
    /**
     * 组件初始化方法
     */
    private void init() {
        Container c = getContentPane();//获取窗体主容器
        File dir = new File("src/img/picture");// 创建简笔画素材文件夹对象
        list = dir.listFiles();// 获取文件夹里的所有文件
        // 初始化背景面板,使用图片文件夹里的第1张简笔画
        centerPanel = new BackgroundPanel(getListImage());
        c.add(centerPanel, BorderLayout.CENTER);// 背景面板放到主容器中部
        FlowLayout flow = new FlowLayout(FlowLayout.RIGHT);// 创建右对齐的流布局
        flow.setHgap(20);// 水平间隔20像素
        JPanel southPanel = new JPanel();// 创建南部面板
        southPanel.setLayout(flow);// 南部面板使用刚才创建好的流布局
        changeButton = new JButton("更换图片");// 实例化更换图片的按钮
        southPanel.add(changeButton);// 南部面板添加按钮
        hiddenButton = new JButton("隐藏");// 实例化隐藏按钮
        southPanel.add(hiddenButton);// 南部面板添加按钮
        c.add(southPanel, BorderLayout.SOUTH);// 南部面板放到主容器的南部位置
    }
​
    /**
     * 获取图片文件夹下的图片,每次调用此方法都会获得不同的文件对象
     * @return 返回图片对象
     */
    private Image getListImage() {
        String imgPath=list[index].getAbsolutePath();// 获取当前索引下的图片文件路径
        ImageIcon imageIcon=new ImageIcon(imgPath);// 获取此图片文件的图标对象
        index++;
        if (index>=list.length){
            index=0;
        }
        return imageIcon.getImage();// 获取图标对象的图片对象
    }
}

2.实现简笔画窗体与主窗体互相关联

①修改DrawPictureFrame类

在主窗体中添加简笔画展示窗体对象,并添加”展开简笔画“按钮;添加initShowPicButton()方法,当在简笔画窗体中单击”隐藏“按钮时,调用此方法,可使主窗体按钮上的”隐藏简笔画“变回”展开简笔画“字样

/**
 * 画图主窗体。
 */
public class DrawPictureFrame extends JFrame {// 继承窗体类
/*此处省略未发生变化的代码*/
    private PictureWindow pictureWindow;// 简笔画展示窗体
    private JButton showPicButton;// 展开简笔画的按钮
​
    /**
     * 构造方法。
     */
    public DrawPictureFrame() {
/*此处省略未发生变化的代码*/
    }
​
    /**
     * 启动程序
     */
    public static void main(String[] args) {
/*此处省略未发生变化的代码*/
    }
​
    /**
     * 为组件添加动作监听
     */
    private void addListener() {
        /*此处省略未发生变化的代码*/
        // 展示简笔画按钮添加动作监听
        showPicButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e)
 {
                boolean isVisible = pictureWindow.isVisible();// 获取简笔画展示窗体的可见状态
                if (isVisible) {
                    showPicButton.setText("展开简笔画");// 修改按钮的文本
                    pictureWindow.setVisible(false);// 隐藏简笔画展示窗体
                } else {
                    showPicButton.setText("隐藏简笔画");
                    // 重新指定简笔画展示窗体的显示位置
                    pictureWindow.setLocation(getX() - pictureWindow.getWidth() - 5, getY());
                    pictureWindow.setVisible(true);// 简笔画展示窗体可见
                }
            }
        });
    }
​
    /**
     * 恢复展开简笔画按钮的文本内容,此方法供简笔画画板的隐藏按钮调用
     */
    public void initShowPicButton() {
        showPicButton.setText("展开简笔画");
    }
​
​
    // 保存按钮及其菜单项所触发的动作事件
    private void do_save_actionPerformed() {
    /*此处省略未发生变化的代码*/
    }
    // 背景色按钮及其菜单项所触发的动作事件
    private void do_background_actionPerformed() {
    }
​
    // 前景色按钮及其菜单项所触发的动作事件
    private void do_foreground_actionPerformed() {
    /*此处省略未发生变化的代码*/
    }
​
    // 细线按钮及其菜单项所触发的动作事件
    private void do_stroke1_actionPerformed() {
    /*此处省略未发生变化的代码*/
    }
​
    // 粗线按钮及其菜单项所触发的动作事件
    private void do_stroke2_actionPerformed() {
    /*此处省略未发生变化的代码*/
    }
​
    // 较粗按钮及其菜单项所触发的动作事件
    private void do_stroke3_actionPerformed() {
    /*此处省略未发生变化的代码*/
    }
​
    private void addWatermark() {
    /*此处省略未发生变化的代码*/
    }
    /**
     * 组件初始化
     */
    private void init() {
        /*此处省略未发生变化的代码*/
        toolBar.setFloatable(false);
        getContentPane().add(toolBar, BorderLayout.NORTH);// 工具栏添加到窗体最北位置
    // 上二行为原有代码,为了方便下面新代码寻找位置插入
        showPicButton = new JButton("展开简笔画");
        toolBar.add(showPicButton);// 工具栏添加按钮
        /*此处省略未发生变化的代码*/
        helpMenu.add(helpMenuItem);
    // 上一行为原有代码,为了方便下面新代码寻找位置插入
        pictureWindow = new PictureWindow(DrawPictureFrame.this);// 创建简笔画展示面板,并将本类当作它的父窗体
    }
}

效果图如下:

至此,该项目大功告成。

完整源码及素材链接:https://download.csdn.net/download/Waylon_Ma/20625199

我是码龙,如果我的文章对你有帮助,请点个 👍🏻 支持我一下

  • 18
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Waylon1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值