在上一节,我们学习了如何使用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坐标系:



屏幕的左上角,坐标度量以象素为单位,水平向右为X轴的正方向,竖直向下为Y轴的正方向,每个坐标点的值表示屏幕上的一个像素点的位置,所有坐标点的值都取整数,如下图所示。




java 矢量绘图工具 java使用graphics画图_java 矢量绘图工具


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列的区域了。

java 矢量绘图工具 java使用graphics画图_java 矢量绘图工具_02

注意,在窗口元素上绘制直线、写文字等操作,使用到的是一个叫做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行,是添加窗口的鼠标点击事件,鼠标每次点击一下小方格,就要判断所点击的放个是否要被消掉。

至此,本项目任务绘图部分的知识点就介绍到这里。