在上一節,我們學習了如何使用swing和awt工具創建一個空的窗口,本節學習如何繪制簡單圖形。
基本繪圖介紹
Java中繪制基本圖形,可以使用Java類庫中的Graphics類,此類位於java.awt包中。在我們自己的java程序文件中,要使用Graphics類就需要使用import java.awt.Graphics語句將Graphics類導入進來。
Graphics類提供基本的幾何圖形繪制方法,主要有:畫線段、畫矩形、畫圓、畫帶顏色的圖形、畫橢圓、畫圓弧、畫多邊形等。本項目僅用到畫直線的功能,其它圖形繪制請自行點擊查閱Java API。
Graphics類的drawLine()方法:drawLine(int x1,int y1,int x2,int y2)
此方法的功能是:在此圖形上下文的坐標系中,使用當前顏色在點 (x1,y1)
和 (x2,y2)
之間畫一條線。
這里需要理解幾個概念:
1)圖形上下文:通俗點講,就是畫圖環境。每個窗口構件(如主窗口、按鈕等),都有一個自己的圖形上下文對象,我們就是使用這個對象來實現在構件上畫圖。這個對象就是Graphics對象。
2)如何獲得圖形上下文:我們要在哪個構件上繪圖,就調用那個構件的getGraphics()方法即可獲得該構件的圖形上下文對象,然后使用這個對象繪圖。
3)Java坐標系:


實踐訓練:繪制游戲區域
import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.Graphics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JFrame; public class DrawSee extends JFrame { private static final int sx = 50;//游戲區域10*10方塊的起始橫坐標 private static final int sy = 50;//游戲區域10*10方塊的起始縱坐標 private static final int w = 40;//每個小方格的邊長 private static final int rw = 400;//游戲區域10*10方塊的邊長 ... }
之所以把這些成員作為DrawSee類的成員變量,而沒有作為某個成員方法的局部變量,這是因為這幾個變量所描述的是DrawSee這個窗口的特征,把他們作為類成員變量更符合邏輯。但是,這幾個變量只在下面第二步描述的方法中用過,其它方法中並沒有使用,所以即使你將這介個變量定義在paintComponent()方法里面,也不會出現錯誤,程序仍能得到正確結果。
1 public void paintComponents(Graphics g) { 2 try { 3 4 // 設置線條顏色為紅色 5 g.setColor(Color.RED); 6 7 // 繪制外層矩形框 8 g.drawRect(sx, sy, rw, rw); 9 10 /* 繪制水平10個,垂直10個方格。 11 * 即水平方向9條線,垂直方向9條線, 12 * 外圍四周4條線已經畫過了,不需要再畫。 13 * 同時內部64個方格填寫數字。 14 */ 15 for(int i = 1; i < 10; i ++) { 16 // 繪制第i條豎直線 17 g.drawLine(sx + (i * w), sy, sx + (i * w), sy + rw); 18 19 // 繪制第i條水平線 20 g.drawLine(sx, sy + (i * w), sx + rw, sy + (i * w)); 21 22 23 } 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } 27 }
為簡單起見,我們在DrawSee類的構造方法中直接調用 paintComponents方法,讓其繪制10行10列的游戲網格。(重載paint方法,在其中繪制才是合理的選擇)這里直接調用paintComponents會看不到繪圖結果,需要在調用paintComponents之前插入一段代碼,讓進程等待500毫秒。代碼見第48至61行:
DrawSee.java文件
1 import java.awt.Color; 2 import java.awt.Container; 3 import java.awt.Font; 4 import java.awt.Graphics; 5 import java.awt.event.MouseAdapter; 6 import java.awt.event.MouseEvent; 7 8 import javax.swing.JFrame; 9 10 11 /** 12 * 13 * 程序入口 14 * 15 */ 16 public class TestDrawLine { 17 public static void main(String[] args) { 18 new DrawSee(); 19 } 20 } 21 22 class DrawSee extends JFrame { 23 24 private static final int sx = 50;//小方格寬度 25 private static final int sy = 50;//小方格高度 26 private static final int w = 40; 27 private static final int rw = 400; 28 29 30 private Graphics jg; 31 32 33 34 private Color rectColor = new Color(0xf5f5f5); 35 36 /** 37 * DrawSee構造方法 38 */ 39 public DrawSee() { 40 Container p = getContentPane(); 41 setBounds(100, 100, 500, 500); 42 setVisible(true); 43 p.setBackground(rectColor); 44 setLayout(null); 45 setResizable(false); 46 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 47 48 try { 49 50 51 52 Thread.sleep(500); 53 } catch (Exception e) { 54 e.printStackTrace(); 55 } 56 57 // 獲取專門用於在窗口界面上繪圖的對象 58 jg = this.getGraphics(); 59 60 // 繪制游戲區域 61 paintComponents(jg); 62 63 64 } 65 66 67 68 public void paintComponents(Graphics g) { 69 try { 70 71 // 設置線條顏色為紅色 72 g.setColor(Color.RED); 73 74 // 繪制外層矩形框 75 g.drawRect(sx, sy, rw, rw); 76 77 /* 繪制水平10個,垂直10個方格。 78 * 即水平方向9條線,垂直方向9條線, 79 * 外圍四周4條線已經畫過了,不需要再畫。 80 * 同時內部64個方格填寫數字。 81 */ 82 for(int i = 1; i < 10; i ++) { 83 // 繪制第i條豎直線 84 g.drawLine(sx + (i * w), sy, sx + (i * w), sy + rw); 85 86 // 繪制第i條水平線 87 g.drawLine(sx, sy + (i * w), sx + rw, sy + (i * w)); 88 89 // 填寫第i行從第1個方格到第8個方格里面的數字(方格序號從0開始) 90 for(int j = 0; j < 10; j ++) { 91 //drawString(g, j, i); 92 } 93 } 94 } catch (Exception e) { 95 e.printStackTrace(); 96 } 97 } 98 99 100 }
現在運行程序,可以看到已經繪制出10行10列的區域了。
注意,在窗口元素上繪制直線、寫文字等操作,使用到的是一個叫做Graphics的對象。在構造函數的第58行語句中,this.getGraphics()語句是獲取游戲窗口的繪圖對象(一個Graphics對象),這里的this是指程序運行后用戶看到的那個窗口,getGraphics()方法就是得到繪圖對象。獲取到這個對象后,被保存到成員變量jg中,這樣,在其他成員方法中就可以直接使用這個jg對象來繪圖了。
DrawSee類另外幾個成員:
setGrid(int cx,int cy,Color color):設置被選中的cx行,cy列網格的背景
compare(int cx,int cy):比較cx行cy列網格和之前選中的網格是否可以消除
drawString(Graphics g, int x, int y):在x行y列網格上寫數字
isEnd(int[][] map) :判斷二維數組map是不是全0
上敘方法請讀者自己分析代碼,在分析時,主要抓住兩個角度,一個是這個方法的邏輯過程是怎樣的,第二個是方法體內用到了哪些語法知識。
下面給出DrawSee類的完整代碼:
1 import java.awt.Color; 2 import java.awt.Container; 3 import java.awt.Font; 4 import java.awt.Graphics; 5 import java.awt.event.MouseAdapter; 6 import java.awt.event.MouseEvent; 7 8 import javax.swing.JFrame; 9 10 11 /** 12 * 13 * 程序入口 14 * 15 */ 16 public class TestDrawLine { 17 public static void main(String[] args) { 18 new DrawSee(); 19 } 20 } 21 22 class DrawSee extends JFrame { 23 private static final long serialVersionUID = 2L; 24 private static final int sx = 50;//小方格寬度 25 private static final int sy = 50;//小方格高度 26 private static final int w = 40; 27 private static final int rw = 400; 28 private int px = 0, py = 0; 29 30 private Graphics jg; 31 private int cc = 0;// 被選中的方格個數 32 private int[][] map;// 存放游戲數據的二維數組 33 private boolean isEnd = false; 34 private Color rectColor = new Color(0xf5f5f5); 35 36 /** 37 * DrawSee構造方法 38 */ 39 public DrawSee() { 40 Container p = getContentPane(); 41 setBounds(100, 100, 500, 500); 42 setVisible(true); 43 p.setBackground(rectColor); 44 setLayout(null); 45 setResizable(false); 46 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 47 48 try { 49 // 創建游戲數據地圖 50 map = SeeAgain.createMap(); 51 52 Thread.sleep(500); 53 } catch (Exception e) { 54 e.printStackTrace(); 55 } 56 57 // 獲取專門用於在窗口界面上繪圖的對象 58 jg = this.getGraphics(); 59 60 // 繪制游戲區域 61 paintComponents(jg); 62 63 64 // 添加鼠監聽事件,當鼠標點擊時觸發 65 this.addMouseListener(new MouseAdapter() { 66 67 // 定義鼠標點擊事件響應過程 68 @Override 69 public void mouseClicked(MouseEvent e) { 70 // 如果游戲結束,返回,不執行后面的代碼 71 if(isEnd) { 72 return; 73 } 74 75 //獲取鼠標點擊的那一點的x,y坐標 76 int x = e.getX(), y = e.getY(); 77 78 /** 79 * 計算當前點擊的方格是第幾個 80 * cx:當前點擊的方格處於水平方向第幾個 81 * cy: 當前點擊的方格處於豎直方向第幾個 82 */ 83 int cx = (x - sx) / w, cy = (y - sy) / w; 84 85 /** 86 * 如果點擊的方格處於游戲區域之外, 87 * 直接返回,不執行后面的代碼 88 */ 89 if(cx < 1 || cy < 1 || cx > 8 || cy > 8) { 90 return; 91 } 92 93 // 被選中的方格個數增加一個 94 cc ++; 95 96 compare(cx,cy); 97 98 } 99 }); 100 101 } 102 103 /** 104 * 判斷二維數組map中的所有元素是否均為0, 105 * 只要有一個不為0,返回false,表示游戲還沒結束;否則返回true表示游戲結束 106 * @param map 二維數組,元素為int類型 107 * @return 108 */ 109 public boolean isEnd(int[][] map) { 110 for(int[] ms : map) { 111 for(int m : ms) { 112 if(m != 0) { 113 return false; 114 } 115 } 116 } 117 return true; 118 } 119 120 121 public void paintComponents(Graphics g) { 122 try { 123 124 // 設置線條顏色為紅色 125 g.setColor(Color.RED); 126 127 // 繪制外層矩形框 128 g.drawRect(sx, sy, rw, rw); 129 130 /* 繪制水平10個,垂直10個方格。 131 * 即水平方向9條線,垂直方向9條線, 132 * 外圍四周4條線已經畫過了,不需要再畫。 133 * 同時內部64個方格填寫數字。 134 */ 135 for(int i = 1; i < 10; i ++) { 136 // 繪制第i條豎直線 137 g.drawLine(sx + (i * w), sy, sx + (i * w), sy + rw); 138 139 // 繪制第i條水平線 140 g.drawLine(sx, sy + (i * w), sx + rw, sy + (i * w)); 141 142 // 填寫第i行從第1個方格到第8個方格里面的數字(方格序號從0開始) 143 for(int j = 0; j < 10; j ++) { 144 drawString(g, j, i); 145 } 146 } 147 } catch (Exception e) { 148 e.printStackTrace(); 149 } 150 } 151 152 private void drawString(Graphics g, int x, int y) { 153 // 為0就不顯示 154 if(map[x][y] != 0) { 155 g.setColor(Color.RED);// Graphics對象顏色在之前又被修改過,所以每次需要將顏色重新設置為紅色 156 g.setFont(new Font("Arial", 0, 40));// 設置Graphics對象字體大小 157 g.drawString(map[x][y] + "", sx + (y * w) + 5, sy + ((x + 1) * w) - 5);// 繪制字符 158 } 159 } 160 161 /*** 162 * 講制定小方格設置為指定背景顏色 163 * @param cx 方格的水平方向序號 164 * @param cy 方格的垂直方向序號 165 * @param color 166 */ 167 private void setGrid(int cx,int cy,Color color){ 168 // 將繪圖對象設置為灰色,后面會將所點擊的方格背景設置為此顏色 169 jg.setColor(color); 170 171 /** 172 * 將所點擊的方格上繪制一個小一點的矩形,矩形背景顏色為color, 173 * 繪制的這個Rect會導致該方格上原有的文字被覆蓋 174 */ 175 jg.fillRect(sx + (cx * w) + 1, sy + (cy * w) + 1, w - 2, w - 2); 176 177 // 將覆蓋的數字重新寫出來,這樣就又看到紅色的文字了。 178 drawString(jg, cy, cx); 179 } 180 181 private void compare(int cx,int cy){ 182 /** 183 * 如果cc是1,表示當前一共選中了一個方格,用px,py來記住這個方格的位置; 184 * 否則,表示現在選中的這個方格要與之前選中的方案比較,決定是否要刪除 185 */ 186 if(cc == 1) { 187 px = cx; 188 py = cy; 189 190 // 將所點擊的方格背景設置為灰色 191 setGrid(cx,cy,Color.LIGHT_GRAY); 192 } 193 else{//此時,cc肯定是大於1的,表示要比較兩個方格的值是否相同 194 SeeAgain.removed(map, py, px, cy, cx );// 讓SeeAgain類的remove方法去判斷上一次所選 195 //的(px,py)處的方格值與本次選擇的(cx,cy)處的方格值是否可以消掉 196 197 // 處理第一個方格 198 setGrid(cx,cy,rectColor); 199 200 // 處理第二個方格 201 setGrid(px,py,rectColor); 202 203 cc = 0;//將cc的值復位 204 } 205 206 // 判斷是否結束游戲 207 isEnd = isEnd(map); 208 if(isEnd) { 209 jg.setColor(Color.RED); 210 jg.setFont(new Font("Arial", 0, 62)); 211 jg.drawString("Game Over!", 100, 220); 212 } 213 } 214 }
上述代碼第65行至第99行,是添加窗口的鼠標點擊事件,鼠標每次點擊一下小方格,就要判斷所點擊的放個是否要被消掉。
至此,本項目任務繪圖部分的知識點就介紹到這里。