前言
在这里,我使用awt的组件建立一个简单的绘图小程序,能够使用的功能只有压住鼠标左键,然后,就能够进行绘制
实现思路
实现这个方法的思路也比较简单,主要有两点,一点就是对线的绘制,另一点就是对线的保存
线的绘制
由于,在鼠标在程序上拖动的时候会有一系列的点,讲这些点一个个的连接起来,就可以成为一条线段了,多个线段连接起来自然就组成了一幅画了
使用
Graphics g;
g.drawLine(int x1,int y1,int x2,int y2);
自然就可以达成绘制的效果了
而绘制的时候就需要重写paint(Graphics g)
这个函数了,在这个函数当中,是当系统需要绘制的时候调用的,因此,在这里重写了绘制的方法就可以保证程序无论如何都能够讲线完整的绘制出来(防止出现当最大化之类的操作导致的重绘使得图片丢失的现象)
线的保存
线的保存就比线的绘制要稍微复杂一点,首先,他是需要讲一条线所需要的所有的点保存下来,其次,他也需要将每一条线都记录下来(只记录点的话,在绘制的时候会出现线a与线b的首尾相连的现象)
因此我们需要一个二维的数组(List)进行保存
private List<List<Point>> lineList = new ArrayList<>();
同时,也要为这个数组建立一些添加点的操作
private void addPoint(int x,int y){
for(List<Point> pl : lineList){
for(Point p : pl){
if(p.x == x && p.y == y){
return;
}
}
}
lineList.get(lineList.size() - 1).add(new Point(x,y));
}
这里主要是,当检测到要加进来的点已经存在要绘制的点的集合当中了,就去不保存这个点,这是我出于要节省内存考虑的,因为,保存两个同样的点就话会使得其中一个点是没有意义的
控制绘图的细节
在这里出于要压着鼠标才能绘制的原因,因此加了一个标志位isDraw
来确定是否进行绘制,在鼠标压下时为true
,鼠标释放时为flase
,只要进行绘制,就在mouseDragged
的监听器当中添加点的集合,之后,就可以调用绘制的函数进行绘制了
屏幕闪烁优化
如果在mouseDragged
接口直接调用repaint
来直接进行绘图的重绘的话,是很容易出现屏幕闪烁的现象,原因在于,调用这个函数会使得屏幕首先清空,然后再重写绘制,当这个函数的调用速度过大,刷新的速度就会变慢(变成了人眼可捕捉的频率)就会出现闪屏的现象了,优化的方法也很简单,在mouseDragged
接口处直接调用绘图的方法,这样,他是不会进行屏幕的重绘的,只会在原有的基础上再绘制,自然不会出现闪屏的现象了
this.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
if(isDraw[0]) {
addPoint(e.getX(), e.getY());
//当然这里也是可以改进的,因为,这里进行了一个无用的双循环
//(drawLine方法)对于已经绘制在屏幕上的内容不会有任何改变
//最好的优化是,在这里绘制时只使用
drawPicture(getGraphics());
}
}
});
//绘图的方法
private void drawPicture(Graphics g){
Point prePoint = null;
for(List<Point> pl : lineList){
for(Point p : pl){
if(prePoint != null){
g.drawLine(prePoint.x,prePoint.y,p.x,p.y);
}
prePoint = p;
}
prePoint = null;
}
}
其他小细节
撤销的方法,很简单,将lineList
的最后一条线删除,然后调用repaint
即可
注意,在这里是不能够直接调用drawPicture
方法的(绘图的方法),因为,这个方法对于已经绘制在屏幕上的内容时不会有改变的
源码
这是源码,和上面的演示事例的方法或许有些不同,但是,思路是一样的
public class RepaintDrawView extends Frame {
private List<List<Point>> lineList = new ArrayList<>();
private boolean isDraw = false;
private int x = -1,y = -1;
public RepaintDrawView(){
this.setBounds(0,0,300,300);
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
isDraw = true;
startDrawLine();
}
@Override
public void mouseReleased(MouseEvent e) {
isDraw = false;
x = -1;
y = -1;
}
});
this.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
if (isDraw) {
addPoint(e.getX(), e.getY());
//使用一个额外的变量保存上一次的x,y的坐标,从而避免了调用drawPicture
//导致的无用的双循环,x,y在鼠标抬起时清空
if (x != -1 && y != -1) {
getGraphics().drawLine(x, y, e.getX(), e.getY());
}
x = e.getX();
y = e.getY();
}
}
});
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
this.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if(e.isControlDown() && e.getKeyCode() == KeyEvent.VK_Z){
if(lineList.size() > 0){
lineList.remove(lineList.size() - 1);
repaint();
}
}
}
});
this.setVisible(true);
}
@Override
public void paint(Graphics g) {
super.paint(g);
//将保存下的线重写绘制出来
drawPicture(g);
}
private void drawPicture(Graphics g){
Point prePoint = null;
for(List<Point> pl : lineList){
for(Point p : pl){
if(prePoint != null){
g.drawLine(prePoint.x,prePoint.y,p.x,p.y);
}
prePoint = p;
}
prePoint = null;
}
}
private void addPoint(int x,int y){
for(List<Point> pl : lineList){
for(Point p : pl){
if(p.x == x && p.y == y){
return;
}
}
}
lineList.get(lineList.size() - 1).add(new Point(x,y));
}
private void startDrawLine(){
lineList.add(new ArrayList<>());
}
}