所以要把图形也重绘出来的办法就是把图形也存进内存里,重写paint方法把图形也重绘出来。
首先是创建画板的窗体,各种初始化各种添加设置组件,然后实例化了个自己写的动态数组,用来存放图形的
public class MyDraw { //实例化一个动态数组对象 MyArray arr = new MyArray(); //主方法,实例化窗口对象,调用显示方法 public static void main(String[] args) { MyDraw md = new MyDraw(); md.show(); } //显示画板窗口的方法 public void show() { //实例化窗口对象,并初始化窗口 JFrame jf = new JFrame("我的画板"); jf.setSize(600,500); jf.setDefaultCloseOperation(3); jf.setLayout(new BorderLayout()); //实例化北边画板对象,并初始化画板 JPanel jp_north = new JPanel(); jp_north.setPreferredSize(new Dimension(584,65)); jp_north.setBackground(Color.gray); //实例化三个形状按钮,并初始化按钮标注 JButton bu_line = new JButton("直线"); JButton bu_rect = new JButton("矩形"); JButton bu_oval = new JButton("椭圆"); JButton bu_pen = new JButton("画笔"); bu_line.setActionCommand("直线"); bu_rect.setActionCommand("矩形"); bu_oval.setActionCommand("椭圆"); bu_pen.setActionCommand("画笔"); //将三个形状按钮添加到北边画板上 jp_north.add(bu_line); jp_north.add(bu_rect); jp_north.add(bu_oval); jp_north.add(bu_pen); //设置形状按钮的按钮监听器,并加在这三个形状按钮上 TypeActionListener typel = new TypeActionListener(); bu_line.addActionListener(typel); bu_rect.addActionListener(typel); bu_oval.addActionListener(typel); bu_pen.addActionListener(typel); //实例化三个颜色按钮,并初始化标注 JButton bu_red = new JButton("红色"); JButton bu_green = new JButton("绿色"); JButton bu_colorchooser = new JButton("调色板"); bu_red.setActionCommand("红色"); bu_green.setActionCommand("绿色"); bu_colorchooser.setActionCommand("调色板"); //将三个形状按钮添加到北边画板上 jp_north.add(bu_red); jp_north.add(bu_green); jp_north.add(bu_colorchooser); //设置颜色按钮的监听器,并加在这三个按钮上 ColorActionListener colorl = new ColorActionListener(); bu_red.addActionListener(colorl); bu_green.addActionListener(colorl); bu_colorchooser.addActionListener(colorl); //设置南边的画板,并初始化画板 MyJPanel jp_south = new MyJPanel(arr); jp_south.setPreferredSize(new Dimension(584,435)); jp_south.setBackground(Color.WHITE); //将两个画板添加到窗体上 jf.add(jp_north,BorderLayout.NORTH); jf.add(jp_south,BorderLayout.SOUTH); jf.setVisible(true); Graphics g = jp_south.getGraphics(); //实例化鼠标监听器对象,并把鼠标监听器加在南边画板上 MyMouseListener ml = new MyMouseListener(g, typel, colorl,arr,jp_south); jp_south.addMouseListener(ml); jp_south.addMouseMotionListener(ml); } }
然后是定义一个图形的类,它拥有图形类型,颜色,构成图形所需要的点的属性,还有一个用来画图的方法,以及一些get set属性的方法。(值得注意的是,在定义属性的时候,先初始化一下属性,不然在运行的时候可能会导致空指针类型的异常)
public class Shape { private int x1,x2,y1,y2; private Color color = Color.BLACK; private String type = "直线"; //shape具有的可以画图的方法 public void draw(Graphics g){ g.setColor(color); if(type.equals("直线")||type.equals("画笔")){ g.drawLine(x1, y1, x2, y2); }else if(type.equals("矩形")){ g.drawRect(Math.min(x1, x2),Math.min(y1, y2),Math.abs(x2-x1), Math.abs(y2-y1)); }else if(type.equals("椭圆")){ g.drawOval(Math.min(x1, x2),Math.min(y1, y2),Math.abs(x2-x1), Math.abs(y2-y1)); } } public int getX1() { return x1; } public void setX1(int x1) { this.x1 = x1; } public int getX2() { return x2; } public void setX2(int x2) { this.x2 = x2; } public int getY1() { return y1; } public void setY1(int y1) { this.y1 = y1; } public int getY2() { return y2; } public void setY2(int y2) { this.y2 = y2; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public String getType() { return type; } public void setType(String type) { this.type = type; } }
然后就是选择图形和选择颜色的按钮监听器的设置了,说到监听器,在最后的调试的时候总调用不出mouseDragged的方法,原来是在给画板添加鼠标监听器的是只用了jp_south.addMouseListener(ml)而MouseAdapter的mouseDragged是在MouseMotionListener里的,所以还要jp_south.addMouseMotionListener(ml)。
public class TypeActionListener implements ActionListener { private String type = "直线"; public void actionPerformed(ActionEvent e) { //按下形状按钮时获得选择的形状 type = e.getActionCommand(); } public String getType(){ return type; } }
public class ColorActionListener implements ActionListener { private Color color = Color.BLACK; public void actionPerformed(ActionEvent e) { //按下颜色按钮后获得选择的颜色 if(e.getActionCommand().equals("红色")){ color = Color.red; }else if(e.getActionCommand().equals("绿色")){ color = Color.green; }else if(e.getActionCommand().equals("调色板")){ color = JColorChooser.showDialog(null, "请选择颜色", Color.black); } } public Color getColor(){ return color; } }
接下来是鼠标监听器,是要画出图形的方法所在,将按钮的监听器对象传进来获得用户选择的图形类型和颜色,还有数组指针和等下用到重绘方法所在的Panel对象。
public class MyMouseListener extends MouseAdapter { private int x1,y1,x2,y2,x3,y3; private Graphics g; private TypeActionListener typel; private ColorActionListener colorl; private MyArray arr; private MyJPanel Panel; //重写构造器 public MyMouseListener(Graphics g,TypeActionListener typel,ColorActionListener colorl,MyArray arr,MyJPanel Panel) { this.g = g; this.colorl = colorl; this.typel = typel; this.arr = arr; this.Panel = Panel; } public void mousePressed(MouseEvent e) { //记录下第一个点 x1 = x3 = e.getX(); y1 = y3 = e.getY(); } public void mouseReleased(MouseEvent e) { if(typel.getType()!="画笔"){ //创建一个Shape对象 Shape shape = new Shape(); //把从形状和颜色按钮获得的选择保存进shape shape.setColor(colorl.getColor()); shape.setType(typel.getType()); //把点存进shape shape.setX1(x1); shape.setY1(y1); shape.setX2(x2); shape.setY2(y2); //把shape存进动态数组 arr.add(shape); } } public void mouseDragged(MouseEvent e){ //用白色重画之前的图形 if(typel.getType().equals("直线")){ g.setColor(Color.WHITE); g.drawLine(x1, y1, x3, y3); }else if(typel.getType().equals("矩形")){ g.setColor(Color.WHITE); g.drawRect(Math.min(x1, x3),Math.min(y1, y3),Math.abs(x3-x1), Math.abs(y3-y1)); }else if(typel.getType().equals("椭圆")){ g.setColor(Color.WHITE); g.drawOval(Math.min(x1, x3),Math.min(y1, y3),Math.abs(x3-x1), Math.abs(y3-y1)); } //调用Panel的重绘方法,以防白色擦去之前的图形 Panel.paint(g); //获取第二个点 x2 = e.getX(); y2 = e.getY(); //画出移动后新的图形 g.setColor(colorl.getColor()); if(typel.getType().equals("直线")){ g.drawLine(x1, y1, x2, y2); }else if(typel.getType().equals("矩形")){ g.drawRect(Math.min(x1, x2),Math.min(y1, y2),Math.abs(x2-x1), Math.abs(y2-y1)); }else if(typel.getType().equals("椭圆")){ g.drawOval(Math.min(x1, x2),Math.min(y1, y2),Math.abs(x2-x1), Math.abs(y2-y1)); }else if(typel.getType().equals("画笔")){ System.out.println(x1+" "+y1+" "+x2+" "+y2+" "+x3+" "+y3); g.drawLine(x3, y3, x2, y2); //创建一个Shape对象 Shape shape = new Shape(); //把从形状和颜色按钮获得的选择保存进shape shape.setColor(colorl.getColor()); shape.setType(typel.getType()); //把点存进shape shape.setX1(x3); shape.setY1(y3); shape.setX2(x2); shape.setY2(y2); arr.add(shape); } //将新的点保存进旧的点 x3 = x2; y3 = y2; } }
这里比之前的画板新增了个画笔的类型,用mouseDragged在移动的过程中获取新的点然后一直连直线,然后把每条小直线都存进数组去。然后这里还修正了之前在拖动鼠标画图形的时候那个拖动的效果会把之前画上去的图形擦去的现象,就是在每次拖动的时候调用paint方法把图形又重新画上去,这样就看不出曾经擦除过了。
最后就是这章要讲的重绘方法paint了,paint方法会在刷新窗口时自己会调用,所以只需将保存进数组的图形画出就好了,由于shape自己封装了draw的方法,所以直接循环全部画出就好了。
public class MyJPanel extends JPanel { private MyArray arr; public MyJPanel(MyArray arr) { this.arr = arr; } public void paint(Graphics g) { super.paint(g); //将动态数组里的所有元素调用画图的方法重绘所有保存的图形 for(int i=0;i<arr.size();i++){ arr.get(i).draw(g); } } }
还有的就是自己写的动态数组,本来不是给shape用的所以有些方法就不理它了,直接把参数类型改下就拿来给shape用了。
public class MyArray { //初始化数组 Shape [] arr = {}; /** * 往数组里面添加元素的方法 * 通过新建一个原数组大小+1大小的数组实现添加 */ public void add(Shape s) { // System.out.println(s.getX1()+" "+s.getY1()+" "+s.getX2()+" "+s.getY2()); Shape temp [] = new Shape [arr.length+1]; for(int i=0;i<arr.length;i++) temp[i] = arr[i]; temp[arr.length] = s; arr = temp; /* for(int i=0;i<arr.length;i++){ System.out.println(arr[i].getX1()+" "+arr[i].getY1()+" "+arr[i].getX2()+" "+arr[i].getY2()); }*/ } /** * 从数组里面取出元素的方法 */ public Shape get(int index){ return arr[index]; } /** * 获取数组的大小的方法 */ public int size(){ return arr.length; } /** * 删除数组指定元素 * 按照指定的数组下标删除,并返回被删除的元素 */ public Shape delete(int index){ Shape del = arr[index]; Shape [] temp = new Shape[arr.length-1]; for(int i=0;i<temp.length;i++){ if(i<index) temp[i] = arr[i]; else temp[i] = arr[i+1]; } arr = temp; return del; } /** * 清空数组,使得数组初始化为一个空数组 */ public void init(){ arr = new Shape [] {}; } /** * 删除数组指定元素 * 按照指定的元素数据删除,并返回被删除的元素 */ /* public String delete(String s){ String del = null; int j=0,num=0; for(int i=0;i<arr.length;i++){ if(arr[i].equals(s)) num++; } // System.out.println(num); String [] temp = new String[arr.length-num]; for(int i=0;i<temp.length;i++){ if(arr[j].equals(s)){ // System.out.println(" "+i+" "+j); del = arr[j]; j++; temp[i] = arr[j]; j++; } else{ // System.out.println("*"+i+" "+j); temp[i] = arr[j]; j++; } // System.out.println(temp[i]); } arr = temp; return del; } */ /** * 将一个指定元素插入指定下标的数组位置中 */ public void jump(Shape s,int index){ Shape temp [] = new Shape [arr.length+1]; for(int i=0;i<temp.length;i++){ if(i<index) temp[i] = arr[i]; else if(i==index) temp[i] = s; else temp[i] = arr[i-1]; System.out.println(temp[i]); } arr = temp; }
还讲了两个处理异常的方法,一个是空指针类型,一个是数组下标越界,前一个出现得比较多,也在代码里面添加了很多输出语句来看哪些是空指针来调试。
关于空指针类型:使用空的对象名 来调用属性或者方法,则会报空指针异常。
1.确定那个对象名为空,syso输出其值.
2.修改对象名的值
关于数组下标越界:操作超过数组下标的数组元素时,会产生下标越界异常
1.看报错的提示,第几个产生异常了
2.修改数组大小,或者加条判断语句,如果超过下标了就不对数组进行操作了