58、Java 2D 图形绘制入门指南

Java 2D 图形绘制入门指南

Java 2D 图形绘制入门指南

1. Java 2D 图形绘制简介

在 Java 编程中,Java 2D 为我们提供了强大的图形绘制功能。通过 Java 2D,我们可以绘制各种基本形状,如线条、矩形、椭圆等,还能设置形状的填充颜色、轮廓粗细,甚至创建透明形状和渐变填充。

1.1 获取图形上下文

在使用 Java 2D 进行绘图之前,我们需要获取一个图形上下文对象。通常的做法是将绘图代码放在组件的 paint 方法中,该方法会接收一个图形上下文作为参数。 paint 方法会在组件需要重绘时被 Swing 调用,例如组件首次显示、窗口最小化后恢复或被其他窗口遮挡再移开等情况。我们也可以通过调用组件的 repaint 方法来手动触发 paint 方法。

图形上下文对象是 Graphics2D 类的实例,但 paint 方法传递的是 Graphics 类的对象,因此我们需要将其转换为 Graphics2D 对象:

public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D)g;
    // 后续代码...
}

为了获得更好的图形效果,我们可以使用抗锯齿提示:

g2.setRenderingHint(
    RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);

1.2 绘制形状

要绘制形状,首先需要创建一个 Shape 对象来表示要绘制的形状。Java 2D 提供了多个实现 Shape 接口的类,例如创建一个基本矩形:

Shape rect = new Rectangle2D.Float(10, 20, 120, 150);

这个矩形的左上角坐标为 (10, 20),宽度为 120,高度为 150。创建好形状后,我们可以调用图形上下文的 draw 方法来绘制形状的轮廓:

g2.draw(rect);

我们还可以对形状进行一些调整:
- 改变颜色 :调用 setColor 方法,如 g2.setColor(Color.RED);
- 改变线条粗细 :调用 setStroke 方法并传入 BasicStroke 类的实例,如 g2.setStroke(new BasicStroke(4));
- 填充形状 :调用 fill 方法,如 g2.fill(rect);
- 同时绘制轮廓和填充颜色 :先调用 draw 方法,再调用 fill 方法,并在中间改变颜色,例如:

g2.setColor(Color.BLACK);
g2.draw(rect);
g2.setColor(Color.MAGENTA);
g2.fill(rect);

1.3 简单图形程序示例

以下是一个简单的程序,用于显示一个椭圆:

import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;

public class SimpleShape extends JFrame {
    public static void main(String [] args) {
        new SimpleShape();
    }

    public SimpleShape() {
        this.setSize(300, 300);
        this.setTitle("A Simple Shape Program");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(new PaintSurface(), BorderLayout.CENTER);
        this.setVisible(true);
    }

    private class PaintSurface extends JComponent {
        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            Shape s = new Ellipse2D.Float(20, 50, 250, 150);
            g2.setPaint(Color.BLACK);
            g2.draw(s);
        }
    }
}

这个程序的重要点如下:
1. 导入了 javax.swing java.awt java.awt.geom 三个包,大多数图形绘制程序至少需要这三个包。
2. SimpleShape 类继承自 JFrame ,也可以继承 JApplet 作为小程序使用,但需要移除 JApplet 未定义的方法调用。
3. main 方法创建了 SimpleShape 类的一个新实例。
4. SimpleShape 构造函数进行了窗口的基本设置,并添加了 PaintSurface 组件。
5. PaintSurface 类继承自 JComponent ,重写了 paint 方法,在方法中进行了图形上下文转换、设置抗锯齿提示、创建椭圆形状并绘制。

2. 创建形状

