《Java游戏编程原理与实践教程》读书笔记(第3章——Java图形处理和Java 2D)

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java游戏编程原理与实践教程》读书笔记(第3章——Java图形处理和Java 2D)相关的知识,希望对你有一定的参考价值。

目录

第3章 Java图形处理和Java 2D

3.1 Java图形坐标系统和图形上下文

3.2 Color类

3.3 Font类和FontMetrics类

3.3.2 FontMetrics类

3.4 常用的绘图方法

3.4.1 绘制直线

3.4.2 绘制矩形

3.4.3 绘制椭圆

3.4.4 绘制弧形

3.4.5 绘制多边形和折线段

3.4.6 清除绘制的图形

3.5 Java 2D简介

3.5.1 Java 2D API

3.5.2 Graphics2D简介

3.5.3 Graphics2D绘制

3.5.4 Graphics2D的属性设置

3.5.5 路径类

3.5.6 平移、缩放或旋转图形


第3章 Java图形处理和Java 2D

Java语言的类库提供了丰富的绘图方法,其中大部分对图形、文本、图像的操作方法都定义在Graphics类中,Graphics类是java.awt程序包的一部分。

3.1 Java图形坐标系统和图形上下文

绘制图形,需要一个坐标系统给予该图形定位。

Java的坐标系统的原点(0, 0)在屏幕的左上角,坐标度量以像素为单位,水平向右为x轴的正方向,垂直向下为y轴的正方向,每个坐标点的值表示屏幕上的一个像素点的位置,所有坐标点的值都取整数。

在屏幕上绘制图形时,所有输出都通过一个图形上下文( graphics context) 来产生。图形上下文有时也称为图形环境,是指允许用户在屏幕上绘制图形的信息,它由Graphics类装,Graphics类的对象可以通过Component类的getGraphics()方法获得。图形上下文表示一个绘制图层,如组件的显示区、打印机上的一页、或者一个屏幕外的图像缓冲区。它提供了绘制3种图形对象(形状、文本和图像)的方法。

public class Demo {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);

        // 获取图形上下文,Graphics类的对象可以通过Component类的getGraphics(方法获得
        Graphics graphics = frame.getGraphics();

        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

在游戏程序开发中,常常在组件的paint()方法内获得java.awt包中的Graphics类的对象,然后调用Graphics类中相应的绘制函数来实现输出。paint()方法是java.awt.Component 类(所有窗口对象的基类)所提供的一个方法,当系统需要重新绘制组件时,将调用该方法。paint() 方法只有一个参数,该参数是Graphics 类的实例。

public class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 一些基本绘制
        g.setColor(new Color(255, 0, 0));
        g.drawString("这是Java中带颜色的文字串", 100, 100);
        g.drawRect(10, 10, 100, 100);
    }
}

显示TestFrame界面

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

绘制组件的时机如下所述:

  • (1 )组件外形发生变化时:如窗口的大小、位置、图标等显示区域更新时,AWT自动从高层直到叶结点组件相应地调用各组件的paint(方法,但这可能有一个延迟感。
  • (2)程序员也可直接调用某一个组件的repaint()或 paint(方法,以立即更新外观(例如在添加新的显示内容后)。

如果要求保留上次的输出结果时可以调用paint()方法,不要求保留上次的输出结果只希望用户能看到最新的输出结果时则可以调用repaint()方法。

3.2 Color类

可以使用java.awt.Color类为绘制的图形设置颜色。Color类使用了sRGB ( standard RGB,标准RGB )颜色空间来表示颜色值。颜色由红(R)、绿(G)、蓝(B)三原色构成,每种原色的强
度用一个byte 值表示,每种原色取值范围从0 (最暗)~255 (最亮),可以根据这3种颜色值的不同组合,显示不同的颜色效果,例如( 0,0,0)表示黑色,(255,255,255)表示白色。

在Java中Color类定义了13种颜色常量供用户使用,它们分别为: Color.blackColor.blueColor.cyanColor.darkGrayColor.gray Color.greenColor.lightGrayColor.magenta、Color.orangeColor.pinkColor.red Color.whiteColor.yellow。从JDK 1.4开始,也可以使用Color类中定义的新常量,它们与上述颜色常量一一对应,分别为: Color.BLACK Color.BLUEColor.CYANColor.DARK_ GRAYColor.GRAYColor.GREEN Color.LIGHT_ GRAYColor.MAGENTA Color.ORANGEColor.PINK Color.REDColor. WHITEColor. YELLOW

public class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 使用常量设置颜色
        g.setColor(Color.GREEN);// 绿色
    }
}

除此之外,用户也可以通过Color类提供的构造方法Color(int r,int g,int b)创建自己需要的颜色。该构造方法通过指定红、绿、蓝3种颜色的值来创建一一个新的颜色,参数r、g、b的取值范围为0~ 255。例如:

Color color = new Color(255, 0, 255);

Graphics类的setColor(Color c)方法可以设置画笔的颜色。如例:

