Windows的畫圖板相信很多人都用過,這次我們就來講講Java版本的簡易畫板的實現。 Windows的畫圖板相信很多人都用過,這次我們就來講講Java版本的簡易畫板的實現。 基本的思路是這樣的:畫板實現大致分三部分:一是畫板界面的實現,二是畫板的監聽以及畫圖的實現,三是畫板的重繪。(文章較長,但是 ...
Windows的畫圖板相信很多人都用過,這次我們就來講講Java版本的簡易畫板的實現。 基本的思路是這樣的:畫板實現大致分三部分:一是畫板界面的實現,二是畫板的監聽以及畫圖的實現,三是畫板的重繪。(文章較長,但是代碼是逐步遞進的,可以按三部分分開來看,實現了當前部分再去看下一部分。)首先是畫板的界面實現,因為我沒有去找具體的圖標,界面上的所有組件都是Swing的自帶組件,所以界面略微有點簡陋,不過如果想要優化也簡單,把界面上的組件都改成自定義的圖標即可。界面實現後,就可以考慮給界面的組件加上監聽,不同的圖形根據具體情況添加不同的監聽方法。然後編寫事件處理類依據不同的圖形編寫畫圖的具體演算法。一個簡易版本的畫圖板基本就差不多可以實現了。重繪這裡先不提放到後面再講。 先來看看畫圖界面的實現: 實現畫圖界面需要用的API類主要有:FlowLayout,GridLayout,Color,Dimension,JButton,JFrame,JPanel。 定義Draw類,讓Draw類繼承JFrame。設置它的大小,標題,可見性等。需要註意的是這裡如果添加的按鈕如果比較多,建議使用數組來完成按鈕的添加,因為如果直接一個一個的加按鈕,不僅會使得代碼量增大,而且不利於查找、添加和代碼的維護。為了使得界面不至於顯得那麼簡陋,這裡使用了幾個Jpanel,和不同的佈局管理器。主窗體使用的是流式佈局管理器,然後使用三個面板,分別承裝圖形按鈕,顏色按鈕和畫布。承裝圖形按鈕和顏色按鈕的面板都使用表格佈局。然後界面的實現就基本完成了。

package Cbs;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* Draw類,用於界面的初始化
* @author CBS
*/
@SuppressWarnings("serial")
public class Draw extends JFrame {
// 界面初始化方法
public void showUI() {
setTitle("畫圖");//窗體名稱
setSize(1200, 900);//窗體大小
setDefaultCloseOperation(3);
setLocationRelativeTo(null);//窗體居中
//流式佈局左對齊
FlowLayout layout = new FlowLayout(FlowLayout.LEFT);
setLayout(layout);//窗體使用流式佈局管理器
this.setResizable(false);//窗體大小不變
//使用數組保存按鈕名
String buttonName[] = { "畫直線", "畫橢圓", "畫曲線", "多邊形",
"橡皮擦", "拖動線","三角形", "畫球形", "筆刷", "噴槍",
"色子", "立體矩形", "立體圓", "立體三角","迭代分形",
"現代分形", "楓葉", "畫樹", "mandelbrot集", "L-System",
"迭代畫線","迭代三角形", "謝爾賓斯基地毯", "畫字元", "清空",
"吸管" ,"矩形","五角星","多線","字元"};
//用於保存圖形按鈕,使用網格佈局
JPanel jp1=new JPanel(new GridLayout(15, 2,10,10));
jp1.setPreferredSize(new Dimension(200, 800));
//迴圈為按鈕面板添加按鈕
for (int i = 0; i < buttonName.length; i++) {
JButton jbutton = new JButton(buttonName[i]);
jp1.add(jbutton);
}
JPanel jp2=new JPanel();//畫布面板
jp2.setPreferredSize(new Dimension(970, 800));
jp2.setBackground(Color.WHITE);
// 定義Color數組,用來存儲按鈕上要顯示的顏色信息
Color[] colorArray = { Color.BLUE, Color.GREEN, Color.RED,
Color.BLACK,Color.ORANGE,Color.PINK,Color.CYAN,
Color.MAGENTA,Color.DARK_GRAY,Color.GRAY,
Color.LIGHT_GRAY,Color.YELLOW};
//用於保存顏色按鈕的面板
JPanel jp3=newJPanel(newGridLayout(1,colorArray.length,3,3));
// 迴圈遍歷colorArray數組,根據數組中的元素來實例化按鈕對象
for (int i = 0; i < colorArray.length; i++) {
JButton button = new JButton();
button.setBackground(colorArray[i]);
button.setPreferredSize(new Dimension(30, 30));
jp3.add(button);
}
//將面板添加到主窗體
this.add(jp1);
this.add(jp2);
this.add(jp3);
//添加按鈕,作為當前顏色
JButton nowColor=new JButton();
nowColor.setPreferredSize(new Dimension(40,40));
nowColor.setBackground(Color.BLACK);//預設黑色
add(nowColor);
//設置窗體的組件可見,如果為FALSE就看不到任何組件
setVisible(true);
}
}
View Code
這裡還要一點要註意的地方,Jpanel面板的添加先後順序不要改變,這是根據流式佈局算出來的面板大小,讀者可以自行更改調試。還有一個就是窗體的setSize方法只對窗體本身有效,如果要改變其他組件的大小要用setPreferredSize方法。這樣畫圖板的基本界面就實現。界面的按鈕和麵板可以根據自身需要更改。
這是界面的大概樣子:


package Cbs;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
public class DrawListener implements ActionListener, MouseListener,
MouseMotionListener {
private Color color;//顏色屬性
private Graphics g;//畫筆屬性
private String str;//保存按鈕上的字元串,區分不同的按鈕
private int x1,y1,x2,y2;//(x1,y1),(x2,y2)分別為滑鼠的按下和釋放時的坐標
private JButton nowColor;//當前顏色按鈕
//獲取Draw類的畫筆對象
public void setG(Graphics g) {
this.g = g;
}
//獲取當前顏色按鈕
public void setNowColor(JButton nowColor) {
this.nowColor = nowColor;
}
@Override
//滑鼠拖動的方法
public void mouseDragged(MouseEvent e) {
//畫曲線的方法
if ("畫曲線".equals(str)) {
int x, y;
x = e.getX();
y = e.getY();
g.drawLine(x, y, x1, y1);
x1 = x;
y1 = y;
}
}
@Override
//滑鼠移動方法
public void mouseMoved(MouseEvent e) {
}
@Override
//滑鼠單擊方法
public void mouseClicked(MouseEvent e) {
}
@Override
//滑鼠按下方法
public void mousePressed(MouseEvent e) {
g.setColor(color);//改變畫筆的顏色
x1=e.getX();//獲取按下時滑鼠的x坐標
y1=e.getY();//獲取按下時滑鼠的y坐標
}
@Override
//滑鼠釋放方法
public void mouseReleased(MouseEvent e) {
x2=e.getX();//獲取釋放時滑鼠的x坐標
y2=e.getY();//獲取釋放時滑鼠的y坐標
//畫直線的方法
if ("畫直線".equals(str)) {
g.drawLine(x1, y1, x2, y2);
}
}
@Override
//滑鼠進入方法
public void mouseEntered(MouseEvent e) {
}
@Override
//滑鼠退出方法
public void mouseExited(MouseEvent e) {
}
@Override
//處理按鈕上的滑鼠點擊動作
public void actionPerformed(ActionEvent e) {
//判斷是顏色按鈕還是圖形按鈕
if ("".equals(e.getActionCommand())) {
JButton jb = (JButton) e.getSource();
color = jb.getBackground();
nowColor.setBackground(color);//處理當前顏色
} else {
str = e.getActionCommand();
}
}
}
View Code
Draw類也要做一些修改,為按鈕和麵板添加監聽:

1 package Cbs;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.FlowLayout;
6 import java.awt.Graphics;
7 import java.awt.GridLayout;
8
9 import javax.swing.JButton;
10 import javax.swing.JFrame;
11 import javax.swing.JPanel;
12
13 /**
14 * Draw類,用於界面的初始化
15 * @author CBS
16 */
17 @SuppressWarnings("serial")
18 public class Draw extends JFrame {
19 private DrawListener dl;
20 private Graphics g;
21 // 界面初始化方法
22 public void showUI() {
23 setTitle("畫圖");//窗體名稱
24 setSize(1200, 900);//窗體大小
25 setDefaultCloseOperation(3);
26 setLocationRelativeTo(null);//窗體居中
27 //流式佈局左對齊
28 FlowLayout layout = new FlowLayout(FlowLayout.LEFT);
29 setLayout(layout);//窗體使用流式佈局管理器
30 this.setResizable(false);//窗體大小不變
31
32 //使用數組保存按鈕名
33 String buttonName[] = { "畫直線", "畫橢圓", "畫曲線", "多邊形",
34 "橡皮擦", "拖動線","三角形", "畫球形", "筆刷", "噴槍",
35 "色子", "立體矩形", "立體圓", "立體三角","迭代分形",
36 "現代分形", "楓葉", "畫樹", "mandelbrot集", "L-System",
37 "迭代畫線","迭代三角形", "謝爾賓斯基地毯", "畫字元", "清空",
38 "吸管" ,"矩形","五角星","多線","字元"};
39 //用於保存圖形按鈕,使用網格佈局
40 JPanel jp1=new JPanel(new GridLayout(15, 2,10,10));
41 jp1.setPreferredSize(new Dimension(200, 800));
42
43 //實例化DrawListener對象
44 dl=new DrawListener();
45 //迴圈為按鈕面板添加按鈕
46 for (int i = 0; i < buttonName.length; i++) {
47 JButton jbutton = new JButton(buttonName[i]);
48 jbutton.addActionListener(dl);//為按鈕添加監聽
49 jp1.add(jbutton);
50 }
51
52 JPanel jp2=new JPanel();//畫布面板
53 jp2.setPreferredSize(new Dimension(970, 800));
54 jp2.setBackground(Color.WHITE);
55
56
57 // 定義Color數組,用來存儲按鈕上要顯示的顏色信息
58 Color[] colorArray = { Color.BLUE, Color.GREEN,
59 Color.RED, Color.BLACK,Color.ORANGE,Color.PINK,Color.CYAN,
60 Color.MAGENTA,Color.DARK_GRAY,Color.GRAY,Color.LIGHT_GRAY,
61 Color.YELLOW};
62 //用於保存顏色按鈕的面板
63 JPanel jp3=new JPanel(new GridLayout(1,colorArray.length,3,3));
64 // 迴圈遍歷colorArray數組,根據數組中的元素來實例化按鈕對象
65 for (int i = 0; i < colorArray.length; i++) {
66 JButton button = new JButton();
67 button.setBackground(colorArray[i]);
68 button.setPreferredSize(new Dimension(30, 30));
69 button.addActionListener(dl);//為按鈕添加監聽
70 jp3.add(button);
71 }
72 //將面板添加到主窗體
73 this.add(jp1);
74 this.add(jp2);
75 this.add(jp3);
76 //添加按鈕,作為當前顏色
77 JButton nowColor=new JButton();
78 nowColor.setPreferredSize(new Dimension(40,40));
79 nowColor.setBackground(Color.BLACK);//預設黑色
80 add(nowColor);
81 //設置窗體的組件可見,如果為FALSE就看不到任何組件
82 setVisible(true);
83 //獲取畫筆對象
84 g=jp2.getGraphics();
85 dl.setG(g);
86 dl.setNowColor(nowColor);
87 //為面板添加滑鼠監聽,用於繪製圖形
88 jp2.addMouseListener(dl);
89 jp2.addMouseMotionListener(dl);
90 }
91
92 }
View Code
drawDrawListener裡面只寫了畫直線和曲線的方法,讀者可以根據自己的需求添加,思路和方式都是一樣的。Draw類裡面有些需要註意的地方在這裡提一下:一個是畫筆g的獲取一定要在窗體的可見之後採取獲取,不然獲取的畫筆對象返回值會是null。二是要為圖形按鈕添加監聽,DrawListener的實例化需要在setVisible方法之前,所以不建議使用構造方法直接傳入g畫筆參數,我使用的是set方法。最後是註意一下使用哪個添加方法,按鈕使用的是addActionListener方法,畫板面板使用的是addMouseListener和addMouseMotionListener方法。使用畫板面板來獲取畫筆並給畫面面板添加監聽是為了讓繪圖的時候圖形不會跑出面板外,這裡的畫筆和監聽都由主窗體獲得也是可以的,不過繪製時會出現線畫出面板的問題。
畫板的重繪:
到這裡畫板的製作已經基本實現了,我們已經可以在上面繪製各種各樣的圖形了。但是細心的人可能會發現一個問題,那就是如果把窗體最小化之後再次打開,畫板上原本已經畫好的東西會全部都消失了。這樣子肯定是不行的,辛辛苦苦畫的“大作”怎麼能說說沒就沒了呢。那麼為什麼會出現這樣的問題呢?要回答這個問題我們就需要先瞭解Java的繪圖機制。做畫圖板我們使用的是Swing組件,這套組件是基於原先的AWT組件開發,在繪製的時候會調用系統的畫圖函數,這就是為什麼我們可以從面板或者是窗體中獲取畫筆對象的原因。這也就是說Java中你所能夠看到窗體,按鈕或者其它的所有組件其實都是畫出來。所以當我們點擊窗體使它最小化或者改變大小的時候,原來的畫的窗體就不能適應需要了,這時系統會調用JFrame的paint方法實現窗體的重繪,也就是再次畫了一個新的窗體,而JFrame的paint方法只對窗體已經添加的組件有效,我們自己繪製的東西並沒有寫在paint方法裡面,所以窗體重繪之後,我們原先繪製的圖形也就消失了。要解決這個問題我們需要重寫父類的paint方法。但是這樣的話問題又來了,畫圖是在DrawListener類裡面實現的,要怎麼把它們弄到paint方法裡面去呢?
當然方法可能有很多,這裡我只介紹我所知道的:要把畫出來的圖形在paint方法中再次畫出來,就需要有東西來保存畫過的圖形。保存可以使用數組或者集合,這裡推薦使用集合,可以很方便的實現添加,而不需要去考慮大小的問題。數組的實現也大同小異,這裡就不多做介紹。確定了使用集合,那麼集合內保存什麼類型的數據呢?毫無疑問應該保存的是圖形的數據,但是集合使用泛型的話也只能保存同一種類型的數據,我們卻有那麼多種圖形?這裡就可以使用介面或者抽象類,我們只需要創建不同得圖形類,讓它繼承抽象類或者是實現介面。然後每畫一個圖形就實例化一個圖形的對象,存入集合中,最後在paint方法中遍歷集合,重新繪製圖形即可。
下麵直接貼最終代碼(仍然只寫了直線和曲線),只是添加了幾行代碼,註意與前面比較。

//圖形介面 package Cbs; //圖形集合 public interface NetJavaShape { public abstract void draw(); } //直線類 package Cbs; import java.awt.Color; import java.awt.Graphics; import Cbs.NetJavaShape; public class ImpLine implements NetJavaShape{ Graphics g; int x1, y1,x2, y2; Color c; public ImpLine(Graphics g,int x1,int y1,int x2,int y2,Color c){ this.g=g; this.c=c; this.x1=x1; this.y1=y1; this.x2=x2; this.y2=y2; } public void draw() { g.setColor(c); g.drawLine(x1, y1, x2, y2); } } //DrawListener類 package Cbs; import java.awt.Color; import java.awt.Graphics; import java.util.List; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import Cbs.NetJavaShape; import javax.swing.JButton; public class DrawListener implements ActionListener, MouseListener, MouseMotionListener { private Color color=Color.BLACK;//顏色屬性,初始值為黑色 private Graphics g;//畫筆屬性 private String str;//保存按鈕上的字元串,區分不同的按鈕 private int x1,y1,x2,y2;//(x1,y1),(x2,y2)分別為滑鼠的按下和釋放時的坐標 private JButton nowColor;//當前顏色按鈕 //保存圖形對象的集合 private List<NetJavaShape> shapesArray = new ArrayList<NetJavaShape>(); //圖形 private NetJavaShape shape; //在draw類中獲取集合 public List<NetJavaShape> getShapesArray() { return shapesArray; } //獲取Draw類的畫筆對象 public void setG(Graphics g) { this.g = g; } //獲取當前顏色按鈕 public void setNowColor(JButton nowColor) { this.nowColor = nowColor; } @Override //滑鼠拖動的方法 public void mouseDragged(MouseEvent e) { //畫曲線的方法 if ("畫曲線".equals(str)) { int x, y; x = e.getX(); y = e.getY(); //實例化對象,曲線也是直線畫的所以不同新建一個曲線類了 shape=new ImpLine(g,x,y,x1,y1,color); //調用畫圖方法 shape.draw(); //將圖形存入集合中 shapesArray.add(shape); // g.drawLine(x, y, x1, y1); x1 = x; y1 = y; } } @Override //滑鼠移動方法 public void mouseMoved(MouseEvent e) { } @Override //滑鼠單擊方法 public void mouseClicked(MouseEvent e) { } @Override //滑鼠按下方法 public void mousePressed(MouseEvent e) { g.setColor(color);//改變畫筆的顏色 x1=e.getX();//獲取按下時滑鼠的x坐標 y1=e.getY();//獲取按下時滑鼠的y坐標 } @Override //滑鼠釋放方法 public void mouseReleased(MouseEvent e) { x2=e.getX();//獲取釋放時滑鼠的x坐標 y2=e.getY();//獲取釋放時滑鼠的y坐標 //畫直線的方法 if ("畫直線".equals(str)) { //實例化對象, shape=new ImpLine(g,x1,y1,x2,y2,color); //調用畫圖方法 shape.draw(); //將圖形存入集合中 shapesArray.add(shape); // g.drawLine(x1, y1, x2, y2); } } @Override //滑鼠進入方法 public void mouseEntered(MouseEvent e) { } @Override //滑鼠退出方法 public void mouseExited(MouseEvent e) { } @Override //處理按鈕上的滑鼠點擊動作 public void actionPerformed(ActionEvent e) { if ("".equals(e.getActionCommand())) { JButton jb = (JButton) e.getSource(); color = jb.getBackground(); nowColor.setBackground(color);//處理當前顏色 } else { str = e.getActionCommand(); } } } //draw類 package Cbs; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.GridLayout; import java.util.ArrayList; import java.util.List; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; /** * Draw類,用於界面的初始化 * @author CBS */ @SuppressWarnings("serial") public class Draw extends JFrame { private DrawListener dl; private Graphics g; //保存圖形對象的集合 private List<NetJavaShape> shapesArray = new ArrayList<NetJavaShape>(); // 界面初始化方法 public void showUI() { setTitle("畫圖");//窗體名稱 setSize(1200, 900);//窗體大小 setDefaultCloseOperation(3); setLocationRelativeTo(null);//窗體居中 FlowLayout layout = new FlowLayout(FlowLayout.LEFT);//流式佈局左對齊 setLayout(layout);//窗體使用流式佈局管理器 this.setResizable(false);//窗體大小不變 //使用數組保存按鈕名 String buttonName[] = { "畫直線", "畫橢圓", "畫曲線", "多邊形", "橡皮擦", "拖動線","三角形", "畫球形", "筆刷", "噴槍", "色子", "立體矩形", "立體圓", "立體三角","迭代分形", "現代分形", "楓葉", "畫樹", "mandelbrot集", "L-System", "迭代畫線","迭代三角形", "謝爾賓斯基地毯", "畫字元", "清空","吸管" ,"矩形","五角星","多線","字元"}; JPanel jp1=new JPanel(new GridLayout(15, 2,10,10));//用於保存圖形按鈕,使用網格佈局 jp1.setPreferredSize(new Dimension(200, 800)); //實例化DrawListener對象 dl=new DrawListener(); //迴圈為按鈕面板添加按鈕 for (int i = 0; i < buttonName.length; i++) { JButton jbutton = new JButton(buttonName[i]); jbutton.addActionListener(dl);//為按鈕添加監聽 jp1.add(jbutton); } JPanel jp2=new JPanel();//畫布面板 jp2.setPreferredSize(new Dimension(970, 800)); jp2.setBackground(Color.WHITE); // 定義Color數組,用來存儲按鈕上要顯示的顏色信息 Color[] colorArray = { Color.BLUE, Color.GREEN, Color.RED, Color.BLACK,Color.ORANGE,Color.PINK,Color.CYAN,Color.MAGENTA,Color.DARK_GRAY,Color.GRAY,Color.LIGHT_GRAY,Color.YELLOW}; //用於保存顏色按鈕的面板 JPanel jp3=new JPanel(new GridLayout(1,colorArray.length,3,3)); // 迴圈遍歷colorArray數組,根據數組中的元素來實例化按鈕對象 for (int i = 0; i < colorArray.length; i++) { JButton button = new JButton(); button.setBackground(colorArray[i]); button.setPreferredSize(new Dimension(30, 30)); button.addActionListener(dl);//為按鈕添加監聽 jp3.add(button); } //將面板添加到主窗體 this.add(jp1); this.add(jp2); this.add(jp3); //添加按鈕,作為當前顏色 JButton nowColor=new JButton(); nowColor.setPreferredSize(new Dimension(40,40)); nowColor.setBackground(Color.BLACK);//預設黑色 add(nowColor); //設置窗體的組件可見,如果為FALSE就看不到任何組件 setVisible(true); //獲取畫筆對象 g=