Java 2D 中所有可绘制的形状都是通过实现 Shape 接口的类创建的。以下是一些常见的基本形状类及其构造函数:
| 类 | 构造函数 | 描述 |
| — | — | — |
| Arc2D.Float | (float x, float y, float w, float h, float start, float extent, int type) | 创建一个圆弧,由前四个参数定义椭圆, start 是圆弧的起始角度(度), extent 是圆弧的角度范围, type 可以是 OPEN CHORD PIE |
| Ellipse2D.Float | (float x, float y, float w, float h) | 创建一个椭圆,其外接矩形的左上角坐标为 (x, y),宽度为 w,高度为 h。如果 w 和 h 相等,则为圆形 |
| Line2D.Float | (float x1, float y1, float x2, float y2) (Point2D p1, Point2D p2) | 创建一条从 (x1, y1) 到 (x2, y2) 的直线,或从点 p1 到点 p2 的直线 |
| Rectangle2D.Float | (float x, float y, float w, float h) | 创建一个矩形,左上角坐标为 (x, y),宽度为 w,高度为 h |
| RoundRectangle2D.Float | (float x, float y, float w, float h, float arcw, float arch) | 创建一个圆角矩形,左上角坐标为 (x, y),宽度为 w,高度为 h, arcw arch 分别指定圆角的宽度和高度 |

这些类的名称可能看起来有些奇怪,例如 Rectangle2D.Float ,实际上 Float Rectangle2D 类的内部类,每个类还有一个 Double 内部类,用于更精确地表示形状。对于大多数情况, float 精度已经足够,但在需要高精度的应用中,可能需要使用 Double 类。

2.1 创建线条

线条是最基本的形状,可以使用 Line2D.Float 类创建。例如:

Shape line1 = new Line2D.Float(0, 0, 100, 200);

也可以使用 Point2D 对象来创建线条:

Point2D start = new Point2D.Float(0, 0);
Point2D end = new Point2D.Float (100, 200);
Shape line1 = new Line2D.Float(start, end);

以下是绘制网格线的代码示例:

for (int i = 0; i < getSize().width; i += 10)
    g2.draw(new Line2D.Float(i, 0, i, getSize().height));
for (int i = 0; i < getSize().height; i += 10)
    g2.draw(new Line2D.Float(0, i, getSize().width, i));

第一个 for 循环绘制垂直线,第二个绘制水平线。

2.2 创建矩形

矩形的创建需要指定起始点 (x, y) 以及宽度和高度。例如:

Shape rect1 = new Rectangle2D.Float(10, 10, 60, 80);

Java 2D 还提供了 RoundRectangle2D 类来创建圆角矩形,例如:

Shape round1 = new RoundRectangle2D.Float(110, 10, 80, 80, 10, 10);

通过设置不同的圆角宽度和高度,可以创建出各种有趣的形状:

Shape round2 = new RoundRectangle2D.Float(210, 10, 60, 80, 50, 75);

2.3 创建椭圆

椭圆是一种圆形形状,其构造函数与矩形类似。例如:

Shape ellipse1 = new Ellipse2D.Float(10, 110, 80, 80);

如果外接矩形是正方形,则椭圆为圆形。以下是不同形状的椭圆示例:

Shape ellipse2 = new Ellipse2D.Float(110, 110, 80, 40);
Shape ellipse3 = new Ellipse2D.Float(210, 110, 40, 80);

2.4 创建圆弧

圆弧是椭圆的一部分,创建圆弧需要指定包含椭圆的外接矩形以及起始角度、角度范围和圆弧类型。例如:

Shape arc1 = new Arc2D.Float(10, 210, 80, 80, 90, 90, Arc2D.OPEN);
Shape arc2 = new Arc2D.Float(110, 210, 80, 80, 0, 180, Arc2D.CHORD);
Shape arc3 = new Arc2D.Float(210, 210, 45, 180, 45, 90, Arc2D.PIE);

2.5 ShapeMaker 程序示例

以下是一个 ShapeMaker 程序,用于绘制多种形状:

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;

public class ShapeMaker extends JFrame {
    public static void main(String [] args) {
        new ShapeMaker();
    }

    public ShapeMaker() {
        this.setSize(300, 300);
        this.setTitle("Shape Maker");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(new PaintSurface(), BorderLayout.CENTER);
        this.setVisible(true);
    }

    private class PaintSurface extends JComponent {
        ArrayList<Shape> shapes = new ArrayList<Shape>();
        Point startDrag, endDrag;
        Shape found = null;

        public PaintSurface() {
            Shape s;
            // 矩形
            s = new Rectangle2D.Float(10, 10, 60, 80);
            shapes.add(s);
            // 圆角矩形
            s = new RoundRectangle2D.Float(110, 10, 80, 80, 10, 10);
            shapes.add(s);
            // 圆角矩形
            s = new RoundRectangle2D.Float(210, 10, 60, 80, 50, 75);
            shapes.add(s);
            // 圆形
            s = new Ellipse2D.Float(10, 110, 80, 80);
            shapes.add(s);
            // 椭圆
            s = new Ellipse2D.Float(110, 110, 80, 40);
            shapes.add(s);
            // 椭圆
            s = new Ellipse2D.Float(210, 110, 40, 80);
            shapes.add(s);
            // 圆弧
            s = new Arc2D.Float(10, 210, 80, 80, 90, 90, Arc2D.OPEN);
            shapes.add(s);
            // 圆弧
            s = new Arc2D.Float(110, 210, 80, 80, 0, 180, Arc2D.CHORD);
            shapes.add(s);
            // 圆弧
            s = new Arc2D.Float(210, 210, 80, 80, 45, 90, Arc2D.PIE);
            shapes.add(s);
        }

        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D)g;
            // 开启抗锯齿
            g2.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            // 绘制背景网格
            g2.setPaint(Color.LIGHT_GRAY);
            for (int i = 0; i < getSize().width; i += 10)
                g2.draw(new Line2D.Float(i, 0, i, getSize().height));
            for (int i = 0; i < getSize().height; i += 10)
                g2.draw(new Line2D.Float(0, i, getSize().width, i));
            // 绘制所有形状
            g2.setColor(Color.BLACK);
            g2.setStroke(new BasicStroke(2));
            for (Shape s : shapes)
                g2.draw(s);
        }
    }
}

这个程序将所有形状存储在一个 ArrayList 中,在 PaintSurface 构造函数中创建形状,在 paint 方法中绘制所有形状。这种方法对于处理多个形状的程序非常有用,当用户绘制新形状时,只需将其添加到 ArrayList 中,每次调用 paint 方法时,所有形状都会被重新绘制。

graph TD;
    A[开始] --> B[创建 ShapeMaker 实例];
    B --> C[设置窗口大小、标题和关闭操作];
    C --> D[添加 PaintSurface 组件];
    D --> E[显示窗口];
    E --> F[PaintSurface 构造函数创建形状];
    F --> G[调用 paint 方法];
    G --> H[转换图形上下文];
    H --> I[设置抗锯齿提示];
    I --> J[绘制背景网格];
    J --> K[绘制所有形状];
    K --> L[结束];

3. 填充形状

前面提到可以使用纯色填充形状,只需先调用 setPaint 方法设置填充颜色,再调用 fill 方法填充形状。例如:

g2.setColor(Color.RED);
g2.fill(rect1);

这里将填充颜色设置为红色,然后填充名为 rect1 的形状。但填充不仅仅局限于纯色,下面将介绍如何创建部分透明的填充以及从一种颜色逐渐过渡到另一种颜色的渐变填充。

3.1 绘制透明形状

Java 2D 允许通过指定合成规则来创建透明形状。虽然合成规则的功能不止设置透明度,但由于篇幅限制,这里只介绍设置透明度的方法:

g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.50F));

关键在于浮点参数值,其范围必须在 0.0 到 1.0 之间。在这个例子中,透明度设置为 0.50F,意味着形状是 50% 透明的,绘制形状时,其下方的内容会部分显示出来。

3.2 使用渐变填充