public class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 设置画笔的颜色
        Color green=new Color(0,255,0);
        g.setColor(green);// 绿色
        g.drawString("Hello Color!",200,200);
    }
}

一旦用户生成了自己需要的颜色,就可以通过java.awt.Component 类中的setBackground
(Color c)
setForeground(Color c)方法来设置组件的背景色和前景色,也可以使用该颜色作为当前的绘图颜色。

public class Demo {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);

        // 设置组件的颜色
        // 设置组件的前景色
        Color foregroundColor = new Color(255, 123, 0);
        frame.setForeground(foregroundColor);
        // 设置组件的背景色
        Color backgroundColor = new Color(0, 255, 12);
        frame.setBackground(backgroundColor);

        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.3 Font类和FontMetrics类

可以使用java.awt.Font类创建字体对象。Java提供了物理字体和逻辑字体两种字体。AWT定义了5种逻辑字体,分别为SansSerif、Serif、 Monospaced、 Dialog 和DialogInpu。

Font类的构造方法如下:

Font (String name, int style, int size) ;
  • 其中参数name为字体名,可以设置为系统上可用的任一种字体,如SansSerif SerifMonospacedDialogDialogInput等;
  • 参数style为字型,即设置加粗、斜体等。可以设置为Font.PLAINFont.BOLDFont.ITALIC或Font.BOLD + Font.ITALIC等;
  • 参数size为字号,其取值为正整数。

例如:

Font font = new Font ("Serif", Font. ITALIC,10) ;

如果需要找到系统上的所有可用字体,可以通过创建java.awt.GraphicsEnviroment类的静态方法getLocalGraphicsEnviroment()的实例,调用getAllFonts()方法来获得系统的所有可用字体;或者通过getAvailableFontFamilyName()方法来取得可用字体的名字。

public class Test {
    public static void main(String[] args) {
        // 获取GraphicsEnviroment实例对象,通过静态方法getLocalGraphicsEnvironment()
        GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
        // 通过getAllFonts()方法获取系统所有的可用字体
        Font[] allFonts = graphicsEnvironment.getAllFonts();
        // 查看系统有哪些可用字体
        for (Font font : allFonts) {
            System.out.println(font.getFontName());
        }
    }
}

例如:在生成可用的字体对象后,可以通过java.awt.Component类中的setFont(Font f)方法设置组件的字体,任何组件都可以设置字体,包括Graphics画笔又可以设置字体。

public class Demo {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        Panel panel = new Panel();
        JButton button = new JButton("按钮");
        button.setSize(50, 100);
        button.setLocation(50, 50);

        // 创建字体
        Font font = new Font("Dialog", Font.BOLD, 25);
        // 设置按钮的字体
        button.setFont(font);

        panel.add(button);
        frame.add(panel);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.3.2 FontMetrics类

使用drawString(String s, int x, int y)方法我们可以指定在框架的(x, y)位置开始显示字符串,但是如果想在框架的中央显示字符串,则需要使用FontMetrics类。FontMetrics 类是一个抽象类,要使用FontMetrics对象,可以通过调用Graphics类中的getFontMetrics()方法。FontMetrics 类定义字体的度量,给出了关于在特定的组件上描绘特定字体的信息。这些字体信息包括ascent (上升量)、descent (下降量)、leading (前导宽度)和height (高度)。其中leading 用于描述两行文本间的间距,如图3-4所示。

FontMetrics类提供了下面几种方法用于获取ascent、descent、 leading 和height:

  • int getAscent();//取得由当前FontMetrics对象描述的字体的ascent值。
  • int getDescent();//取得由当前FontMetrics对象描述的字体的descent值。
  • int getLeading();//取得由当前FontMetrics对象描述的字体的leading值。
  • int getHeight();//取得使用当前字体的- -行文本的标准高度。
class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 设置字体
        Font font = new Font("楷体", Font.BOLD, 35);
        // 设置当前使用的字体
        g.setFont(font);
        // 获取FontMetrics对象
        FontMetrics fontMetrics = g.getFontMetrics();
        // 获取字符串的宽度
        int width = fontMetrics.stringWidth("Java游戏编程");
        // 获取当前使用字体的ascent值
        int ascent = fontMetrics.getAscent();
        // 获取当前使用字体的descent值
        int descent = fontMetrics.getDescent();
        int x = (getWidth() - width) / 2;
        int y = (getHeight() + ascent) / 2;
        g.drawString("Java游戏编程", x, y);
    }
}

