JAVA 畫圖板實現(基本畫圖功能+界面UI)二、功能實現及重繪實現
上篇博客中介紹了界面的實現方法,在這篇博客中將對每個按鈕的功能的實現進行講解並介紹重繪
首先肯定要添加事件監聽機制了,那麽問題來了,事件源對象是誰?需要添加什麽方法?事件接口是什麽?
1、我們需要點擊按鈕,獲取按鈕上的文字信息,顏色信息,粗細信息。那麽,此時事件源對象就是按鈕,監聽方法就是addActionListener(ActionListener e),事件接口就是ActionListener
2、要根據在窗體上按下和釋放的位置畫一個圖形,此時事件源對象就是窗體,監聽方法就是addMouseListener(MouseListener e),事件接口就是MouseListener
1public class DrawListener extends MouseAdapter implements ActionListener { 2 3 private Color color = Color.black;// 聲明顏色屬性,存儲用戶選擇的顏色 4 private int width = 1;// 聲明粗細屬性,存儲用戶選擇的粗細 5 private String type = "Line";// 聲明圖形屬性,存儲用戶選擇的圖形 6 private int x1, y1, x2, y2, x3 = 0, y3 = 0, x4 = 0, y4 = 0, x5, y5;//聲明坐標值屬性,存儲鼠標按下和釋放的坐標值 7 private Graphics2D g;// 聲明畫筆類屬性,組件是畫出來的,現在要在組件上畫圖形,Graphics從組件上獲取 8 private DrawMain dm;// 聲明畫圖程序窗體組件屬性 9 private JTextField text;// 獲取文本框內容 10 private double H = 100;// 等腰三角形的高度 11 private int num = 0; 12 private List<Shape> list; 13 private ImageIcon i = newImageIcon("C:\\Users\\long452a\\Desktop\\a1.jpg"); 14 15 /** 16 * 構造方法 17 * 18 * @param dm畫圖程序的窗體組件對象 19 */ 20 public DrawListener(DrawMain dm, JTextField text, List<Shape> list) { 21 this.dm = dm; 22 this.text = text; 23 this.list = list; 24 } 25 26 /** 27 * 點擊按鈕時執行的事件處理方法 28 * 29 * @param e對象中存儲了事件源對象的信息和動作信息 30 */ 31 public void actionPerformed(ActionEvent e) { 32 } 33 34 /** 35 * Invoked when the mouse button has been clicked (pressed and released) on a 36 * component. 37 */ 38 public void mouseClicked(MouseEvent e) { 39 } 40 41 /** 42 * Invoked when a mouse button has been pressed on a component. 43 */ 44 public void mousePressed(MouseEvent e) { 45 } 46 47 /** 48 * Invoked when a mouse button has been released on a component. 49 */ 50 public void mouseReleased(MouseEvent e) { 51 } 52 53 public void mouseDragged(MouseEvent e) { 54 } 55 }
這樣我們的類就建好了,下面就該寫裏面的方法了
1 /** 2 * 點擊按鈕時執行的事件處理方法 3 * 4 * @param e對象中存儲了事件源對象的信息和動作信息 5 */ 6 public void actionPerformed(ActionEvent e) { 7 String text = e.getActionCommand(); 8 if (text.equals("")) { 9 JButton button = (JButton) e.getSource(); 10 color = button.getBackground(); 11 } else if (text.equals("1") || text.equals("3") || text.equals("5")) { 12 width = Integer.parseInt(text); 13 } else { 14 type = text; 15 } 16 // System.out.println(color + ">>" + width + ">>" + type); 17 } 18 19 /** 20 * Invoked when the mouse button has been clicked (pressed and released) on a 21 * component. 22 */ 23 public void mouseClicked(MouseEvent e) { 24 x4 = x2; 25 y4 = y2; 26 } 27 28 /** 29 * Invoked when a mouse button has been pressed on a component. 30 */ 31 public void mousePressed(MouseEvent e) { 32 x1 = e.getX() + 7; 33 y1 = e.getY() + 183; 34 if (y1 < 183) 35 y1 = 183; 36 g = (Graphics2D) dm.getGraphics();// 從窗體上獲取畫筆對象 37 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 設置畫筆抗鋸齒 38 g.setColor(color);// 設置畫筆顏色 39 g.setStroke(new BasicStroke(width));// 設置畫筆線條粗細 40 41 } 42 43 /** 44 * Invoked when a mouse button has been released on a component. 45 */ 46 public void mouseReleased(MouseEvent e) { 47 x2 = e.getX() + 7; 48 y2 = e.getY() + 183; 49 if (y2 < 183) 50 y2 = 183; 51 if (type.equals("iso_Tri")) { 52 if (x1 == x2) { 53 g.drawLine(x1, y1, x2, y2); 54 g.drawLine(x1, y1, x1 + (int) H, (y2 + y1) / 2); 55 g.drawLine(x2, y2, x1 + (int) H, (y2 + y1) / 2); 56 list.add(new Shape(x1, y1, x2, y2, width, color, type, i, dm, text)); 57 list.add(new Shape(x1, y1, x1 + (int) H, (y2 + y1) / 2, width, color, type, i, dm, text)); 58 list.add(new Shape(x2, y2, x1 + (int) H, (y2 + y1) / 2, width, color, type, i, dm, text)); 59 } else if (y1 == y2) { 60 g.drawLine(x1, y1, x2, y2); 61 g.drawLine(x1, y1, (x1 + x2) / 2, y1 + (int) H); 62 g.drawLine(x2, y2, (x1 + x2) / 2, y1 + (int) H); 63 list.add(new Shape(x1, y1, x2, y2, width, color, type, i, dm, text)); 64 list.add(new Shape(x1, y1, x1 + (x1 + x2) / 2, y1 + (int) H, width, color, type, i, dm, text)); 65 list.add(new Shape(x2, y2, x1 + (x1 + x2) / 2, y1 + (int) H, width, color, type, i, dm, text)); 66 } else { 67 double a = Math.atan((double) (x2 - x1) / (double) (y2 - y1)); 68 double x3 = (double) (x1 + x2) / 2 + H * Math.cos(a); 69 double y3 = (double) (y1 + y2) / 2 - H * Math.sin(a); 70 g.drawLine(x1, y1, x2, y2); 71 g.drawLine(x1, y1, (int) x3, (int) y3); 72 g.drawLine(x2, y2, (int) x3, (int) y3); 73 list.add(new Shape(x1, y1, x2, y2, width, color, type, i, dm, text)); 74 list.add(new Shape(x1, y1, x1 + (int) x3, (int) y3, width, color, type, i, dm, text)); 75 list.add(new Shape(x2, y2, (int) x3, (int) y3, width, color, type, i, dm, text)); 76 } 77 } else if (type.equals("Polygon")) { 78 if (num == 0) { 79 g.drawLine(x1, y1, x2, y2); 80 list.add(new Shape(x1, y1, x2, y2, width, color, type, i, dm, text)); 81 x5 = x2; 82 y5 = y2; 83 } 84 num++; 85 if (num == 1) { 86 x3 = x1; 87 y3 = y1; 88 } 89 if (x2 == x4 && y2 == y4) { 90 g.drawLine(x1, y1, x3, y3); 91 list.add(new Shape(x1, y1, x3, y3, width, color, type, i, dm, text)); 92 num = 0; 93 } else { 94 g.drawLine(x2, y2, x5, y5); 95 list.add(new Shape(x2, y2, x5, y5, width, color, type, i, dm, text)); 96 x5 = x2; 97 y5 = y2; 98 } 99 } else { 100 Shape s = new Shape(x1, y1, x2, y2, width, color, type, i, dm, text); 101 s.draw(g); 102 list.add(s); 103 } 104 } 105 106 public void mouseDragged(MouseEvent e) { 107 if (type.equals("Pencil")) { 108 x2 = e.getX() + 7;//這裏的+7 +183 是調出來的,能夠使畫的圖是沿著鼠標 109 y2 = e.getY() + 183; 110 if (y2 < 183) 111 y2 = 183; 112 Shape s = new Shape(x1, y1, x2, y2, width, color, type, i, dm, text); 113 s.draw(g); 114 list.add(s); 115 x1 = x2; 116 y1 = y2; 117 } else if (type.equals("Erase")) { 118 x2 = e.getX() + 7; 119 y2 = e.getY() + 183; 120 if (y2 < 183) 121 y2 = 183; 122 Shape s = new Shape(x1, y1, x2, y2, width, Color.WHITE, type, i, dm, text); 123 s.draw(g); 124 list.add(s); 125 x1 = x2; 126 y1 = y2; 127 } else if (type.equals("噴槍")) // 難點 128 { 129 Random rand = new Random();// 實例化一個隨機數類的對象 130 int size = rand.nextInt(50);// 隨機決定要畫的點數 131 x2 = e.getX() + 7; 132 y2 = e.getY() + 183; 133 for (int j = 0; j < size; j++) { 134 // 在0-7之間可以取50次 135 int x = rand.nextInt(10); 136 int y = rand.nextInt(10); 137 // 不斷改變(x1,y1)的坐標值,實現在(x1,y1)的周圍畫點 138 Shape s = new Shape(x2 + x, y2 + y, x2 + x, y2 + y, width, color, type, i, dm, text); 139 s.draw(g); 140 list.add(s); 141 x1 = x2; 142 y1 = y2; 143 } 144 } 145 }
仔細看看代碼,你也許註意到了,我在畫圖時創建了一個Shape對象,並且還把Shape對象存到了一個List中,為什麽要這麽做?你們可以去別人的博客上貼一個粗略實現的畫圖板代碼,畫幾條直線然後再改變窗體的大小試試看,是不是畫的直線不見了?那要怎麽做才能使這些圖形保存下來呢?這就需要用到重繪了。
但是,我們需要知道,為什麽圖形會消失?其實計算機中的界面什麽都是畫出來的。自然組件也是畫出來的,而改變組件大小的時候,原來的組件無法滿足現在組件的顯示要求,所以組件要重新畫一次,然而此時組件上的圖形並不會重新再畫一次,為什麽?組件已經默認有重繪的方法了paint(Graphics g),所以我們需要重寫這個方法,但是我們怎麽能讓計算機知道之前畫了什麽呢?這是就需要把每次畫的內容記錄到隊列或數組中,重繪時就可以根據這個隊列或是數組進行了。而創建Shape對象自然是為了方便保存這些圖形的數據了。
下面是Shape的寫法
1 public class Shape { 2 3 private int x1, y1, x2, y2, width; 4 private Color color; 5 private String type; 6 private ImageIcon i; 7 private DrawMain dm; 8 private JTextField text; 9 10 public Shape(int x1, int y1, int x2, int y2, int width, Color color, String type, ImageIcon i, DrawMain dm, 11 JTextField text) { 12 this.x1 = x1; 13 this.y1 = y1; 14 this.x2 = x2; 15 this.y2 = y2; 16 this.width = width; 17 this.color = color; 18 this.type = type; 19 this.i = i; 20 this.dm = dm; 21 this.text = text; 22 } 23 24 25 public void draw(Graphics2D g) { 26 g.setColor(color); 27 g.setStroke(new BasicStroke(width)); 28 if (type.equals("Line") || type.equals("Pencil") || type.equals("iso_Tri") || type.equals("Polygon") 29 || type.equals("Erase") || type.equals("噴槍")) { 30 g.drawLine(x1, y1, x2, y2); 31 } else if (type.equals("Oval")) { 32 g.fillOval(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1)); 33 } else if (type.equals("Rect")) { 34 g.drawRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1)); 35 } else if (type.equals("RoundRect")) { 36 g.drawRoundRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1), 37 Math.abs(x2 - x1) / 3, Math.abs(y2 - y1) / 3); 38 } else if (type.equals("fill3DRect")) { 39 g.fill3DRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1), true); 40 } else if (type.equals("fillArc")) { 41 double r = ((x2 - x1) * (x2 - x1) + y1 * y1) / (2 * y1); 42 double m = Math.asin((double) (x2 - x1) / r); 43 g.fillArc(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), (int) r, 0, (int) (m * 180 / Math.PI)); 44 } else if (type.equals("Image")) { 45 g.drawImage(i.getImage(), Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1), dm); 46 } else if (type.equals("Text")) { 47 g.drawString(text.getText(), x1, y1); 48 } 49 } 50 51 }
現在還有一個問題,paint該寫在哪?寫在之前寫的DrawMain中
private List<Shape> list = new ArrayList<Shape>(); public void paint(Graphics gr) { super.paint(gr); Graphics2D g = (Graphics2D) gr; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); for (int i = 0; i < list.size(); i++) { Shape shape = list.get(i); shape.draw(g); } }
提醒一下:別忘了把事件監聽添加到組件中!!!!
這樣一來,我們的畫圖板就能完美使用了。當然這個畫圖板跟系統中的比起來還是差了很多了,比如撤銷、重做、保存等功能都還不具備,剩下的功能就需要後面慢慢研究再完善了。
JAVA 畫圖板實現(基本畫圖功能+界面UI)二、功能實現及重繪實現