当我们写好一个简易画板,然后在上面画线,画圆等,再把窗口最小化,接着打开,你发现了什么??对,很神奇的事发生了,不是在做梦,之前的所有图形全部消失了,why??原来, 当窗体在屏幕上显示时,首先是将窗体对象的数据从内存中取出来放到缓存中,再在屏幕上进行绘制。当窗体发生改变的时候,程序会重新从内存中获取更新后的数据绘制。all effort are in vain!那怎么办呢?嗯嗯,方法当然是有的啦,就是重绘!!
通过重写JFrame类的paint(Graphics g)方法来保存我们画过的形状,大概就是下面这么个过程:
1)创建保存形状的队列(采用泛型)
2)每绘制一个形状,就将绘制过的形状保存到队列。
这里就要提到图形类,其实就是创建一个图形的类,里面有画这个图形的方法啦。不过在此前,要创建一个抽象的Shape类,让图形类去继承它;因为要创建很多图形类嘛,哈哈。
之后呢要在事件源上的g(Graphics类)进行重写paint()方法,把队列中存储的图形全部绘制出来,这样就搞定啦。不过要提醒的是,如果是在窗体上进行重写paint()方法,图形可能会越界到其他面板上哦!!
大概的代码如下:
首先是实现类:
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JColorChooser; import javax.swing.JFrame; import javax.swing.JPanel; public class Draw { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //实例化一个画板对象 Draw sd=new Draw(); //调用显示画板的方法 sd.showUI(); } public void showUI() { /* * 实例化一个窗体对象 */ JFrame jf=new JFrame(); jf.setTitle("普通画板");//设置窗体标题 jf.setSize(600,500);//设置窗体大小 jf.setLayout(new BorderLayout());//设置窗体布局为边框布局 jf.setResizable(false);//禁止窗体改变大小 jf.setLocationRelativeTo(null);//设置窗体居中 jf.setDefaultCloseOperation(3);//设置关闭窗体关闭程序 //调用创建北边面板方法 JPanel northpanel=creatNorthPanel(); //将面板添加到窗体上 jf.add(northpanel,BorderLayout.NORTH); //调用创建中间面板方法 JPanel centerpanel=creatCenterPanel(); //将面板添加到窗体上 jf.add(centerpanel,BorderLayout.CENTER); jf.setVisible(true);//设置窗体为可见 // 先让窗体可见,再弄下面的程序!!!!!!!!!!!!!!!!!!!!!!!! Graphics g=centerpanel.getGraphics();//从中间面板中接收Graphics对象 DrawListener ml=new DrawListener(g,this);//实例化一个DrawListener类对象 //给中间面板添加动作监听器,绑定MouseListener事情处理类对象 centerpanel.addMouseListener(ml); //给中间面板添加动作监听器,绑定MouseMotionListener事件处理类对象 centerpanel.addMouseMotionListener(ml); } private String str="Line";//定义一个存储图像的属性值 /* * 获得存储图像的属性 */ public String getStr() { return str; } private Color color;//定义颜色的属性值 /* * 获得颜色的属性 */ public Color getColor() { return color; } /* * 创建北边面板的方法 */ public JPanel creatNorthPanel() { JPanel north_panel=new JPanel();//实例化北边面板的对象 //设置北边面板的属性 north_panel.setPreferredSize(new Dimension(500,100));//设置北边面板大小 north_panel.setLayout(new FlowLayout(FlowLayout.LEFT));//设置北边面板布局为流式布局,并向左对齐 //匿名,实现ActionListener接口 ActionListener al=new ActionListener() { //事情发生类方法 public void actionPerformed(ActionEvent e) { //判断是否点击到"Color"按钮 if(e.getActionCommand().equals("Color")) { //弹出一个颜色选择器,供选择颜色;选择好的颜色返回color color=JColorChooser.showDialog(null, "颜色选择器", Color.BLACK); } else {//获取按钮上的文本 str=e.getActionCommand();//将获得的字符赋给字符串str } } };//注意加“;”!!!!!!!!!!!!!!!!!! //创建一位数组,储存按钮上的文本 String []array={"Line","Rectangle","Oval","RoundRect","Polygon","Pencil","Color","Eraser","Brush","Gunjet"}; // 遍历数组,创建按钮,并添加文本在按钮上 for(int i=0;i<array.length;i++) { JButton jb=new JButton(array[i]);//实例化按钮对象 jb.setPreferredSize(new Dimension(100,40));//设置按钮大小 //给事件源添加动作监听器方法,绑定事件处理类对象 jb.addActionListener(al); north_panel.add(jb);//将按钮添加到北边面板上 } return north_panel; } /* * 创建中间面板的方法 */ public JPanel creatCenterPanel() { JPanel center_panel=new JPanel(){ //重写paint()方法 public void paint(Graphics g) { super.paint(g);//调用父类的paint()方法 //绘制存储的图形 for(int i=0;i<DrawListener.list.size();i++) { Shape shape=DrawListener.list.get(i);//获取存储图像类 shape.draw(g);//调用图形类绘制方法 } } };//实例化中间面板的对象 center_panel.setBackground(Color.WHITE);//设置面板的背景色 return center_panel; } }
然后是数组队列的创建:
import java.util.Collection; public interface SelfArrayList <E>{ public void add(E e);//向队列尾部添加元素方法 public boolean remove(E e);//移除此列表中首次出现的指定元素(如果存在) public E get(int index);//取得队列中指定位置的一个对象 public int size();//得到队列长度 public void add(int index, E e); //将指定的元素插入此列表中的指定位置。 public boolean addAll(Collection<? extends E> c);//按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。 public void clear();// 移除此列表中的所有元素 public int indexOf(E e);//返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。 public boolean isEmpty();//如果此列表中没有元素,则返回 true public int lastIndexOf(E e);//返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。 public E set(int index, E e);// 用指定的元素替代此列表中指定位置上的元素。 public E remove(int index);//移除此列表中指定位置上的元素。 }
然后是Shape的抽象类:
import java.awt.Color; import java.awt.Graphics; public abstract class Shape { private int x1,x2,y1,y2;//定义坐标属性 private Color color;//定义颜色属性 private int width;//定义宽度属性 private int height;//定义高度属性 //构造方法 public Shape(int x1,int x2,int y1,int y2,Color color,int width,int height) { this.x1=x1; this.x2=x2; this.y1=y1; this.y2=y2; this.color=color; this.width=width; } public void draw(Graphics g) { } public void setx1(int x1) { this.x1=x1; } public int getx1() { return x1; } public void setx2(int x2) { this.x2=x2; } public int getx2() { return x2; } public void sety1(int y1) { this.y1=y1; } public int gety1() { return y1; } public void sety2(int y2) { this.y2=y2; } public int gety2() { return y2; } public void setColor(Color color) { this.color=color; } public Color getColor() { return color; } public void setWidth(int width) { this.width=width; } public int getWidth() { return width; } public void setHeight(int height) { this.height=height; } public int getHeight() { return height; } }
然后是各种图形类,这里就只放一个直线类和橡皮擦类啦:
import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; /* * 直线的图形类 */ public class ShapeLine extends Shape { //继承的子类要和父类有同一套构造方法 public ShapeLine(int x1, int x2, int y1, int y2, Color color, int width) { super(x1, x2, y1, y2, color, width, width); // TODO Auto-generated constructor stub } //画直线的方法 public void draw(Graphics g) { g.setColor(this.getColor());//设置图形颜色 Graphics2D g2=(Graphics2D)g;//强制转型,把Graphics类型转换成Graphics2D类型 g2.setStroke(new BasicStroke(this.getWidth()));//设置线条粗细 g2.drawLine(getx1(), gety1(), getx2(), gety2());//画直线 } }
import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; public class ShapeEraser extends Shape { public ShapeEraser(int x1, int x2, int y1, int y2, Color color, int width, int height) { super(x1, x2, y1, y2, color, width, height); // TODO Auto-generated constructor stub } public void draw(Graphics g) { g.setColor(this.getColor());//设置白色颜色 Graphics2D g2=(Graphics2D)g;//强制转型 //设置笔的粗细为2.0F,线条端点是CAP_ROUND,点划线模式为 JOIN_ROUND g2.setStroke(new BasicStroke(10.0F,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); g2.drawLine(getx1(),gety1(),getx2(),gety2()); g2.setStroke(new BasicStroke()); } }
最后是事件监听器:
import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.Random; public class DrawListener implements MouseListener ,MouseMotionListener { private int x1,x2,y1,y2;//记录鼠标两次点击的坐标 private Graphics g;//从界面对象上得到的画布对象 private Graphics2D gg; private Draw sd; private int flag=1; public int m,n; public int x3,y3,x4,y4; private int m1=0,n1=0; //创建一个数组队列,存储图像类 public static UseArrayList<Shape> list=new UseArrayList<Shape>(); //构造方法,用来接收Graphics对象和Draw类对象 public DrawListener(Graphics g,Draw sd){ this.g=g; this.sd=sd; } //鼠标按下的点的坐标 public void mousePressed(MouseEvent e){ //记录第一次点击的x,y:通过事件对象e得到的 x1=e.getX(); y1=e.getY(); //获得点击“Polygon”时初次点击的点的坐标 if(sd.getStr().equals("Polygon")) { x3=e.getX(); y3=e.getY(); flag++; if(flag==2) { m=x3; n=y3; x4=m; y4=n; } } //判断鼠标是按左键还是右键 if(e.getButton()==1) { //按左键,颜色为sd得到的黑色 g.setColor(sd.getColor()); } else if(e.getButton()==3) { //按右键,颜色为绿色 g.setColor(Color.GREEN); } } //鼠标释放时的坐标 public void mouseReleased(MouseEvent e) { //记录第二次点击时的坐标 x2=e.getX(); y2=e.getY(); //判断是否为Line if(sd.getStr().equals("Line")) { // //调用画布对象的方法,画直线 // g.drawLine(x1,y1,x2,y2); Shape shapeLine=new ShapeLine(x1,x2,y1,y2,Color.BLACK,1); shapeLine.draw(g); list.add(shapeLine);//将直线类添加到队列上 } /* * 判断是否为矩形Rectangle */ if(sd.getStr().equals("Rectangle")) { // g.drawRect(Math.min(x1, x2), Math.min(y1, y2),Math.abs(x1-x2), Math.abs(y1-y2));//Math.min(x1, x2), Math.min(y1, y2)! Shape shapeRectangle=new ShapeRectangle(x1,x2,y1,y2,Color.BLACK,1,4); shapeRectangle.draw(g); list.add(shapeRectangle);//将矩形类添加到队列上 } /* * 判断是否为椭圆形Oval */ if(sd.getStr().equals("Oval")) { // g.drawOval(x1, y1,Math.abs(x1-x2) , Math.abs(y1-y2)); Shape shapeOval=new ShapeOval(x1,x2,y1,y2,Color.BLACK,1,4); shapeOval.draw(g); list.add(shapeOval);//将椭圆类添加到队列上 } /* * 判断是否为RoundRect */ if(sd.getStr().equals("RoundRect")) { // g.drawRoundRect(x1, y1, Math.abs(x1-x2),Math.abs(y1-y2), Math.abs(x1-x2)/2,Math.abs(y1-y2)/2 ); Shape shapeRoundRect=new ShapeRoundRect(x1,x2,y1,y2,Color.BLACK,1,4); shapeRoundRect.draw(g); list.add(shapeRoundRect); } /* * 判断是否为Polygon */ //获取拖动过程中的坐标值 int x = e.getX(); int y = e.getY(); if(sd.getStr().equals("Polygon")) { //判断鼠标点击的坐标是不是与第一次点击的一样,不同则画线 if((x!=m)||(y!=n)) { // //画线 // g.drawLine(x4, y4, x, y); Shape shapePolygon=new ShapePolygon(x4,x,y4,y,Color.BLACK,1,4); shapePolygon.draw(g); list.add(shapePolygon); //赋值 x4=x; y4=y; } // Shape shapePolygon=new ShapePolygon(x4,y4,x,y,Color.BLACK,1,4); // shapePolygon.draw(g); // list.add(shapePolygon); } } /* * 当鼠标在事件源上单击时按键执行的方法 */ public void mouseClicked(MouseEvent e) { if(sd.getStr().equals("Polygon")) { //获取点击过程中的坐标值 int x = e.getX(); int y = e.getY(); //如果双击鼠标,当前的点会与初始点连线 if(x==m1&&y==n1) { // g.drawLine(x, y, m, n); Shape shapePolygon=new ShapePolygon(x,m,y,n,Color.BLACK,1,4); shapePolygon.draw(g); list.add(shapePolygon); flag=1;//使flag归为1,便能改变再次画多边形时的第一次单击的坐标值 } m1=e.getX(); n1=e.getY(); } } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} /* * 当鼠标在事件源上拖拉时按键执行的方法 */ public void mouseDragged(MouseEvent e) { //判断点击的按钮是否为"Pencil" if(sd.getStr().equals("Pencil")) { //获取x,y的坐标值 int x=e.getX(); int y=e.getY(); // //画线 // g.drawLine(x1, y1, x, y); Shape shapePencil=new ShapePencil(x1,x,y1,y, Color.BLACK, 1, 4); shapePencil.draw(g); list.add(shapePencil); //赋值 x1=x; y1=y; } /////////////////////////////////////////////////////////////////////////////////////////////////////// //判读点击的按钮是否为"Eraser" if(sd.getStr().equals("Eraser")) { //获取x,y的坐标值 int x=e.getX(); int y=e.getY(); // //设置线条为白色 // g.setColor(Color.WHITE); // //画线 // gg =(Graphics2D)g; //实例化一个Graphics2D对象 // //设置笔的粗细为2.0F,线条端点是CAP_ROUND,点划线模式为 JOIN_ROUND // gg.setStroke(new BasicStroke(10.0F,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); // gg.drawLine(x1,y1,x,y); // gg.setStroke(new BasicStroke()); Shape shapeEraser=new ShapeEraser(x1,x,y1,y,Color.WHITE,1,4); shapeEraser.draw(g); list.add(shapeEraser); //赋值 x1=x; y1=y; } /////////////////////////////////////////////////////////////////////////////////////////////////////// //判读点击的按钮是否为"Brush" if(sd.getStr().equals("Brush")) { //获取x,y的坐标值 int x=e.getX(); int y=e.getY(); // //设置线条为选择的颜色 // g.setColor(sd.getColor()); // //画线 // gg =(Graphics2D)g; //实例化一个Graphics2D对象 // //设置笔的粗细为2.0F,线条端点是CAP_ROUND,点划线模式为 JOIN_ROUND // gg.setStroke(new BasicStroke(10.0F,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); // gg.drawLine(x1,y1,x,y); // gg.setStroke(new BasicStroke()); Shape shapeBrush=new ShapeBrush(x1,x,y1,y,Color.BLACK,1,4); shapeBrush.draw(g); list.add(shapeBrush); //赋值 x1=x; y1=y; //////////////////////////////////////////////////////////////////////////////////////////// } //判读点击的按钮是否为"Gunjet" if(sd.getStr().equals("Gunjet")) { //获取x,y的坐标值 int x=e.getX(); int y=e.getY(); int x0=0; int y0=0; // //设置线条为黑色 // g.setColor(sd.getColor()); // //噴槍大小 // int size=8; // //噴槍点數 // int count=10; // for(int i=0;i<count;i++) // { x0 = new Random().nextInt(size)+ 1; // y0 = new Random().nextInt(size) + 1; // g.fillRect(x+x0, y+y0,5,5); // } Shape shapeGunjet=new ShapeGunjet(x1,x,y1,y,Color.BLACK,1,4); shapeGunjet.draw(g); list.add(shapeGunjet); } } /* * 事件源上移动鼠标的方法 */ public void mouseMoved(MouseEvent e) { } }
这样就基本完成啦,然后至于对界面的美好等就需要继续努力咯!!
相关推荐
4. **事件处理**:在易语言中,我们需要监听窗口或画板的特定事件,比如“窗口初始化”或“画板重绘”。在这些事件的处理函数中,我们可以调用绘制函数来更新进度条的显示。 5. **进度更新**:当程序执行的任务进度...
在易语言中,我们可以通过编写事件处理程序,如“画板重绘”事件,来控制画板上的内容显示。 制作进度条的关键步骤包括以下几个方面: 1. **创建画板组件**:在易语言的界面设计中,添加一个画板组件到窗体上,...
易语言画板自绘源码,画板自绘,标尺子程序_绘制标尺刻度,恢复鼠标状态,无拖动时激活恢复,刻度区重绘,客户区重绘,二级缓冲绘制,客户区刷新,选中辅助线,高亮辅助线,拖动辅助线,客户绘制的图形,GetProp,SetRect,SetProp,...
4. **内存位图**:为了提高效率和避免频繁的屏幕重绘,通常会使用内存位图来保存当前画板的状态。当需要更新画板时,将内存位图的内容复制到窗口上,而不是重新绘制所有图形。 5. **文本处理**:除了图形绘制,自绘...
在易语言中,开发者需要理解窗口消息机制,比如WM_PAINT消息,这是窗口需要重绘时发送的消息。收到该消息后,开发者需要在OnPaint事件中执行自绘代码,确保界面的更新。同时,界面的绘制应考虑效率,避免不必要的重...
3. 事件响应:设置画板的绘图事件,如WM_PAINT消息,当需要更新画布时,调用绘制逻辑函数进行重绘。 4. 数据管理:存储列表项的数据结构,如数组或列表,用于驱动绘图逻辑。 5. 用户交互:处理鼠标和键盘事件,使...
1. **自绘机制**:自绘的核心在于响应窗口的重绘事件,通过`窗口过程`函数捕获并处理这些事件。在易语言中,你需要覆盖默认的绘制行为,使用`画图`命令绘制列表框的背景、条目和选中状态。 2. **列表重画**:在列表...
在这个“易语言画板自绘列表”主题中,我们主要讨论的是如何在易语言环境下创建和操作自绘列表,这涉及到多个核心概念和技术。 首先,**画板**是编程中常见的一种图形用户界面(GUI)组件,它允许程序员在上面进行...
在这个源码中,开发者需要实现OnPaint事件,该事件会在控件需要重绘时被触发,用于执行实际的绘图操作。 2. **图形API的使用**:易语言提供了丰富的图形API,如画线、画圆、填充颜色等。这些函数可以用来在控件上...
6. **图形缓存**:掌握如何利用内存中的位图进行图形缓存,提高界面重绘性能。 7. **界面交互设计**:设计并实现用户与自绘界面的交互逻辑,如按钮点击、拖放操作等。 8. **源码分析**:通过对提供的源码进行分析...
在VS2015环境下,利用MFC框架实现的一款高仿“画图”工具,实现绝大部分功能甚至更多创新点。(注释超详细) 实现了点、直线、曲线、折线、矩形、圆形、多边形等等形状,并且具有区域限制、鼠标捕捉等功能;...
2. **事件驱动编程**:易语言是基于事件驱动的,源码中会包含各种事件处理函数,如`鼠标点击事件`、`窗口重绘事件`等。这些事件触发相应的绘图操作,实现动态效果。 3. **坐标系统和绘图模式**:了解易语言中的坐标...
1. **注册消息处理**:首先,我们需要注册WM_PAINT消息,当系统需要重绘列表框时,会发送这个消息。在易语言中,这通常通过`窗口过程`或`消息过滤`函数实现。 2. **初始化画笔和刷子**:在自绘开始前,我们需要设置...
2. **绘制列表项**:在画板的重绘事件中,我们需要遍历数据源,根据每一项数据的内容和格式,在画板上进行绘制。这可能涉及到字符串格式化、字体设置、颜色选择等细节。 3. **滚动处理**:为了实现滚动功能,需要...
在易语言中,画板通常会有一个与之关联的事件,例如“画板重绘”事件,当画板需要更新时(如滚动条移动),这个事件会被触发。在这个事件处理程序中,我们会根据滚动条的位置来决定应该在画板上显示哪一部分的图片。...
6. **优化性能**:自绘可能会带来额外的性能开销,因此在实现自绘编辑框时,需要注意尽可能减少不必要的重绘,例如使用更新区域或者双缓冲技术来提高效率。 7. **兼容性与扩展性**:自绘编辑框可能需要考虑与其他...
易语言中的事件处理是编程的核心部分,比如"画板重绘"事件,当画板需要更新时,会触发这个事件,我们在这里编写绘制列表的代码。此外,还有"鼠标点击"事件,可以用来处理用户的交互,如选择列表项。 再者,要掌握...
- 使用离屏渲染优化,避免在屏幕更新时频繁的重绘操作。 - 如果可能,可以采用OpenGL或Metal进行更底层的图形处理,提升性能。 11. **测试与适配**: - 在不同尺寸和分辨率的设备上进行测试,确保界面适应性和...
3. 处理绘图事件:在画板的"画板重绘"事件中,首先清空画板(通常用白色填充),然后根据需要调整画笔的颜色、透明度等属性。接着,使用"绘制文本"命令在画板的指定位置绘制标签的内容。 4. 实现透明效果:透明度...