public class Demo {
    public static void main(String[] args) {
        final TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.4 常用的绘图方法

3.4.1 绘制直线

在Java中可用使用下面的方法绘制一条直线:

drawLine(int x1, int y1, int x2, int y2);

其中,参数x1、x2、x2、y2分别表示该直线的起点(x1, y1)和终点(x2, y2)的坐标值。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 画一条直线
        g.drawLine(0, 0, 200, 300);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.4.2 绘制矩形

Java中提供了绘制空心矩形(只绘制矩形的轮廓)和填充矩形的方法,分别针对普通直角矩形、圆角矩形和三维矩形有不同的绘制方法。

(1)普通直角矩形

  • 可以使用下面方法绘制普通直角矩形的轮廓:drawRect(int x,int y, int width, int height);
  • 如果需要绘制一个有填充颜色的普通直角矩形,可以使用下面的方法:fillRect(int x, int y, int width, int height) ;

这两种方法的参数含义相同,x、y分别表示矩形左上角的x坐标和y坐标,width、 height 分别表示矩形的宽和高。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 画一个普通的直角矩形
        g.drawRect(100, 100, 100, 150);
        // 画一个填充颜色的直角矩形,默认填充黑色,可以指定颜色
        g.setColor(Color.GREEN);
        g.fillRect(250, 100, 100, 150);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

(2)圆角矩形

  • 可以使用下面的方法绘制圆角矩形的轮廓:drawRoundRect(int x,int y, int width, int height, int arcwidth, int arcHeight) ;
  • 如果需要绘制一个有填充颜色的圆角矩形,可以使用下面的方法:fillRoundRect(int x, int y, int width, int height, int arcwidth, int arcHeight) ;

这两种方法的参数含义相同, x、y分别表示矩形左上角的x坐标和y坐标,width、 height 分别表示矩形的宽和高,参数arcWidth和arcHeight分别表示圆角弧的水平直径和竖直直径,如图3-6所示。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 画一个普通的圆角矩形
        g.drawRoundRect(100, 100, 100, 150, 30, 30);
        // 画一个填充颜色的圆角矩形,默认填充黑色,可以指定颜色
        g.setColor(Color.GREEN);
        g.fillRoundRect(250, 100, 100, 150, 30, 30);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

(3)三维矩形

  • 可以使用下面方法绘制三维矩形的轮廓:draw3DRect(int x,int y, int width, int height, boolean raised) ;
  • 如果需要绘制-个有填充颜色的三维矩形,可以使用下面的方法:fill3DRect(int x,int y,int width, int height, boolean raised) ;

这两种方法的参数含义相同,x、y分别表示矩形左上角的x坐标和y坐标,width、 height 分别表示矩形的宽和高,raised 为真( True )表示矩形从表面凸起,raised 为假( False )表示矩形从表面凹进。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 画一个普通的三维矩形
        g.draw3DRect(100, 100, 100, 150, true);
        // 画一个填充颜色的三维矩形,默认填充黑色,可以指定颜色
        g.setColor(Color.GREEN);
        g.fill3DRect(250, 100, 100, 150, false);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.4.3 绘制椭圆

  • 可以使用下面的方法绘制空心椭圆:drawOval (int x,int y, int width, int height) ;
  • 如果需要绘制一个有填充颜色的椭圆,可以使用下面的方法:fillOval(int x,int y, int width, int height) ;

这两种方法的参数含义相同, x、y分别表示该椭圆外接矩形左上角的x坐标和y坐标, width、height分别表示外接矩形的宽和高,如图3-7所示。如果设置外接矩形为正方形,即width和height相等,则可以绘制圆。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 画一个空心椭圆
        g.drawOval(100, 100, 100, 150);
        // 画一个填充颜色的椭圆,默认填充黑色,可以指定颜色
        g.setColor(Color.GREEN);
        g.fillOval(250, 100, 100, 150);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.4.4 绘制弧形

弧形可以看做椭圆的部分,因此它的绘制也是根据其外接矩形进行的。通过drawArc()方法和fillArc()方法可以分别绘制弧线和扇形。这两种方法为:

  • 绘制弧线:drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) ;
  • 绘制扇形:fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) ;

其中x、y、width、height 参数的含义与drawOval0方法的参数含义相同,参数startAngle表示该弧的起始角度,参数arcAngle表示生成角度(从startAngle开始转了多少度),且水平向右方向表示0度,从0度开始沿逆时针方向旋转为正角,如图3-9所示。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 绘制弧线
        g.drawArc(100, 100, 100, 150, 30, 100);
        // 画一个填充颜色的椭圆,默认填充黑色,可以指定颜色
        g.setColor(Color.GREEN);
        g.fillArc(250, 100, 100, 150, 30, 100);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.4.5 绘制多边形和折线段

(1)绘制多边形

使用drawPolygon(方法和fllPolygon)方法可以分别绘制多边形的外框轮廓和填充多边形。

  • 绘制只有轮廓的多边形:drawPolygon(int[ ] xPoints, int[ ] yPoints, int nPoints) ;
  • 绘制带有颜色填充的多边形:fillPolygon(int[ ] xPoints, int[ ] yPoints, int nPoints) ;

