用Java语言编写一个简易画板
讲了三篇概博客的概念,今天,我们来一点实际的东西。我们来探讨一下如何用Java语言,编写一块简易的画图板。
一、需求分析
无论我们使用什么语言,去编写一个什么样的项目,我们的第一步,总是去分析这个项目需要满足怎样的需求。
那么,画板需要满足怎样的需要呢?换句话说,在画板上,我们应该赋予它什么功能呢?从我们熟悉的画板来看,我们需要实现诸如铅笔、橡皮、喷枪、刷子的功能,我们可以画出一些规则的图形,比如直线、矩形、圆。最好我们还能调整画笔的颜色和粗细。以上,我们希望的是,当我们点击一个按钮的时候,我们就能实现相对应的功能。一个简单画板的功能大概就这么多了,一些关于添加文本框、放大镜等的操作在简单画板上就不一一实现了。
二、实现画板
1、基本图形的绘制
我们先从最基本的画一些基本图形开始。在Java里有一个画笔类Graphics,而我们实现画板功能的关键就在这里。在Graphics这个画笔类里面,有几个方法,比如:画矩形、圆、直线。了解了这一点,我们的思路就来了。接下来,我们将一步一步认识实现基本图形绘制的全过程。
首先,我们需要实例化一个JFrame容器,并给它设置初始值。其次,我们需要在这个容器上,添加一些按钮。这里以直线、矩形、圆为例。这部分的实现比较简单,需要注意的是,我们可以通过实例化一个字符串类型的数组去存储按钮里的内容,然后通过遍历数组的方式去实现多个按钮的建立,以此减少代码行数。给出代码如下:

其次,因为涉及到关于鼠标点击以及按钮点击的操作,我们需要用到事件监听机制,MouseListener和ActionListener。因为它们都是接口,不能被实例化对象,所以我们需要建立一个事件处理类去实现这两个接口,并重写其中的抽象方法。具体代码如下:

当然,仅仅这样是不够的。首先,我们需要对事件源,也就是我们的按钮和画板界面通过addXXXListener的方式添加事件处理机制。其次,我们还需要对对应的方法,在其方法体中写上我们需要实现的功能。画笔类是一个抽象类,因此它是不能被实例化的,我们只能通过getGraphics这个方法从JFrame或者是JPanel里去获取画笔。注意,画笔的获取要在让界面可见之后。之后,因为我们在事件处理类里面要用到画笔,所以,我们还需要将画笔从我们的画板界面传到我们的事件处理类当中,我们可以在事件处理类里面定义一个setGraphics的方法,同时定义一个Graphics的引用去接收这个画笔。具体代码如下:


接下来就是调用画笔里的方法了。举个例子,当我们获取的按钮里的内容是Line的时候,那么,我们需要调用画笔Graphics里的drawLine方法,我们需要给它起始点和终点的x,y坐标。所以,我们在mousePressed方法里,记录下起始点的x,y坐标,在mouseReleased里记录下释放点的xy坐标,然后调用这个drawLine方法。那么,我们就可以在我们按下和释放的地方连成一条直线。代码实现如下:

这样我们就实现了三个基本图形的绘制。其他的, 在Graphics画笔类里面还有关于弧形、多边形、填充矩形等绘制方法,这里就不一一列举了。
2、画笔颜色与粗细的变化
Java里有一个Color类,它可以用作画笔颜色的设置。除了固定颜色的选取,例如Color.BLACK,我们程序员自己还可以自己去调取颜色。我们知道,每一种颜色,都可以由红、绿、蓝三基色按不同程度调配而成。在Java里有一个关于Color的构造方法,传入三个int类型、范围在0到255之间的数,分别对应红绿蓝三种颜色的程度。我们可以通过Color 颜色名=new Color(int a,int b,int c)的方式去自行定义自己想要的颜色。之后,我们可以通过.setColor的方式,将我们新建的颜色赋给我们的画笔。
而关于画笔粗细的设置方法,它并不在Graphics类里面,而是在它的子类Graphics2D里面。相较于Graphics,Graphics2D同样是一个抽象类,但是它里面新增了不少方法,比如给画笔设置粗细的方法setStroke。而在setStroke这个方法中,你所需要传递的参数是一个Stroke类型的参数,而Stroke是一个接口,所以不能够实例化,因此我们给它传递的是Stroke的一个封装类BasicStroke,它实现了Stroke这个接口。因此,我们设置粗细的方式为:(Graphics2D)g.setStroke(new BasicStroke(float width))。
如果我们需要实现这样的情况,在画板界面里有红、绿、蓝等多种颜色的按钮,当点击后画笔变成相应颜色,我们可以通过给按钮设置颜色的方式去处理这样的问题。给按钮或者窗体设置颜色的方式是.setBackground(Color c)。
3、铅笔、刷子、橡皮以及喷枪的实现
现在,我们知道了如何画一个圆,画一条直线。在实现铅笔等功能钱,我想先问个问题,如何去画一个点?我有两个想法,一个是,你可以通过画实心圆的方式来画一个点,给它半径指定为一个像素,那么所画出的实心圆就成了一个点。第二种,可以通过画直线的方式,指定起始位置和释放位置相同,那么画出的也是一个点。
接下来,我们要讲讲如何实现铅笔的功能。这里需要介绍一个新的事件监听方法,MouseMotionListener。在这个接口里,有一个鼠标拖动的监听方法。我们可以用它来实现铅笔的实现。它的工作机制是每隔一段时间,就获取一次你鼠标所在的位置。当然,这个时间非常短。事实上,我们可以观察在画图板上的直线。我们会发现每一条不规则曲线,都是由若干条直线构造而成的。那么,在这个拖动过程当中,我们可以在开始的地方记录下它的xy坐标,然后每运行一次拖动获取,就改变一次起始的xy坐标,那么,我们就实现了所谓的铅笔绘图功能。因为我们的事件处理类同时也继承了MouseListener,在拖动进行的开始,事实上我们已经进行了一次点击操作,所以,最开始的坐标我们不需要记录。注意,因为是不同的监听方法,所以一定要记得给画板界面添加MouseMotionListener的监听方法。铅笔的具体代码实现如下:

实现了铅笔,刷子其实也就那么回事,我们只需要将铅笔实现这部分的代码复制下来,并在之前添加一行给画笔加粗的代码即可,加粗了的铅笔,不就是我们的刷子吗?至于橡皮,我们也可以这么理解,只要找到与画板界面底色相类似的颜色,给画笔赋这样的颜色,不就能覆盖掉原有的部分了吗?
接下来是关于喷枪的实现。我们可以将喷枪理解成,在铅笔的基础上(去掉铅笔所绘的轨迹),对于一个点,在一定范围内,随机画若干个点。类似的变换,我们可以对一个点的xy坐标加减一定范围内不等的值,画出来的效果,就类似于喷枪效果了。生成随机数的方法在Random这个类里面,所以我们首先需要实例化一个Random类,然后通过nextInt(int a)的方式去获取一个0到a-1的随机数。这样,我们就能实现喷枪了。具体代码如下:

4、重绘
在这个实验过程当中,我们很容易发现一个问题,当我们最小化或者说调整窗体大小的时候,我们所绘制的图形消失了,然而按钮还在。这是为什么呢?事实上,每当我们进行一次最小化或者调整窗体大小的时候,我们原先的那个窗体都被关闭了,接着计算机又重新绘制了一个全新的窗体。所以,我们自行绘制的图形都没有了。那么,为什么我们的按钮还在?事实上,不仅仅是按钮,swing包内其他我们熟悉的组件,账号输入框、密码输入框等元素,也还是存在。事实上,每一个元素、每一个窗体,它都有一个重绘方法。当窗体改变大小的时候,生成新窗体后,计算机会调用这个重绘方法,所以你看到按钮等元素在改变界面后仍然在界面上。那么,如何让我们自行绘制的图形也像按钮一样,不会随着改变窗体而消失呢?
事实上,我们只需要重写一下画图界面上的重绘方法。这也就是为什么我要继承JFrame这个类的原因。因为继承了JFrame,所以,我们可以重写它继承的paint方法,在其中加上我们所绘制的内容,那么在窗体重绘的过程当中,我们所绘制的内容也就重新绘制上去了。
这只不过是重绘的第一步。因为我们绘制的内容有可能过于复杂,那么我们在主类里面如何去得知我们绘制了什么呢?我们可以定义一个shape类,然后在事件处理类里面定义一个shape类型的数组,每画一个图,那么,我们就用数组将它存储下来,然后让index++。为了简化paint方法里的内容,我们可以定义一个shape抽象类,里面定义一个抽象方法draw,然后申明不同图形,让它们都继承shape类,并且重写draw方法。这样,在paint方法里,我们就可以直接调用draw方法,而不用去写各种判断语句。这样做简化了我们的代码。
代码具体实现如下:



这里只给出shapeLine这个类的定义,关于其他类型的图案的定义就没有一一给出了,让看客自行发散。
5、进行界面的美化
假设我们把所有的按钮都如此加上,那么,我们会发现,画图界面会非常的不美观。为了增强界面的美观程度,我们可以通过引进panel的方法。我们可以将画基本图形的按钮放在一块面板内,颜色按钮放在一块面板里,而铅笔等功能又放到一块面板里。同时为了区分每个界面,我们还可以在重绘方法里,编写这样的语句g.drawLine来给界面加上边框。
完成程序,具备想要的功能只是第一步。之后我们需要做的就是去尽量让我们的界面美观,并且,一些能进行缩减的代码尽量缩减,使我们的代码看起来更加的精简干练。
三、总结
一个画板,说简单也简单,说复杂也复杂。以后我们打的程序肯定会越来越复杂,我们需要更加严谨,最好是加上一定的注释(当然我个人写代码是不喜欢写注释的,可能也是因为写的少的原因)。画板谈的比较粗糙,欢迎各位大神指正。
用Java语言编写一个简易画板的更多相关文章
- 使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网)
使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网) 一,前期准备 1,Java IDE(Eclipse)与JDK的安装与配置jdk-15.0.1-免配置路径版提取码:earu免安装版 ...
- 用java语言写一个简易版本的登录页面,包含用户注册、用户登录、用户注销、修改密码等功能
package com.Summer_0421.cn; import java.util.Arrays; import java.util.Scanner; /** * @author Summer ...
- 第二次作业利用java语言编写计算器进行四则运算
随着第一次作业的完成,助教 牛老师又布置了第二次作业:用java语言编写一个程序然后进行四则运算用户用键盘输入一个字符来结束程序显示统计结果.一开始看到这个题目我也着实吓了一跳 因为不知道如何下手而且 ...
- Fastjson是一个Java语言编写的高性能功能完善的JSON库。
简介 Fastjson是一个Java语言编写的高性能功能完善的JSON库. 高性能 fastjson采用独创的算法,将parse的速度提升到极致,超过所有json库,包括曾经号称最快的jackson. ...
- Atiitt 使用java语言编写sql函数或存储过程
Atiitt 使用java语言编写sql函数或存储过程 1.1. java编写sql函数或存储过程的机制1 1.2. Java编写sp的优点1 1.3. 支持java源码,class文件,blog f ...
- 使用java语言编写窗口按钮
使用java语言编写窗口按钮 代码如下: package Day08; import java.awt.FlowLayout; import javax.swing.JButton;import ja ...
- 用C语言编写一个简单的词法分析程序
问题描述: 用C或C++语言编写一个简单的词法分析程序,扫描C语言小子集的源程序,根据给定的词法规则,识别单词,填写相应的表.如果产生词法错误,则显示错误信息.位置,并试图从错误中恢复.简单的恢复方法 ...
- 基于OpenGL编写一个简易的2D渲染框架-06 编写一个粒子系统
在这篇文章中,我将详细说明如何编写一个简易的粒子系统. 粒子系统可以模拟许多效果,下图便是这次的粒子系统的显示效果.为了方便演示,就弄成了一个动图. 图中,同时显示了 7 种不同粒子效果,看上去效果挺 ...
- linux c语言编写一个shell壳
目的:我们要用c语言编写一个shell可以运行在linux机器上的. 介绍:shell所在的层次 我们要做的是操作系统,用于用户与操作系统进行交互的myhsell 思路:用户输入 一行字符串,我们先 ...
随机推荐
- Step by step Dynamics CRM 2013安装
原创地址:http://www.cnblogs.com/jfzhu/p/4008391.html 转载请注明出处 SQL Server可以与CRM装在同一台计算机上,也可安装在不同的计算机上.演示 ...
- UITest 单元测试常用的断言
XCTFail(format…) 生成一个失败的测试: XCTFail(@"Fail"); XCTAssertNil(a1, format...) 为空判断, a1 为空时通过,反 ...
- Atitit 数据库的事件机制--触发器与定时任务attilax总结
Atitit 数据库的事件机制--触发器与定时任务attilax总结 1.1. 事件机制的图谱1 2. 触发器的类型2 3. 实现原理 After触发器 Vs Instead Of触发器2 3.1. ...
- Leetcode-2 Add Two Numbers
#2. Add Two Numbers You are given two linked lists representing two non-negative numbers. The digits ...
- OpenCASCADE Conic to BSpline Curves-Hyperbola
OpenCASCADE Conic to BSpline Curves-Hyperbola eryar@163.com Abstract. Rational Bezier Curve can repr ...
- 前端学PHP之文件操作
× 目录 [1]文件类型 [2]文件属性 [3]目录路径[4]目录遍历[5]目录统计[6]目录增删[7]目录复制[8]文件操作[9]文件内容 前面的话 在程序运行时,程序本身和数据一般都存在内存中,当 ...
- No row with the given identifier exists:
最近在弄一个后台项目,有用到hibernate操作数据库.写hql语句表一对一关联查询的时候报这个错误.受到了csdn上一篇博客的启发,解决了我的问题.他的博客地址:http://blog.csdn. ...
- SubSonic3.0使用例子
前段时间开发的框架使用了SubSonic2.2以后,觉得开发效率提高了不少,后期维护起来也非常方便,不由的喜欢上了SubSonic.中间有想过升级到更高版本,但度娘一下就放弃了,只有极少的中文版说明, ...
- 在Linux中运行Nancy应用程序
最近在研究如何将.NET应用程序移植到非Windows操作系统中运行,逐渐会写一些文章出来.目前还没有太深的研究,所以这些文章大多主要是记录我的一些实验. 这篇文章记录了我如何利用NancyFx编写一 ...
- 【记录】JS 获取 URL 中文参数编码
比如 URL:http://www.xxxx.com/中文参数 这个在 js 获取"中文参数"的时候会出现乱码. 解决方法:decodeURIComponent(获取的中文参数);