杰瑞科技汇

Graphics2D如何绘制复杂图形?

Graphics2D 是 Java 2D API 的核心类,它扩展了 Graphics 类,提供了更强大、更灵活的 2D 图形绘制功能,与早期 Graphics 类主要用于绘制基本图形(线、矩形、圆)不同,Graphics2D 引入了现代图形学的概念,如坐标变换、抗锯齿、任意形状的路径、渐变和纹理填充等。

Graphics2D如何绘制复杂图形?-图1
(图片来源网络,侵删)

你可以把 Graphics2D 想象成一个功能极其丰富的“虚拟画笔”和“虚拟画布”,你可以用它来绘制从简单的 UI 组件到复杂的图表、游戏画面等各种 2D 图形。


核心概念:Graphics2D 的工作模式

Graphics2D 的工作遵循一个核心思想:修改状态,然后绘制,这个状态由一个叫做 Graphics2D 对象自身的属性集合来定义,在调用任何绘制方法(如 draw()fill())之前,你需要先设置好这些属性。

这些属性主要包括:

  • Stroke (描边): 定义线条的样式,如宽度、虚线模式等。
  • Paint (填充): 定义填充图形内部的方式,可以是纯色、渐变色或图案。
  • Font (字体): 定义绘制文本时使用的字体。
  • Transform (变换): 定义坐标系的变换,如平移、旋转、缩放等。
  • Composite (合成): 定义新绘制的图形与已有图形如何混合(如透明度)。
  • Rendering Hint (渲染提示): 提供关于如何进行高质量渲染的提示,如是否启用抗锯齿。

如何获取 Graphics2D 对象?

你通常不会直接 new 一个 Graphics2D 实例,相反,它会由 Java AWT 或 Swing 框架在特定事件(如组件需要重绘时)传递给你。

Graphics2D如何绘制复杂图形?-图2
(图片来源网络,侵删)

最常见的场景是在 Swing 组件的 paintComponent 方法中:

import javax.swing.*;
import java.awt.*;
public class MyCanvas extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        // 1. 首先调用父类的 paintComponent,确保组件的正常绘制
        super.paintComponent(g);
        // 2. 将传入的 Graphics 对象强制转换为 Graphics2D
        Graphics2D g2d = (Graphics2D) g;
        // 3. 你就可以使用 g2d 的所有强大功能了
        // ... 在这里绘制你的图形 ...
    }
}

为什么需要强制转换? paintComponent 方法的参数是 Graphics,这是一个更基础、更通用的类,为了使用 Java 2D 的高级功能,你必须将其“升级”为 Graphics2D


Graphics2D 的核心功能与使用示例

下面我们通过代码示例来展示 Graphics2D 的主要功能。

示例1:基本图形绘制

这是 Graphics2D 的基础,但它比 Graphics 更灵活。

Graphics2D如何绘制复杂图形?-图3
(图片来源网络,侵删)
// 在 paintComponent 方法中
Graphics2D g2d = (Graphics2D) g;
// 设置抗锯齿,让图形边缘更平滑
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 1. 绘制一个填充矩形
g2d.setColor(Color.BLUE);
g2d.fillRect(50, 50, 100, 80);
// 2. 绘制一个描边矩形
g2d.setColor(Color.RED);
g2d.setStroke(new BasicStroke(3.0f)); // 设置线条宽度为3像素
g2d.drawRect(170, 50, 100, 80);
// 3. 绘制一个填充圆形
g2d.setColor(Color.GREEN);
g2d.fillOval(50, 160, 100, 100);
// 4. 绘制一个描边圆形
g2d.setColor(Color.ORANGE);
g2d.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.drawOval(170, 160, 100, 100);

示例2:使用 Paint(渐变填充)

Paint 接口允许你用更复杂的方式填充图形,比如线性渐变。

// 创建一个从左到右的线性渐变
// 参数:起点坐标, 终点坐标, 颜色数组, 颜色位置数组 (null 表示均匀分布)
GradientPaint gradient = new GradientPaint(50, 50, Color.YELLOW, 250, 250, Color.RED);
// 设置当前的填充模式为这个渐变
g2d.setPaint(gradient);
// 用这个渐变填充一个矩形
g2d.fillRect(50, 50, 200, 200);

示例3:使用 Stroke(虚线描边)

BasicStroke 可以让你创建虚线、点线等。

// 创建一个虚线模式
// 参数:线宽, 端点样式, 连接样式, 虚线模式数组, 虚线偏移量
float[] dashPattern = {10.0f, 5.0f}; // 10像素实线,5像素空白
BasicStroke dashedStroke = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, dashPattern, 0.0f);
g2d.setStroke(dashedStroke);
g2d.setColor(Color.PURPLE);
g2d.drawRect(50, 50, 200, 200);

示例4:使用 Transform(坐标变换)

这是 Graphics2D 最强大的功能之一,它允许你移动、旋转、缩放整个坐标系。

重要提示:变换操作是累积的,并且会影响后续的所有绘制,通常的做法是保存当前状态 -> 应用变换 -> 绘制 -> 恢复状态。

// 保存当前原始的 Graphics2D 状态
g2d.save();
// 1. 平移坐标系
g2d.translate(100, 100); // 原点 (0,0) 现在在 (100,100) 位置
// 2. 旋转坐标系 (弧度)
// Math.PI / 2 表示 90 度
g2d.rotate(Math.PI / 4); // 围绕当前原点 (0,0) 旋转,也就是新的 (100,100) 点
// 3. 缩放坐标系
g2d.scale(1.5, 1.5); // x 和 y 方向都放大 1.5 倍
// 在这个变换后的坐标系中绘制一个矩形
// 这个矩形在原始坐标系中的位置会非常复杂,但现在画起来很简单
g2d.setColor(Color.MAGENTA);
g2d.fillRect(-50, -50, 100, 100); // 围绕新原点绘制
// 恢复到之前保存的状态,避免影响后续绘制
g2d.restore();
// 在原始坐标系中绘制一个矩形,用于对比
g2d.setColor(Color.BLACK);
g2d.drawRect(50, 50, 100, 100);