其中多边形的顶点是由数组xPoints和yPoints中对应下标的相应元素组成的坐标来指定,数组xPoints存放所有顶点的x坐标,数组yPoints存放所有顶点的y坐标,参数nPoints指定多边形的顶点个数。drawPolygon()方法在绘制多边形时并不自动关闭多边形的最后一条边,而仅是一段开放的折线。因此,如果需要画封闭的边框型多边形,记住在数组的尾部再添上一个起始点的坐标。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 绘制只有轮廓的多边形
        g.drawPolygon(new int[]{90, 20, 30, 40, 50, 90}, new int[]{50, 70, 80, 20, 60, 50}, 6);
        // 绘制带有颜色填充的多边形,默认填充黑色,可以指定颜色
        g.setColor(Color.GREEN);
        g.fillPolygon(new int[]{100, 200, 230, 240, 250, 100}, new int[]{150, 170, 180, 120, 160, 150}, 6);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

除此以外,drawPolygon(Polygon p)方法和fllPolgon(Polygon p)方法也可以用来绘制多边形。这两种方法的参数是一个Polygon类的对象。如果想在多边形上增加一个顶点,可以使用addPoint (int x, int y) 方法。因此可以通过先创建一个空的Polygon对象,再重复调用addPoint()方法将所有多边形的顶点加入创建的Polygon对象中,然后通过调用drawPolygon(Polygon p)方法或者fllPolygon(Polygon p)方法绘制多边形。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 绘制只有轮廓的多边形
        Polygon p1 = new Polygon();
        p1.addPoint(10, 20);
        p1.addPoint(20, 30);
        p1.addPoint(40, 70);
        p1.addPoint(100, 30);
        p1.addPoint(10, 20);
        g.drawPolygon(p1);
        // 绘制带有颜色填充的多边形,默认填充黑色,可以指定颜色
        Polygon p2 = new Polygon();
        p2.addPoint(110, 120);
        p2.addPoint(120, 130);
        p2.addPoint(40, 70);
        p2.addPoint(200, 130);
        p2.addPoint(110, 120);
        g.setColor(Color.GREEN);
        g.fillPolygon(p2);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

(2)绘制折线段

  • 可以使用drawPolygonline()方法绘制折线段:drawPolygonline (int[ ] xPoints, int[ ] yPoints, int nPoints);

其中数组xPoints存放所有顶点的x坐标,数组yPoints存放所有顶点的y坐标,nPoints 指定折线段顶点的个数。

class TestFrame extends JFrame {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 绘制折线段
        g.drawPolyline(new int[]{10, 50, 90, 20}, new int[]{30, 100, 200, 250}, 4);
    }
}

public class Demo {
    public static void main(String[] args) {
        TestFrame frame = new TestFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.4.6 清除绘制的图形

可以使用clearRect()方法清除绘制的图形。clearRect(int x,int y,int width,int height)用背景色填充指定矩形以达到清除该矩形的效果,也就是说当-个Graphics对象使用该方法时,相当于在使用一个“橡皮擦”。参数x、y是被清除矩形的左上角的坐标,另外两个参数是被清除矩形的宽和高。

public class Demo {
    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);
        final Panel panel = new Panel();

        JButton button1 = new JButton("绘制图形");
        button1.setSize(100, 200);
        button1.setLocation(50, 50);
        button1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Graphics g = panel.getGraphics();
                // 绘制一个填充矩形
                g.setColor(Color.ORANGE);
                g.fillRect(50, 50, 100, 100);
            }
        });
        panel.add(button1);

        JButton button2 = new JButton("清除绘制图形");
        button2.setSize(100, 200);
        button2.setLocation(300, 300);
        button2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Graphics g = panel.getGraphics();
                // 调用方法清除绘制的图形,即清除原来画出来的矩形
                g.clearRect(50, 50, 100, 100);
            }
        });
        panel.add(button2);

        frame.setContentPane(panel);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.5 Java 2D简介

3.5.1 Java 2D API

Java 2D API ( Application Programming Interface )增强了抽象窗口工具包( AWT )的图形、文本和图像功能,可以创建高级图形库,开发更为强大的用户接口和新型的Java 应用程序。Java2D API对AWT进行了扩展,提供了更加灵活、功能更全面的绘制包,使其支持更多的图形绘制操作。

Java 2D是Java核心库的一部分,它包含的包有如下几个:

  • java.awt:java.awt包含了一些新增的2DAPI类和接口。其中Graphics2D 继承自java.awt.Graphics ,是描绘2D图形的对象。同时在Graphics2D中新增了许多状态属性,如Stroke、Paint、Clip、Transform等。
  • java.awt.imagejava.awt.image.renderable:java.awt.image和java.awt.image.renderable包中包含用于图像定义和绘制的类和接口。
  • java.awt.colorjava.awt.font:java.awt.font包中包含用于文本布局和字体定义的类和接口; Font 类的功能已得到增强,可支持详细的字体信息规范和复杂印刷特性的使用。Font 对象代表系统上可用字样集的字样实例。
  • java.awt.geom:java.awt.geom包则包含可以勾勒任何形状的GeneralPath 类。它可以由许多不同种类的subpath构成,例如lines和quadratic curves等。为了兼顾方便性,在此包中还定义了许多基本几何图形,包括Arc2D、CubicCurve2D、Line2D 等。
  • java.awt.print:java.awt.print包中包含用于打印所有基于Java 2D的文本、图形和图像的类及接口。

3.5.2 Graphics2D简介

Graphics2D扩展了java.awt.Graphics 包,使得对形状、文本和图像的控制更加完善.Graphics2D对象保存了大量用来确定如何绘制图形的信息,其中大部分都包含在- 个Graphics2D对象的6个属性中,这6个属性分别如下所述:

  • (1)绘制( paint): 该属性确定所绘制线条的颜色,以及填充图形的颜色和图案等。用户可以通过setPaint(Paint p)方法进行该属性值的设置。
  • (2)画笔( stroke ): 该属性可以确定线条的类型以及粗细,还有线段端点的形状。用户可以通过setStroke(Stroke s)方法进行该属性值的设置。
  • (3)字体( font ):该属性可以确定所显示字符串的字体。用户可以通过setFont(Font f)方法进行该属性值的设置。
  • (4)转换( transform ):该属性确定了图形绘制过程中要应用的转换方法,通过指定转换方法可将所画内容进行平移、旋转和缩放。用户可以通过setTransform()方法进行该属性值的设置。
  • (5)剪切( clip): 该属性定义了组件上某区域的边界。用户可以通过setCIlip(Clip c)方法进行该属性值的设置。
  • (6)合成( composite ): 该属性定义了如何绘制重叠的几何图形,使用合成规则可以确定重叠区域的显示效果。用户可以通过setComposite(Composite c)方法来设置该属性的值。

使用Griphics2D对象的方法进行图形的绘制,该对象的常用方法如下:

  • (1)abstract void clip(Shape s):将当前Clip与指定Shape的内部区域相交,并将Clip设置为所得的交集。
  • (2)abstract void draw(Shape s):使用当前Graphics2D.上下文的设置勾画Shape的轮廓。
  • (3)abstract void drawImage(BufferedImage img, BufferedImageOp op, intx, inty):呈现使用BufferedlmageOp过滤的BufferedImage 应用的呈现属性,包括Clip、 Transform 和Composite属性。
  • (4)abstract boolean drawlmage(Image img, Affine Transform xform, ImageObserver obs):呈现一幅图像,在绘制前进行从图像空间到用户空间的转换。
  • (5)abstract void drawString(String s,float x,float y):使用Graphics2D 上下文中当前文本属性状态呈现由指定String 指定的文本。
  • (6)abstract void drawString(String str, int x, int y):使用Graphics2D.上下文中的当前文本属性状态呈现指定String 的文本。
  • (7)abstract void fill(Shape s):使用Graphics2D上下文的设置,填充Shape的内部区域。
  • (8)abstract Color getBackground(:返回用于清除区域的背景色。
  • (9)abstract Composite getComposite():返回Graphics2D.上下文中的当前Composite。
  • (10)abstract Paint getPaint():返回Graphics2D上下文中的当前Paint。
  • (11)abstract Stroke getStroke(): 返回Graphics2D 上下文中的当前Stroke。
  • (12)abstract boolean hit(Rectangle rect, Shape s, boolean onStroke):检查指定的Shape 是否与设备空间中的指定Rectangle 相交。
  • (13)abstract void rotate(double theta):将当前的Graphics2D Transform 与旋转转换连接。
  • (14)abstract void rotate(double theta, double x, double y):将当前的Graphics2DTransform与平移后的旋转转换连接。
  • (15)abstract void scale(double sx, double sy):将当前Graphics2D Transform与可缩放转换连接。
  • (16)abstract void setBackground(Color color):设置Graphics2D上下文的背景色。
  • (17)abstract void setComposite(Composite comp):为Graphics2D 上下文设置Composite。Composite用于所有绘制方法中,例如drawImage、drawString、 draw 和fill。它指定新的像素如何在呈现过程中与图形设备上的现有像素组合。
  • (18)abstract void setPaint(Paint paint):为Graphics2D.上下文设置Paint属性。
  • (19)abstract void setStroke(Stroke s):为Graphics2D.上下文设置Stroke。
  • (20)abstract void setTransform(AffineTransform Tx):重写Graphics2D.上下文中的Transform。
  • (21)abstract void shear(double shx, double shy):将当前Graphics2D Transform 与剪裁转换连接。
  • (22)abstract void translate(double tx, double ty):将当前的Graphics2D Transform与平移转换连接。
  • (23)abstract void translate(int x, int y):将Graphics2D上下文的原点平移到当前坐标系统中的点(x,y)。

3.5.3 Graphics2D绘制

Graphics2D是Graphics类的子类,也是一个抽象类,不能实例化Graphics2D对象,为了使用Graphics2D,可以通过Graphics对象传递-一个组件的绘制方法给Graphics2D 对象。方法如下面的代码段所示:

    @Override
    public void paint(Graphics g) {
        Graphics2D graphics2D = (Graphics2D) g;
    }

Java 2D API提供了几种定义点、直线、曲线、矩形、椭圆等常用几何对象的类,这些新几何类是java.awt.geom包的组成部分,包括Point2D、Line2D、Arc2D、Rectangle2D、 Ellipse2D、CubicCurve2D等。每个类都有单精度和双精度两种像素定义方式,例如Point2D.double 和Point2D.float, Line2D.double和Lint2D.float等,用这些类可以很容易地绘制基本的二维图形对象。

class TestPanel extends JPanel {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 获取Graphics2D
        Graphics2D g2D = (Graphics2D) g;
        // 获取Line2D对象,绘制直线
        Line2D line = new Line2D.Float(1.0f, 2.0f, 150.0f, 20.0f);
        g2D.draw(line);
        // 获取Rectangle2D对象,绘制矩形
        Rectangle2D rect = new Rectangle2D.Float(50, 80, 100, 100);
        Color color = new Color(100, 20, 0);
        g2D.setColor(color);
        g2D.draw(rect);
        // 获取Ellipse2D对象,绘制圆形
        Ellipse2D ellipse = new Ellipse2D.Double(200, 180, 100, 100);
        g2D.setColor(Color.RED);
        g2D.fill(ellipse);
        // 设置字符串
        g2D.drawString("Java游戏编程", 20, 200);
        // 获取CubicCurve2D对象,绘制弧线,比直线多了两个控制点
        CubicCurve2D cubic = new CubicCurve2D.Float(0, 100, 120, 50, 170, 270, 220, 50);
        g2D.draw(cubic);
    }
}

public class Demo {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);

        TestPanel panel = new TestPanel();
        frame.setContentPane(panel);

        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.5.4 Graphics2D的属性设置

下面介绍在Graphics2D的图形上下文中设置常用的属性如paint、stroke和composite。

(1)paint

paint用于填充绘制图形的颜色或图案。API提供了两种方式:GradientPaint和TexturePaint。

  • GradientPaint定义在两种颜色间渐变的填充方式。
  • TexturePaint是利用重复图像片段的方式定义的一种纹理填充方式。

GradientPaint类提供了下面的构造方法来建立颜色渐变方式:

  • ①GradientPaint(float x1, float y1, Color colorl, float x2, float y2, Color color2);
  • ②GradientPaint(float x1, float y1, Color color1, float x2, float y2, Color color2, boolean cyclic);
  • ③GradientPaint(Point2D p1, Color colorl, Point2D p2, Color color2);
  • ④GradientPaint(Point2D pl, Color colorl, Point2D p2, Color color2, boolean cyclic);

其中,构造方法①和②中参数x1、y1指定颜色渐变的起点坐标,x2、y2指定颜色渐变的终点坐标,填充颜色从colorl渐变至color2。构造方法②中参数cyclic 为True时,填充方式与构造方法①定义的相同;如果cyclic 为False,则填充方式为非周期性渐变。构造方法③与构造方法①类似,构造方法④与构造方法②类似,只是在构造方法③和④中使用了Point2D 对象来指定填充颜色渐变的起始(p1)和结束(p2)位置。

class TestPanel extends JPanel {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 获取Graphics2D
        Graphics2D g2D = (Graphics2D) g;
        // 实例化GradientPaint对象
        GradientPaint gradientPaint = new GradientPaint(20f, 30f, Color.RED, 200f, 200f, Color.GREEN);
        // 设置paint属性
        g2D.setPaint(gradientPaint);
        // 填充一个矩形来显示渐变
        g2D.fill(new Rectangle2D.Float(10f, 10f, 100f, 200f));
    }
}

public class Demo {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);

        TestPanel panel = new TestPanel();
        frame.setContentPane(panel);

        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

而TexturePaint类的构造方法为:TexturePaint(BufferedImage txtr, Rectangle2D anchor)。其中txtr用来定义一个单位的填充图像的材质,anchor用来复制材质。

class TestPanel extends JPanel {
    // 只需要继承任意组件类(Component类的子类),都可以重写paint(Graphics g)方法
    @Override
    public void paint(Graphics g) {
        // 获取Graphics2D
        Graphics2D g2D = (Graphics2D) g;
        // 创建BufferedImage对象,width是宽度,height是高度,imageType是创建的图像类型
        BufferedImage texture = new BufferedImage(5, 5, 1);
        // 创建一个Graphics2D对象
        Graphics2D pattern=texture.createGraphics();
        // 设置纹理图案的颜色
        pattern.setColor(Color.GREEN);
        // 填充纹理图案
        pattern.fillRect(0, 0, 20, 20);
        pattern.setColor(Color.ORANGE);
        pattern.fillOval(0, 0, 20, 20);
        // 创建用于定位和复制纹理的Rectangle2D对象
        Rectangle2D rect = new Rectangle2D.Float(0, 0, 20, 20);
        // 创建TexturePaint对象
        TexturePaint texturePaint = new TexturePaint(texture, rect);
        // 以指定的纹理设置绘图方式
        g2D.setPaint(texturePaint);
        // 以纹理方式绘制填充矩形
        g2D.fill(new Rectangle2D.Double(100, 50, 270, 270));
    }
}

public class Demo {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLocation(200, 200);
        frame.setSize(500, 500);

        TestPanel panel = new TestPanel();
        frame.setContentPane(panel);

        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

(2)stroke

stroke用于在绘制图形的轮廓时确定线条的形状和粗细,通常使用BasicStroke 对象来定义,通过setStroke()方法设定stroke的属性值。BasicStroke 定义的特性包括线条宽度、笔形样式、线段连接样式和短划线图案等。使用stroke属性设定图形轮廓的绘制方式时,首先调用setStroke()方法设定轮廓的绘制方式,然后使用setPaint()方法定义画笔如何绘制该图形,最后使用draw()方法来绘制该图形。

在BasicStroke中定义了一组基本的简单图形轮廓的直线绘制和点划线绘制方式,它提供了3种绘制粗线的末端样式: CAP_BUTT、CAP_ROUND和CAP_ SQUARE;以及3种线段连接样式:JOIN_BEVEL、JOIN_MITER和JOIN_ROUND。

BasicStroke类提供了下面的构造方法来建立画笔的绘制方式:

  • ①BasicStoke 0);
  • ②BasicStoke (float width);
  • ③BasicStoke (float width, int cap, int join);
  • ④BasicStoke (float width, int cap, int join, float miterlimit);
  • ⑤BasicStoke (float width, int cap, int join, float miterlimit, float[} dash, float dash phase);

其中width表示轮廓线的宽度;cap 表示轮廓线末端的样式(可选值为:BasicStroke.CAP_BUTTBasicStroke.CAP_ROUNDBasicStroke.CAP_ SQUARE);join 表示相交线段的连接样式(可选值为:BasicStroke.JOIN_BEVELBasicStroke.JOIN_MITERBasicStroke.JOIN_ROUND);miterlimit表示在JOIN_MITER 模式下若相交的尖形末端大于miterlimit,则超过部分被削去;dash为虛线的样式,数组内的值为短划线和空白间距的值;dash_ phase为虚线样式数组的起始索引。

public class Frame extends JFrame {
    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;

        // 创建一个BasicStroke对象来设置直线的绘制方式
        Line2D line = new Line2D.Double(30, 50, 100, 80);
        Stroke stroke = new BasicStroke(10.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL);
        g2.setStroke(stroke);
        g2.draw(line);

