Java具有十分丰富的绘图功能,通过调用对象方法,可以实现众多诸如绘制直线,矩形,圆形等功能。
通过实现一个仿画图软件的画图板,可以让我们更好的了解Java中的众多绘图机制,并能够加深对事件概念的认识和使用
在Java中,我们通过使用Graphics和Graphics2D类创建的画笔画布对象,来实现一些基本绘图功能。
首先创建一个继承JFrame的画板类,创建一个窗体对象作为画板,我们便在其上来实现绘画功能。上面说过,我们是通过Graphics类对象来实现绘图功能的,可以通过JFframe窗体对象的getGraphics方法获取该窗体的画笔画布对象,然后便可实现在窗体上的绘图工作。
其次,想像一下,如果要在画板上绘制一条直线,那么应该在窗体上点击鼠标,拖动鼠标,释放鼠标,然后绘制出一条直线,这便要求用到事件机制,可以创建一个监听类继承MouseAdapter类,来实现鼠标上的各种事件监听,MouseAdapter类里包含了所有关于鼠标事件的实现。然后将前面获取的Graphics对象传给监听类,当触发鼠标事件时,便用该getGraphics对象来绘制图案。
绘制一条直线,可以调用Graphics类的drawLine()方法来实现,该方法的API文档介绍如下:
public abstract voiddrawLine(int x1,int y1,int x2,int y2)
在此图形上下文的坐标系中,使用当前颜色在点(x1, y1)
和(x2, y2)
之间画一条线。
参数:x1
- 第一个点的x 坐标。y1
- 第一个点的y 坐标。x2
- 第二个点的x 坐标。y2
- 第二个点的y 坐标。
我们可以在鼠标按下时获取第一个点坐标,鼠标释放时获取第二个点坐标,然后通过此方法来画一条直线。
public void drawRect(int x,int y,int width,int height)
绘制指定矩形的边框。矩形的左边缘和右边缘分别位于 x
和 x + width
。上边缘和下边缘分别位于 y
和 y + height
。使用图形上下文的当前颜色绘制该矩形。
参数:x
- 要绘制矩形的 x 坐标。y
- 要绘制矩形的 y 坐标。width
- 要绘制矩形的宽度。height
- 要绘制矩形的高度。
同样的,可通过按下鼠标获取第一个点坐标,释放鼠标获取第二个点坐标,然后第二个点坐标与第一个点坐标差得到要绘制矩形的长和宽,再调用该方法实现绘制。
public abstract void drawOval(int x,int y,int width,int height)
绘制椭圆的边框。得到一个圆或椭圆,它刚好能放入由x
、y
、width
和height
参数指定的矩形中。椭圆覆盖区域的宽度为width + 1
像素,高度为height + 1
像素。
参数:
x
- 要绘制椭圆的左上角的x 坐标。
y
- 要绘制椭圆的左上角的y 坐标。
width
- 要绘制椭圆的宽度。
height
- 要绘制椭圆的高度。
可以看到该方法是用来画椭圆的,那么怎么画圆呢?显然只要width = height就可以了。
public abstract boolean drawImage(Image img,int x,int y,ImageObserver observer)
绘制指定图像中当前可用的图像。图像的左上角位于该图形上下文坐标空间的 (x, y)。图像中的透明像素不影响该处已存在的像素。
参数:img
- 要绘制的指定图像。如果 img
为 null,则此方法不执行任何操作。x
- x 坐标。y
- y 坐标。observer
- 转换了更多图像时要通知的对象。
返回:
如果图像像素仍在更改,则返回 false
;否则返回 true
此方法在任何情况下都立刻返回,甚至在图像尚未完整加载,并且还没有针对当前输出设备完成抖动和转换的情况下也是如此。
如果图像已经完整加载,并且其像素不再发生更改,则 drawImage
返回true
。否则,drawImage
返回false
,并且随着更多的图像可用或者到了绘制动画另一帧的时候,加载图像的进程将通知指定的图像观察者。
drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
绘制一个覆盖指定矩形的圆弧或椭圆弧边框。
drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
绘制一个由 x 和 y 坐标数组定义的闭合多边形。
drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
用此图形上下文的当前颜色绘制圆角矩形的边框。
drawString(String str, int x, int y)
使用此图形上下文的当前字体和颜色绘制由指定 string 给定的文本。
fill3DRect(int x, int y, int width, int height, boolean raised)
绘制一个用当前颜色填充的 3-D 高亮显示矩形。
fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
填充覆盖指定矩形的圆弧或椭圆弧。
fillOval(int x, int y, int width, int height)
使用当前颜色填充外接指定矩形框的椭圆。
fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
填充由 x 和 y 坐标数组定义的闭合多边形。
fillRect(int x, int y, int width, int height)
填充指定的矩形。
fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
用当前颜色填充指定的圆角矩形。
if(ButtonName.equals("任意多边形")){
dr.drawLine(x1, y1, x2, y2);
}
if(ButtonName.equals("任意多边形")){
if( ( (ClickX - x1) * (ClickX - x1) + (ClickY - y1) * (ClickY - y1) ) < 900){
dr.drawLine(tempX, tempY, x1, y1);
//重绘部分
shapeArray[index++] = rshape;
if(index == 1000)
index = 0;
initmreck();
}
else if( flag2 == true ){
dr.drawLine(tempX, tempY, ClickX, ClickY);
tempX = ClickX;
tempY = ClickY;
}
else{
dr.drawLine(x2, y2, ClickX, ClickY);
tempX = ClickX;
tempY = ClickY;
flag2 = true;
}
乍一看,一脸懵逼的感觉。可以不在意这些概念,绘制分形图形,即在绘图时可以根据一些分形公式计算出响应坐标值,再进行绘制,可以绘制出如下面的图形:
这图形的绘制公式为:
即用该公式计算出相应坐标值,在调用drawLine()方法在每个坐标处绘制一个点,组成起来即可,下面是我绘制该图形的部分代码:
if(ButtonName.equals("分形1")){
int movx = x1;
int movy = y1;
//重绘部分
Shape shape = new Shape();
shape.setS(ButtonName, x1, y1,x2,y2,bColor);
shapeArray[index++] = shape;
if(index == 10000)
index = 0;
int bx = x2 - x1;
int by = y2 - y1;
double fx=0,fy=0,rfx=0,rfy=0;
for(int i = 0; i < 100000; i++){
fx = Math.sin((-2) * rfy) - Math.cos(((-2) * rfx));
fy = Math.sin((-1.2) * rfx) - Math.cos(2 * rfy);
rfx = fx;
rfy = fy;
if(i == 20000)
dr.setColor(Color.red);
if(i == 50000)
dr.setColor(Color.blue);
if(i == 80000)
dr.setColor(Color.green);
dr.drawLine((int)(rfx * bx) + movx, (int)(rfy * bx) + movy, (int)(rfx * bx) + movx, (int)(rfy * bx) + movy);
}
}
//鼠标拖动监听
public void mouseDragged(MouseEvent e){
int x2 = e.getX();
int y2 = e.getY();
if(ButtonName.equals("铅笔")){
dr.drawLine(x1, y1, x2, y2);
//重绘部分
Shape shape = new Shape();
shape.setS(ButtonName,x1,y1,x2,y2,bColor);
shapeArray[index++] = shape;
if(index == 1000)
index = 0;
x1 = x2;
y1 = y2;
}
}
其中x1,y1为按下鼠标时获取的坐标值
private Color changeColor(Color bColor){
int red = bColor.getRed();
int green = bColor.getGreen();
int blue = bColor.getBlue();
red += 4;
green += 4;
blue += 4;
if(red >= 255) red = 255;
if(green >= 255) green = 255;
if(blue >= 255) blue = 255;
return new Color(red, green, blue);
}
if(ButtonName.equals("立体圆")){
dr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
dr.setColor(bColor);
int r = (x2 - x1)/2;
dr.fillOval(x1, y1, r * 2, r * 2);
for(int i = r; i > 0; i--){
dr.setColor(changeColor(dr.getColor()));
if(i % 2 == 0){
x1 = x1 + 1;
y1 = y1 + 1;
}
dr.fillOval(x1, y1, i * 2, i * 2);
}
}
//设置重绘存储图形的数组
private Shape [] shapeArray = new Shape[10000];
public void paint(Graphics g){
super.paint(g);//使基类的paint方法可用
for(int i = 0; i < shapeArray.length; i++){
if(null != shapeArray[i]){
Shape shape = shapeArray[i];
if(shape.Type.equals("直线")){
//设置线条粗细
((Graphics2D) g).setStroke(shape.s);
g.setColor(shape.Color);
g.drawLine(shape.x1, shape.y1, shape.x2, shape.y2);
}
}