31、Java 图形绘制与动画制作全解析

Java图形绘制与动画制作全解析

Java 图形绘制与动画制作全解析

1. Java 图形绘制基础

在 Java 中,我们可以利用多边形和其他形状来绘制图形。虽然加载像 .GIF 和 .JPG 这样的图像文件似乎更简单,但使用多边形绘制图形有两个显著优势:
- 速度快 :即使是小图标,加载和显示图像文件也比绘制一系列多边形要慢。
- 可缩放性好 :通过改变创建多边形的参数,能轻松改变整个图像的大小,且比图像文件缩放更快、效果更好。

以下是一个绘制特定形状的代码示例:

// finish sign
comp2D.setColor(Color.red);
GeneralPath sign3 = new GeneralPath();
sign3.moveTo(110F, 78F);
sign3.lineTo(321F, 289F);
sign3.lineTo(290F, 317F);
sign3.lineTo(81F, 107F);
sign3.closePath();
comp2D.fill(sign3);

编译并在命令行运行该程序后,输出应类似于特定的图形。

2. 绘制饼图

接下来,我们将创建一个名为 PiePanel 的图形用户界面组件来显示饼图。 PiePanel JPanel 的子类,使用它的程序需按以下步骤操作:
1. 使用构造方法 PiePanel(int) 创建 PiePanel 对象,参数指定饼图的切片数量。
2. 调用对象的 addSlice(Color, float) 方法为每个切片指定颜色和值。

例如,以下是使用 PiePanel 表示美国学生贷款还款数据的代码:

PiePanel loans = new PiePanel(4);
loans.addSlice(Color.green, 101F);
loans.addSlice(Color.yellow, 68F);
loans.addSlice(Color.blue, 91F);
loans.addSlice(Color.red, 25F);
贷款状态 金额(十亿美元)
学生已偿还金额 101
在校学生贷款金额 68
还款中学生贷款金额 91
违约学生贷款金额 25

为了实现 PiePanel ,我们还需要一个辅助类 PieSlice 来表示每个切片:

class PieSlice {
    Color color = Color.lightGray;
    float size = 0;
    PieSlice(Color pColor, float pSize) {
        color = pColor;
        size = pSize;
    }
}

PiePanel 类的完整代码如下:

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

public class PiePanel extends JPanel {
    private PieSlice[] slice;
    private int current = 0;
    private float totalSize = 0;
    private Color background;

    public PiePanel(int sliceCount) {
        slice = new PieSlice[sliceCount];
        background = getBackground();
    }

    public void addSlice(Color sColor, float sSize) {
        if (current <= slice.length) {
            slice[current] = new PieSlice(sColor, sSize);
            totalSize += sSize;
            current++;
        }
    }

    public void paintComponent(Graphics comp) {
        super.paintComponent(comp);
        Graphics2D comp2D = (Graphics2D) comp;
        int width = getSize().width - 10;
        int height = getSize().height - 15;
        int xInset = 5;
        int yInset = 5;
        if (width < 5)
            xInset = width;
        if (height < 5)
            yInset = height;
        comp2D.setColor(background);
        comp2D.fillRect(0, 0, getSize().width, getSize().height);
        comp2D.setColor(Color.lightGray);
        Ellipse2D.Float pie = new Ellipse2D.Float(
            xInset, yInset, width, height);
        comp2D.fill(pie);
        float start = 0;
        for (int i = 0; i < slice.length; i++) {
            float extent = slice[i].size * 360F / totalSize;
            comp2D.setColor(slice[i].color);
            Arc2D.Float drawSlice = new Arc2D.Float(
                xInset, yInset, width, height, start, extent,
                Arc2D.Float.PIE);
            start += extent;
            comp2D.fill(drawSlice);
        }
    }
}

为了测试 PiePanel ,我们创建一个使用它的小程序 PieApplet

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

