• 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行,是添加窗口的鼠标点击事件,鼠标每次点击一下小方格,就要判断所点击的放个是否要被消掉。

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

  • 相关阅读:
    HDOJ 1284 钱币兑换问题
    WA : csu1019 simple line editor
    HDOJ1232 并查集
    最长回文子串
    Where's Waldorf?
    csu 1148 词典
    csu 1011 Counting Pixels
    Product:java高精度乘法
    内置类型开方
    csu 1019 Simple Line Editor
  • 原文地址:https://www.cnblogs.com/bayes/p/5505640.html
Copyright © 2020-2023  润新知