除了使用纯色,还可以指定渐变填充,通过 GradientPaint 类将两种颜色混合。 GradientPaint 类的构造函数如下表所示:
| 构造函数 | 描述 |
| — | — |
| GradientPaint(float x1, float y1, Color c1, float x2, float y2, Color c2) | 创建一个渐变,点 (x1, y1) 的颜色为 c1,点 (x2, y2) 的颜色为 c2,两点之间的颜色平滑过渡。超出 (x1, y1) 的所有点颜色为 c1,超出 (x2, y2) 的所有点颜色为 c2 |
| GradientPaint(Point2D p1, Color c1, Point2D p2, Color c2) | 创建一个渐变,点 p1 的颜色为 c1,点 p2 的颜色为 c2,两点之间的颜色平滑过渡。超出 p1 的所有点颜色为 c1,超出 p2 的所有点颜色为 c2 |
| GradientPaint(float x1, float y1, Color c1, float x2, float y2, Color c2, boolean cyclic) | 与第一个构造函数相同,但如果 cyclic 参数为 true ,渐变模式将在两点之外无限重复 |
| GradientPaint(Point2D p1, Color c1, Point2D p2, Color c2, boolean cyclic) | 与第二个构造函数相同,但如果 cyclic 参数为 true ,渐变模式将在两点之外无限重复 |

以下是一个设置从洋红色到黄色渐变填充的示例:

GradientPaint gp = new GradientPaint(0, 0, Color.MAGENTA, 0, 100, Color.YELLOW);

选择两个颜色点位置的建议如下:
- 这些点是相对于组件的左上角,而不是要填充的形状。通常希望两个点都位于或靠近要绘制形状的边缘。
- 最简单的方法是创建名为 x y width height 的变量,并使用这些变量创建形状和渐变填充。
- 如果希望第一种颜色在顶部,第二种颜色在底部,第一个点使用 (x, y) ,第二个点使用 (x, y + height)
- 如果希望第一种颜色在左侧,第二种颜色在右侧,第一个点使用 (x, y) ,第二个点使用 (x + width, y)
- 每个点都会被绘制为指定的完整颜色。如果希望在渐变开始之前对象边缘有纯色带,可以选择在对象内部的点,例如 (10, 10) (width - 10, height - 10)
- 如果使用第三个或第四个构造函数并将 cyclic 参数指定为 true ,渐变模式会重复。此时应选择更靠近的点,以便在对象内部看到重复效果。例如,如果对象宽度为 150,可以选择 (0, 0) (0, 50) 这样的点,使渐变在对象内重复三次。

下表展示了使用 GradientPaint 类创建的四种不同渐变填充示例,每个矩形的大小为 100 x 100,同时列出了每个填充的点相对于 x y width height 的位置,每个填充的点 1 颜色为黑色,点 2 颜色为白色:
| 渐变填充名称 | 点 1(黑色) | 点 2(白色) |
| — | — | — |
| gp1 | x, y | x, y + height |
| gp2 | x, y | x + width, y |
| gp3 | x, y + 35 | x, y + height + 35 |
| gp4 | x + 35, y + 35 | x + width - 35, y + height - 35 |

graph TD;
    A[开始] --> B[选择填充方式];
    B --> C{纯色填充};
    C -- 是 --> D[setPaint 设置颜色];
    D --> E[fill 填充形状];
    C -- 否 --> F{透明填充};
    F -- 是 --> G[setComposite 设置透明度];
    G --> E;
    F -- 否 --> H[创建 GradientPaint 对象];
    H --> I[根据建议选择点位置];
    I --> J[设置渐变填充];
    J --> E;
    E --> K[结束];

综上所述,Java 2D 提供了丰富的图形绘制和填充功能,通过掌握获取图形上下文、创建各种形状以及不同的填充方式,可以开发出具有吸引力的图形应用程序。无论是简单的线条和矩形,还是复杂的透明形状和渐变填充,都能在 Java 2D 中轻松实现。希望这些知识能帮助你在 Java 图形编程的道路上迈出坚实的一步。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值