public class PieApplet extends JApplet {
    Color uneasyBeingGreen = new Color(0xCC, 0xCC, 0x99);
    Color zuzusPetals = new Color(0xCC, 0x66, 0xFF);
    Color zootSuit = new Color(0x66, 0x66, 0x99);
    Color sweetHomeAvocado = new Color(0x66, 0x99, 0x66);
    Color shrinkingViolet = new Color(0x66, 0x66, 0x99);
    Color miamiNice = new Color(0x33, 0xFF, 0xFF);
    Color inBetweenGreen = new Color(0x00, 0x99, 0x66);
    Color norwegianBlue = new Color(0x33, 0xCC, 0xCC);
    Color purpleRain = new Color(0x66, 0x33, 0x99);
    Color freckle = new Color (0x99, 0x66, 0x33);

    public void init() {
        Container pane = getContentPane();
        PiePanel pie = new PiePanel(10);
        pie.addSlice(uneasyBeingGreen, 1284);
        pie.addSlice(zuzusPetals, 1046);
        pie.addSlice(zootSuit, 281);
        pie.addSlice(sweetHomeAvocado, 232);
        pie.addSlice(shrinkingViolet, 176);
        pie.addSlice(miamiNice, 148);
        pie.addSlice(inBetweenGreen, 143);
        pie.addSlice(norwegianBlue, 133);
        pie.addSlice(purpleRain,130);
        pie.addSlice(freckle, 127);
        pane.add(pie);
        setContentPane(pane);
    }
}

同时,创建一个简单的网页 PieApplet.html 来包含这个小程序:

<applet code="PieApplet.class" width="300" height="200">
</applet>

这个小程序展示了 10 个国家的人口数据饼图。当调整小程序窗口大小时,饼图会根据新尺寸重新绘制,因为其 (x,y)、高度和宽度变量基于 PiePanel 的大小。

3. 常见问题解答
  • 如何绘制顺时针的弧线?
    可以通过将弧线大小指定为负数来实现。例如:
Arc2D.Float smile = new Arc2D.Float(35F, 20F, 15F, 20F,
    0F, -90F, Arc2D.Float.OPEN);
  • Ellipses.Float 构造方法中指定的 (x,y) 坐标是什么?
    这些坐标代表椭圆或圆的最小 x 值和最小 y 值,相当于围绕它们绘制的无形矩形的左上角坐标。
4. 小测验
  1. 在程序中绘制图形前,使用什么方法更改当前颜色?
    • a. shiftColor()
    • b. setColor()
    • c. Could you repeat the question?
      答案:b。可以使用 setBackground() 方法设置小程序的背景颜色,使用 Graphics 类的 setColor() 方法选择当前颜色。
  2. 如果想使用组件的高度和宽度来确定绘制图形的大小,可以使用什么?
    • a. A ruler and a friend who’s good at math
    • b. getHeight() and getWidth()
    • c. getSize().height and getSize().width
      答案:c。
  3. 第一个示例程序关注的社会问题是什么?
    • a. Overpopulation
    • b. Defaulted student loans
    • c. People who compress air in places with signs that clearly indicate it is prohibited, especially restaurants
      答案:c。不过人口过剩也是个大问题。
5. 活动建议
  • 创建一个 PieApplet 类的版本,将颜色值和饼图切片值作为参数,而不是硬编码在小程序的源代码中。
  • 创建一个 Sign 类的版本,使其形状能根据面板的可用空间进行调整,就像 PiePanel 组件那样。
6. Java 动画制作

在 Java 中,我们还可以实现动画效果。动画的基本原理是在特定位置绘制图像,移动图像位置,然后让计算机在新位置重新绘制图像。

以下是一个使用一系列图像文件创建动画的示例程序 Animate.java

import java.awt.*;

public class Animate extends javax.swing.JApplet
    implements Runnable {
    Image[] picture = new Image[6];
    int totalPictures = 0;
    int current = 0;
    Thread runner;
    int pause = 500;

    public void init() {
        for (int i = 0; i < 6; i++) {
            String imageText = null;
            imageText = getParameter("image"+i);
            if (imageText != null) {
                totalPictures++;
                picture[i] = getImage(getCodeBase(), imageText);
            } else
                break;
        }
        String pauseText = null;
        pauseText = getParameter("pause");
        if (pauseText != null) {
            pause = Integer.parseInt(pauseText);
        }
    }

    public void paint(Graphics screen) {
        super.paint(screen);
        Graphics2D screen2D = (Graphics2D) screen;
        if (picture[current] != null)
            screen2D.drawImage(picture[current], 0, 0, this);
    }

    public void start() {
        if (runner == null) {
            runner = new Thread(this);
            runner.start();
        }
    }

    public void run() {
        Thread thisThread = Thread.currentThread();
        while (runner == thisThread) {
            repaint();
            current++;
            if (current >= totalPictures)
                current = 0;
            try {
                Thread.sleep(pause);
            } catch (InterruptedException e) { }
        }
    }

    public void stop() {
        if (runner != null) {
            runner = null;
        }
    }
}

