用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来给界面加上边框。

完成程序,具备想要的功能只是第一步。之后我们需要做的就是去尽量让我们的界面美观,并且,一些能进行缩减的代码尽量缩减,使我们的代码看起来更加的精简干练。

三、总结

一个画板,说简单也简单,说复杂也复杂。以后我们打的程序肯定会越来越复杂,我们需要更加严谨,最好是加上一定的注释(当然我个人写代码是不喜欢写注释的,可能也是因为写的少的原因)。画板谈的比较粗糙,欢迎各位大神指正。

时间: 2024-12-25 20:09:56

用Java语言编写一个简易画板的相关文章

c语言:编写一个简易计算器,打印菜单界面,实现加减乘除运算,可以退出菜单界面

.编写一个简易计算器 程序: #include<stdio.h> enum  OP { EXIT,//0 ADD,//1 SUB,//2 MUL,//3 DIV//4 }; void menu()//menu表示菜单 { printf("**** 1.add  ****\n"); printf("**** 2.sub  ****\n"); printf("**** 3.mul  ****\n"); printf("**** 

使用java语言编写窗口按钮

使用java语言编写窗口按钮 代码如下: package Day08; import java.awt.FlowLayout; import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JTextField; public class ShowFlowLayout extends JFrame { public ShowFlowLayout() { //Set

Swift语言编写一个简单的条形码扫描APP

swift语言编写一个简单的条形码扫描APP 原文地址:appcoda 在处理职员在杂货店的收银台排了很长的队伍,在机场帮助检查背包和旅客,或者在主要的食品供应商,协助处理乏味的存货清单过程,条形码扫描是很简单的处理工具.实际上,他们已经用了这个办法来解决消费者在智能购物,图书分类,等其他目的.因此,让我们来制作一个iPhone版本的条形码扫描工具吧! 对我们来说幸运的是,苹果已经制作了条形码扫描的程序,实现它是一件很简单的事情.我们将要研究进入AV Foundation框架的世界,组建APP,

C语言编写一个&#39;*&#39;金字塔的程序

olj3xg踩系凭珊氏菲<http://weibo.com/LXzpRp/230927982968498303012864> 8e8pxe栏胸俾侔善胶<http://weibo.com/20180414pp/230927983255722881978368> e46952阉友河痹敲呕<http://weibo.com/keMXfnmp/230927983173935224852480> 8fpln0计孪甘摆谱匕<http://weibo.com/20180414p

基于OpenGL编写一个简易的2D渲染框架01——创建窗口

最近正在学习OpenGL,我认为学习的最快方法就是做一个小项目了. 如果对OpenGL感兴趣的话,这里推荐一个很好的学习网站 https://learnopengl-cn.github.io/ 我用的是 vs2013,使用C++语言编写项目.这个小项目叫Simple2D,意味着简易的2D框架.最终的目的是可以渲染几何图形和图片,最后尝试加上一个2D粒子系统和Box2D物理引擎,并编译一个简单的游戏. 第一步,就是创建一个Win32项目. 接下来,生成一个窗口.编写一个RenderWindow类,

Java中编写一个完美的equals方法

首先看下Java语言规范对equals方法的要求: 1,自反性,对于任何非控引用x,x.equals(x)都应该返回true. 2,对称性,对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true. 3,传递性,如果x.equals(y),y.equals(z)都返回true,那么,x.equals(z)返回true. 4,一致性,如果x和y引用的对象没有发生变化,那么无论调用多少次x.equals(y)都返回相同的结果. 5,对于任意非空引用x,

用 C 语言编写一个简单的垃圾回收器

人们似乎认为编写垃圾回收机制是很难的,是一种只有少数智者和Hans Boehm(et al)才能理解的高深魔法.我认为编写垃圾回收最难的地方就是内存分配,这和阅读K&R所写的malloc样例难度是相当的. 在开始之前有一些重要的事情需要说明一下:第一,我们所写的代码是基于Linux Kernel的,注意是Linux Kernel而不是GNU/Linux.第二,我们的代码是32bit的.第三,请不要直接使用这些代码.我并不保证这些代码完全正确,可能其中有一些我 还未发现的小的bug,但是整体思路仍

用java语言实现一个观察者模式

观察者模式(也被称为发布/订阅模式)提供了避免组件之间紧密耦合的另一种方法,它将观察者和被观察的对象分离开.在该模式中,一个对象通过添加一个方法(该方法允许另一个对象,即观察者注册自己)使本身变得可观察.当可观察的对象更改时,它会将消息发送到已注册的观察者.这些观察者收到消息后所执行的操作与可观察的对象无关这种模式使得对象可以相互对话,而不必了解原因.Java语言与C#语言的事件处理机制就是采用的此种设计模式. 例如,用户界面(同一个数据可以有多种不同的显示方式)可以作为观察者,业务数据是被观察

《用C++语言编写一个程序,求PI的值》

1 //编写一个C++程序求PI的值 2 /* 3 PI=16arctan(1/5)-4arctan(1/239) 4 其中arctan用如下形式的极数计算: 5 arctan=x-(x^3/3)+(x^5/7)-(x^7/7)+... 6 */ 7 #include<iostream> 8 using namespace std; 9 double arctan(double x){ 10 double sqr = x*x; 11 double e = x; 12 double r = 0;