很多 GUI 程序都需要在组件上绘制图形,比如实现一个五子棋的小游戏,就需要在组件上绘制棋盘和棋子。在 java.awt 包中专门提供了一个 Graphics 类,它相当于一个抽象的画笔,其中提供了各种绘制图形的方法,使用 Graphics 类的方法就可以完成在组件上绘制图形。下表列出了 Graphics 类中常用的方法。

    方法声明 方法描述
    void setColor(Color c) 将此图形上下文的当前颜色设置为指定颜色
    void setFont(Font t) 将此图形上下文的字体设置为指定字体
    void drawLine(int x1, int y1, int x2, int y2) 以(x1, y1)和(x2 ,y2)为端点绘制一条线段
    void drawRect(int x, int y, int width, int height) 绘制指定矩形的边框。矩形的左边缘和右边缘分别位于 x 和 x + width。上边缘和下边缘分别位于 y 和 y + height
    void drawOval(int x, int y, int width, int height) 绘制椭圆的边框得到一个圆或椭圆,它**刚好能放入由 x、y、width 和 height 参数指定矩形**中。椭圆覆盖区域的宽度为 width + 1 像素,高度为 height +1 像素
    void fillRect(int x, int y, int witdth, int height) 用当前颜色于填充指定的矩形。该矩形左边缘和右边缘分别位于 x 和 x + width - 1。上边缘和下边缘分别位于 y 和 y + height - 1
    void fillOval(int x, int y, int width, int height) 用当前颜色填充外界指定矩形框的椭圆
    void drawString(String str, int x, int y) 使用此图形上下文的当前字体和颜色绘制指定的文本 str。最左侧字符左下角位于(x, y)坐标

    上表中列出了 Graphics 的常用方法,为了更好地理解和使用它们,下面对这些方法进行详细的说明。

    ① setColor()方法
    用于指定上下文颜色,方法中接收一个 Color 类型的参数。在 AWT 中,Color 类代表颜色,其中定义了许多代表各种颜色的常量,例如 Color.RED,Color.BULE 等,这些常量都是 Color 类型的,可以直接作为参数传递给 setColor()方法。

    ② setFont()方法
    用于指定上下文字体,方法中接收一个 Font 类型的参数。Font 类表示字体,可以使用 new 关键字创建 Font 对象Font 的构造方法中接收 3 个参数,第一个是 String 类型,表示字体名称,如“宋体”“微软雅黑”等;第二个是参数是 int 类型,表示字体的类型,参数接收 Font 类的三个常量 Font.PLANT、Font.ITALC Font.BOLD;第三个参数为 int 类型,表示字体的大小。

    ③ drawRect()方法和 drawOval()方法
    用于绘制矩形和椭圆形的边框

    ④ fillRect()方法和 fillOval()方法
    用于使用当前的颜色填充绘制完成的矩形和椭圆形。

    ⑤ drawString()方法
    用于绘制语段文本,第一个参数 str 表示绘制的文本内容,第二个和第三个参数 x、y 为绘制文本的左下角坐标。

    了解了 Graphics 的方法后,接下来通过一个案例来演示如何使用 Graphics 在组件中进行绘图。在组件第一次显示时,**AWT 线程都会自动去调用组件**的 paint(Graphics g)方法,****该方法传入一个 Graphics 类型的**对象**用于绘制图形。因此,想要在组件中绘制图形,就需要重写它的 paint()方法。

    目前大部分的网站为了防止用户在注册时重复提交表单,在注册页面都会有一个图片验证码。接下来通过重写 Panel 组件的 paint()方法,在一个 Panel 面板上绘制一张图片验证码,如下所示。

    1. import java.awt.*;
    2. import java.util.Random;
    3. public class example14 {
    4. public static void main(String[] args) {
    5. //创建 Frame 对象
    6. final Frame frame = new Frame("验证码");
    7. //创建 Panel 对象
    8. final Panel panle = new Mypanel();
    9. frame.add(panle);
    10. frame.setSize(200, 100);
    11. //将 Frame 居中
    12. frame.setLocationRelativeTo(null);
    13. frame.setVisible(true);
    14. }
    15. }
    16. class Mypanel extends Panel {
    17. public void paint(Graphics g) {
    18. int width = 160; //定义验证码图片的宽度
    19. int height = 40; //定义验证码图片的高度
    20. g.setColor(Color.LIGHT_GRAY); //设置上下文颜色
    21. //填充验证码背景,LIGHT_GRAT,即看到的类似于灰色部分;从坐标(0,0)开始,覆盖到到坐标(width,height)
    22. g.fillRect(0, 0, width, height);
    23. g.setColor(Color.BLACK); //设置上下文颜色
    24. //绘制边框,即窗口内黑色矩形部分,起点坐标为(0,0),覆盖到坐标(width -1,height -1),比验证码背景少一个像素
    25. g.drawRect(0, 0, width - 1, height - 1);
    26. //绘制干扰点,即小点点样的东西
    27. Random r = new Random();
    28. for (int i = 0; i < 100; i++) {
    29. int x = r.nextInt(width) - 2;
    30. int y = r.nextInt(height) - 2;
    31. //绘制椭圆的边框,得到一个椭圆,使它刚好能放入由 x、y、2 和 2 指定的矩形中。两个 2 指的是椭圆覆盖区域的宽度为 2 像素,高度为 2 像素。因为椭圆面积过小,只有两个像素,所以在图中变成了一个个小方块,但放大后确实是椭圆形
    32. g.drawOval(x, y, 2, 2);
    33. }
    34. g.setFont(new Font("黑体", Font.BOLD, 30)); //设置验证码字体为 30 字号的粗体黑体
    35. g.setColor(Color.BLUE); //设置验证码颜色为蓝色
    36. //产生随机验证码
    37. char[] chars = ("0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM").toCharArray();
    38. StringBuffer sb = new StringBuffer();
    39. for (int i = 0; i < 4; i++) {
    40. int pos = r.nextInt(chars.length);
    41. char c = chars[pos];
    42. sb.append(c + "");
    43. }
    44. g.drawString(sb.toString(), 20, 30); //写入验证码,最左侧字符左下角位于(20, 30)坐标
    45. }
    46. }

    运行程序,生成的窗口,如下所示。
    QQ截图20200621134611.png

    2020/7/24 update
    Question

    复习时第二次研究源码,发现一个怎么也排查不出来的 bug。第一次学习时,只是粗略的把代码敲到 ecplise 然后运行,这次学习则是从头至尾钻研了一遍。发现窗体中的随机验证码出现了两次,以前学习的时候以为是第一次运行后留下的文本信息,没太在意,这次无意中想要尝试打印出 StringBuffer 里的内容,发现控制台输出了两条语句,这证明了那不是我的错觉,而是真实的出现了两次随机验证码。

    既然发现了问题那就解决问题,但是问题就出现在这,我尝试了很长时间,但是毫无进展,从代码表面看没有丝毫的漏洞。

    希望以后能够尽快解决吧。

    image.png