这个程序使用线程来处理动画,通过 Thread.sleep() 方法控制每个图像的显示时间。动画的图像通过网页参数获取,参数名从 image0 开始,最多可显示 6 个图像。动画速度由 pause 参数指定。

7. 加载和显示图像

在 Java 中,使用 getImage() 方法加载图像,该方法需要两个参数:包含图像文件的 Web 地址或文件夹,以及图像文件名。例如:

Image turtlePicture = getImage(getCodeBase(), "Mertle.gif");

paint() 方法中,使用 drawImage() 方法显示图像。为了避免动画闪烁,该程序的 paint() 方法不调用其父类的 paint() 方法。

8. 总结

通过本文,我们学习了 Java 中图形绘制和动画制作的基础知识。图形绘制方面,使用多边形绘制图形具有速度快和可缩放性好的优势;动画制作方面,利用线程和图像加载方法可以实现简单的动画效果。希望这些知识能帮助你在 Java 编程中创造出更丰富的多媒体应用。

Java 图形绘制与动画制作全解析

9. 动画实现原理深入剖析

动画的实现依赖于线程的运用,这是因为动画通常是一个持续的过程,需要在程序响应用户输入的同时,独立地控制图像的显示和切换。在 Animate 程序中,线程的使用尤为关键,它使得动画的各个环节能够有条不紊地进行。以下是对 Animate 程序中线程相关部分的详细剖析:

import java.awt.*;

public class Animate extends javax.swing.JApplet
    implements Runnable {
    Image[] picture = new Image[6];
    int totalPictures = 0;
    int current = 0;
    Thread runner;
    int pause = 500;

    public void init() {
        for (int i = 0; i < 6; i++) {
            String imageText = null;
            imageText = getParameter("image"+i);
            if (imageText != null) {
                totalPictures++;
                picture[i] = getImage(getCodeBase(), imageText);
            } else
                break;
        }
        String pauseText = null;
        pauseText = getParameter("pause");
        if (pauseText != null) {
            pause = Integer.parseInt(pauseText);
        }
    }

    public void start() {
        if (runner == null) {
            runner = new Thread(this);
            runner.start();
        }
    }

    public void run() {
        Thread thisThread = Thread.currentThread();
        while (runner == thisThread) {
            repaint();
            current++;
            if (current >= totalPictures)
                current = 0;
            try {
                Thread.sleep(pause);
            } catch (InterruptedException e) { }
        }
    }

    public void stop() {
        if (runner != null) {
            runner = null;
        }
    }
}
  • init() 方法 :该方法负责初始化动画所需的图像和暂停时间。通过循环遍历参数,获取图像文件名并加载图像,同时根据 pause 参数设置动画的暂停时间。
  • start() 方法 :当 runner 线程为空时,创建一个新的线程并启动它。这确保了动画在小程序启动时开始运行。
  • run() 方法 :这是动画的核心部分,使用 while 循环不断执行以下操作:
    1. 调用 repaint() 方法,请求重绘屏幕,以显示新的图像。
    2. 递增 current 变量,指向下一个要显示的图像。
    3. 如果 current 超过了总图像数,将其重置为 0,实现循环显示。
    4. 使用 Thread.sleep(pause) 方法暂停一段时间,控制动画的速度。
  • stop() 方法 :当小程序停止时,将 runner 线程置为 null ,停止动画的运行。
10. 动画优化策略

为了提升动画的性能和视觉效果,我们可以采取以下优化策略:

  • 减少闪烁 :如前文所述,为避免动画闪烁, Animate 程序的 paint() 方法不调用其父类的 paint() 方法。此外,还可以使用双缓冲技术,先在内存中绘制整个图像,然后一次性将其显示在屏幕上,减少屏幕刷新的次数。
  • 动态调整图像数量 :可以修改 Animate 程序,使其能够根据实际情况动态调整可显示的图像数量,而不是固定为 6 个。这样可以增加程序的灵活性。
  • 优化线程管理 :在动画停止时,确保线程资源被正确释放,避免内存泄漏。可以在 stop() 方法中添加更多的清理代码,确保线程安全退出。
11. 图形绘制与动画的综合应用案例

以下是一个综合应用图形绘制和动画的案例,展示了如何在一个程序中同时实现静态图形绘制和动态动画效果。

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

public class CombinedApplet extends JApplet implements Runnable {
    // 图形绘制相关变量
    private PiePanel pie;
    // 动画相关变量
    Image[] pictures = new Image[3];
    int totalPics = 0;
    int currentPic = 0;
    Thread animator;
    int delay = 300;

    public void init() {
        // 初始化图形绘制
        Container pane = getContentPane();
        pie = new PiePanel(3);
        pie.addSlice(Color.red, 100);
        pie.addSlice(Color.green, 200);
        pie.addSlice(Color.blue, 150);
        pane.add(pie);

        // 初始化动画
        for (int i = 0; i < 3; i++) {
            String imgText = getParameter("img" + i);
            if (imgText != null) {
                totalPics++;
                pictures[i] = getImage(getCodeBase(), imgText);
            }
        }
        String delayText = getParameter("delay");
        if (delayText != null) {
            delay = Integer.parseInt(delayText);
        }
    }

    public void start() {
        if (animator == null) {
            animator = new Thread(this);
            animator.start();
        }
    }

    public void run() {
        Thread thisThread = Thread.currentThread();
        while (animator == thisThread) {
            repaint();
            currentPic++;
            if (currentPic >= totalPics) {
                currentPic = 0;
            }
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) { }
        }
    }

    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        if (pictures[currentPic] != null) {
            g2d.drawImage(pictures[currentPic], 200, 200, this);
        }
    }

    public void stop() {
        if (animator != null) {
            animator = null;
        }
    }
}

这个程序在一个小程序中同时实现了饼图的绘制和动画效果。在 init() 方法中,初始化了饼图和动画所需的图像;在 run() 方法中,实现了动画的循环显示;在 paint() 方法中,绘制当前的动画图像。

12. 流程图展示

下面是 Animate 程序的执行流程图:

graph TD;
    A[小程序启动] --> B[init()方法];
    B --> C{是否有图像参数};
    C -- 是 --> D[加载图像];
    C -- 否 --> E[结束加载];
    D --> F{是否有pause参数};
    F -- 是 --> G[设置暂停时间];
    F -- 否 --> H[使用默认暂停时间];
    B --> I[start()方法];
    I --> J[启动线程];
    J --> K[run()方法];
    K --> L[调用repaint()];
    L --> M[显示图像];
    M --> N[递增current];
    N --> O{current是否超过总图像数};
    O -- 是 --> P[重置current为0];
    O -- 否 --> Q[继续];
    P --> Q;
    Q --> R[Thread.sleep()];
    R --> L;
    A --> S[stop()方法];
    S --> T[停止线程];
13. 总结与展望

通过本文的学习,我们全面了解了 Java 中图形绘制和动画制作的相关知识。从使用多边形绘制图形到创建复杂的动画效果,我们掌握了多种技术和方法。图形绘制方面,多边形的使用为我们提供了快速、可缩放的图形解决方案;动画制作方面,线程的运用使得我们能够实现流畅的动画效果。

在未来的开发中,我们可以进一步拓展这些知识,结合更多的 Java 特性,如事件处理、网络编程等,创造出更加丰富和交互性强的多媒体应用。例如,开发一个基于网络的动画游戏,让用户能够实时参与和互动;或者创建一个动态的图形报表系统,根据实时数据更新图形显示。希望大家能够不断探索和实践,将这些知识运用到实际项目中,创造出更优秀的 Java 程序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值