        // 创建一个BasicStroke对象来设置椭圆的绘制方式
        Ellipse2D ellipse = new Ellipse2D.Double(150, 50, 90, 90);
        stroke = new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 2, new float[]{10, 5}, 0);
        g2.setStroke(stroke);
        g2.draw(ellipse);

        // 创建一个BasicStroke对象来设置矩形的绘制方式
        Rectangle2D rect = new Rectangle2D.Double(30, 100, 80, 80);
        stroke = new BasicStroke(10, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0);
        g2.setStroke(stroke);
        g2.draw(rect);
    }

    public static void main(String[] args) {
        Frame frame = new Frame();
        frame.setTitle("测试");
        frame.setSize(300, 300);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

(3)composite

composite用于定义绘制重叠图形的绘制方式。当绘制多个图像时,遇到图像重叠的情况,需要确定重叠部分的颜色显示方式,重叠区域的像素颜色决定了该部分图像的透明程度。在composite的定义中,最常用的就是AlphaComposite, 我们可以通过setComposite()方 法来将AlphaComposite对象添加到Graphics2D上下文中,设置图像重叠部分的复合样式。

AlphaComposite有两个构造器方法如下:

private AlphaComposite(int rule)
private AlphaComposite(int rule, float alpha)

AlphaComposite类中定义了多种新颜色与已有颜色的复合规则,例如SRC _OVER表示混合时新颜色(源色)应覆盖在已有颜色(目标色)之上; DST_OUT表示混合时去除已有颜色; SRC _OUT表示混合时去除新颜色; DST_ OVER 表示混合时用已有颜色覆盖新颜色。

在设置混合颜色的同时,还可以设置颜色的透明度alpha 值,它用百分比表示在颜色重叠时当前颜色的透明度,alpha 取值范围从0.0(完全透明) ~ 1.0 (完全不透明)。

public class Frame extends JFrame {
    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;

        g2.setColor(Color.RED);
        Rectangle2D rect1 = new Rectangle2D.Double(50, 50, 100, 100);
        g2.fill(rect1);

        g2.setColor(Color.GREEN);
        Rectangle2D rect2 = new Rectangle2D.Double(120, 70, 100, 100);
        AlphaComposite comp = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f);
        g2.setComposite(comp);
        g2.setColor(Color.red);
        g2.setComposite(comp);
        g2.fill(rect2);
    }

    public static void main(String[] args) {
        Frame frame = new Frame();
        frame.setTitle("测试");
        frame.setSize(300, 300);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.5.5 路径类

在java.awt.geom包中定义了几何图形类,包括点、直线、矩形、圆、椭圆、多边形等。

路径类用于构造直线、二次曲线和三次曲线的几何路径,它可以包括多个子路径。

下面讲解GeneralPath类,介绍路径类的功能和应用。

1.构造方法

  • (1) GeneralPath(int rule):以rule指定缠绕规则构建对象。缠绕规则确定路径内部的方式。有两种方式的缠绕规则: Path2D.WIND_ EVEN ODD用于确定路径内部的奇偶(even-odd) 缠绕规则;Path2D.WIND NON ZERO用于确定路径内部的非零(non-zero) 缠绕规则。
  • (2) GeneralPath():以默认的缠绕规则Path2D.WIND NON ZERO构建对象。
  • (3) GeneralPath(int rule, int initialCapacity):以rule指定缠绕规则和initialCapacity指定的容量(以存储路径坐标)构建对象。
  • (4) GeneralPath(Shape s):以Shape对象s构建对象。

2.常用方法

路径对象的常用方法如下:

  • (1)void append(Shape s, boolean connect):将指定Shape对象的几何形状追加到路径中,也许会使用一条线段将新几何形状连接到现有的路径段。如果connect为True并且路径非空,则被追加的Shape几何形状的初始moveTo操作将被转换为lineTo 操作。
  • (2)void closePath():回到初始点使之形成闭合的路径。
  • (3)boolean contains(double x, double y):测试指定坐标是否在当前绘制的边界内。
  • (4)void curveTo(float x1, float y1, float x2, float y2, float x3, float y3):将3个新点定义的曲线段添加到路径中。
  • (5)Rectangle2D getBounds2D():获得路径的边界框。
  • (6)Point2D getCurrentPoint():获得当前添加到路径的坐标。
  • (7)int getWindingRule():获得缠绕规则。
  • (8)void lineTo(float x, floaty):绘制一条从当前坐标到(x, y)指定坐标的直线,将(x, y)坐标添加到路径中。
  • (9)void moveTo(float x, float y):从当前坐标位置移动到(x,y) 指定位置,将(x, y)添加到路径中。.
  • (10)void quadTo(float x1, float y1, float x2, float y2):将两个新点定义的曲线段添加到路径中。
  • (11)void reset():将路径重置为空。
  • (12)void setWindingRule(int rule):设置缠绕规则。
  • (13)void transform(AffineTransform at):使用指定的Affine Transform变换此路径的几何形状。
public class Frame extends JFrame {
    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;

        // 用路径画一个三角形
        GeneralPath path = new GeneralPath();
        path.moveTo(50f, 50f);
        path.lineTo(300f, 100f);
        path.lineTo(150f, 150f);
        path.lineTo(50f, 50f);
        g2.draw(path);
    }

    public static void main(String[] args) {
        Frame frame = new Frame();
        frame.setTitle("测试");
        frame.setSize(400, 400);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

3.5.6 平移、缩放或旋转图形

需要平移、缩放或旋转一个图形,可以使用AffineTransform类来实现对图形的这些操作。
(1)首先使用AffineTransform类创建一个对象:AffineTransform trans=new AffineTransform();。对象trans具有最常用的3个方法来实现对图形变换的操作:

  • translate(double a,double b):将图形在x轴方向移动a个单位像素, y轴方向移动b个单位像素。a是正值时向右移动,负值是向左移动; b是正值时是向下移动,负值是向上移动。
  • scale(double a,double b):将图形在x轴方向缩放a倍, y轴方向缩放b倍。
  • rotate(double number,double x,double y):将图形沿顺时针或逆时针以(x, y)为轴点旋转number个弧度。

(2)进行需要的变换,比如要把一个矩形绕点(100,100)顺时针旋转60°, 那么就要先做好准备:transrotate (60.0*3.1415927/180,100,100) ;
(3)把Graphics对象,比如g2d设置为具有trans这种功能的“画笔”:g2d. setTransform(trans) ;假如rect是一个矩形对象,那么g2d.draw(rect)画的就是旋转后的矩形。

public class Frame extends JFrame {
    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;

        // 用路径画一个矩形
        Rectangle2D rect = new Rectangle2D.Double(250, 50, 80, 150);
        g2.setColor(Color.yellow);
        g2.fill(rect);

        // 创建AffineTransform类对象
        AffineTransform transform = new AffineTransform();
        // 将图形以点(100,100)为轴点选择45°
        transform.rotate(45, 100, 100);
        // 设置旋转
        g2.setTransform(transform);
        // 绘制旋转后的图形
        g2.fill(rect);
    }

    public static void main(Stri

以上是关于《Java游戏编程原理与实践教程》读书笔记(第3章——Java图形处理和Java 2D)的主要内容,如果未能解决你的问题,请参考以下文章

《Java游戏编程原理与实践教程》读书笔记(第5章——推箱子游戏)

[读书笔记]Java编程思想

Java编程思想读书笔记--第14章类型信息

《深入理解Java虚拟机》读书笔记——第1章 走近Java

Java编程思想读书笔记--第21章并发

《Java编程思想》读书笔记之第3章-操作符