示例5:绘制任意形状(Path2D

Graphics2D 可以绘制由直线和曲线组成的任意复杂路径。

// 创建一个路径对象
Path2D path = new Path2D.Double();
// 移动到起点
path.moveTo(50, 50);
// 画一条线到 (150, 50)
path.lineTo(150, 50);
// 画一条二次贝塞尔曲线
// 参数:控制点x, 控制点y, 终点x, 终点y
path.quadTo(200, 100, 150, 150);
// 画一条线回到起点,形成封闭图形
path.lineTo(50, 50);
// 填充这个路径
g2d.setColor(Color.CYAN);
g2d.fill(path);
// 描边这个路径
g2d.setColor(Color.BLUE);
g2d.setStroke(new BasicStroke(2));
g2d.draw(path);

Graphics2D 的应用场景

  1. Swing/AWT 自定义绘制:当你需要超越标准组件(如 JButton, JLabel)的功能时,例如绘制一个自定义的进度条、图表、仪表盘等。
  2. 游戏开发:2D 游戏(如平台跳跃游戏、策略游戏)的渲染核心,每一帧都会通过 paintComponent 清空画布,然后重新绘制所有游戏元素(玩家、敌人、背景等)。
  3. 数据可视化:绘制柱状图、折线图、饼图等,你需要根据数据动态计算坐标和形状,然后用 Graphics2D 将其绘制出来。
  4. 图像处理:虽然 Java 有专门的 BufferedImage 和图像处理类,但 Graphics2D 提供了在图像上绘制图形、文本、叠加其他图像等基础功能。
  5. 图表生成:生成报告、网页中使用的静态图表图片。

完整可运行的示例代码

下面是一个完整的、可运行的 Swing 应用,它在一个窗口中展示了上面提到的各种 Graphics2D 功能。

import javax.swing.*;
import java.awt.*;
public class Graphics2DDemo extends JFrame {
    public Graphics2DDemo() {
        setTitle("Java Graphics2D Demo");
        setSize(600, 500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        // 将自定义的 JPanel 添加到窗口中
        add(new DrawingPanel());
    }
    public static void main(String[] args) {
        // 在事件调度线程中创建和显示 GUI
        SwingUtilities.invokeLater(() -> {
            new Graphics2DDemo().setVisible(true);
        });
    }
}
// 自定义的 JPanel,负责所有绘制工作
class DrawingPanel extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        // 启用抗锯齿,使图形更平滑
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // --- 1. 基本图形 ---
        drawBasicShapes(g2d);
        // --- 2. 渐变填充 ---
        drawGradient(g2d);
        // --- 3. 虚线描边 ---
        drawDashedShape(g2d);
        // --- 4. 坐标变换 ---
        drawTransformedShape(g2d);
        // --- 5. 任意路径 ---
        drawCustomPath(g2d);
    }
    private void drawBasicShapes(Graphics2D g2d) {
        g2d.setColor(Color.BLUE);
        g2d.fillRect(20, 20, 80, 60);
        g2d.setColor(Color.RED);
        g2d.drawRect(110, 20, 80, 60);
        g2d.setColor(new Color(0, 128, 0)); // 绿色
        g2d.fillOval(20, 100, 60, 60);
        g2d.setColor(Color.ORANGE);
        g2d.drawOval(100, 100, 60, 60);
    }
    private void drawGradient(Graphics2D g2d) {
        GradientPaint gradient = new GradientPaint(200, 20, Color.YELLOW, 300, 100, Color.RED);
        g2d.setPaint(gradient);
        g2d.fillRect(200, 20, 100, 80);
    }
    private void drawDashedShape(Graphics2D g2d) {
        float[] dashPattern = {10.0f, 5.0f};
        BasicStroke dashedStroke = new BasicStroke(3.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, dashPattern, 0.0f);
        g2d.setStroke(dashedStroke);
        g2d.setColor(Color.PURPLE);
        g2d.drawRect(320, 20, 100, 80);
    }
    private void drawTransformedShape(Graphics2D g2d) {
        g2d.save(); // 保存原始状态
        g2d.translate(350, 250); // 平移
        g2d.rotate(Math.PI / 6); // 旋转30度
        g2d.setColor(Color.MAGENTA);
        g2d.fillRect(-40, -40, 80, 80); // 围绕新原点绘制
        g2d.restore(); // 恢复状态
    }
    private void drawCustomPath(Graphics2D g2d) {
        Path2D star = new Path2D.Double();
        double centerX = 100;
        double centerY = 350;
        double outerRadius = 50;
        double innerRadius = 20;
        int points = 5;
        for (int i = 0; i < points * 2; i++) {
            double radius = (i % 2 == 0) ? outerRadius : innerRadius;
            double angle = Math.toRadians(i * 180.0 / points) - Math.PI / 2;
            double x = centerX + Math.cos(angle) * radius;
            double y = centerY + Math.sin(angle) * radius;
            if (i == 0) {
                star.moveTo(x, y);
            } else {
                star.lineTo(x, y);
            }
        }
        star.closePath();
        g2d.setColor(Color.CYAN);
        g2d.fill(star);
        g2d.setColor(Color.BLUE);
        g2d.setStroke(new BasicStroke(2));
        g2d.draw(star);
    }
}

Graphics2D 是 Java 进行 2D 图形绘制的基石,掌握它的关键在于理解其状态驱动的绘制模式,即通过设置 Stroke, Paint, Transform 等属性,然后调用 draw()fill() 方法,虽然它已经被一些更现代的库(如 JavaFX)部分取代,但在 Swing 应用、数据可视化和 2D 游戏开发中,Graphics2D 依然是一个非常强大和重要的工具。

分享:
扫描分享到社交APP
上一篇
下一篇