Java入門:繪制簡單圖形


在上一節,我們學習了如何使用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坐標系:

*Java的坐標原點(0,0)位於 屏幕的左上角,坐標度量以象素為單位,水平向右為X軸的正方向,豎直向下為Y軸的正方向,每個坐標點的值表示屏幕上的一個像素點的位置,所有坐標點的值都取整數,如下圖所示。
4)當前顏色:指圖形上下文當前的顏色。每個圖形上下文都有自己當前的顏色。通過Graphics對象的getColor()方法可獲取改顏色,setColor()方法可設置顏色。

實踐訓練:繪制游戲區域

第一步:給DrawSee類添加成員變量,用來描述游戲區域的特征。
對DrawSee類來說,此類主要完成的功能是與用戶交互,即顯示游戲區域,顯示數字,響應用戶鼠標點擊,顯示用戶鼠標點擊后的結果等。我們現在考慮繪制10行10列游戲區域的問題。先給DrawSee類添加如下四個成員變量:
DrawSee.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()方法里面,也不會出現錯誤,程序仍能得到正確結果。

 

第二步:添加繪制游戲區域的方法(即繪制10行10列的紅色網格)
DrawSee.java文件
 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行,是添加窗口的鼠標點擊事件,鼠標每次點擊一下小方格,就要判斷所點擊的放個是否要被消掉。

至此,本項目任務繪圖部分的知識點就介紹到這里。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM