在 Java 2D 编程教程的这一部分中,我们将展示一些效果。
泡泡
在第一个示例中,我们将看到不断增长的彩色气泡,它们在屏幕上随机出现和消失。 该示例来自 Java 2D 演示。
BubblesEx.java
package com.zetcode;import java.awt.BasicStroke;import java.awt.Color;import java.awt.Dimension;import java.awt.EventQueue;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.RenderingHints;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.geom.Ellipse2D;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.Timer;class Surface extends JPanelimplements ActionListener {private final Color colors[] = {Color.blue, Color.cyan, Color.green,Color.magenta, Color.orange, Color.pink,Color.red, Color.yellow, Color.lightGray, Color.white};private Ellipse2D.Float[] ellipses;private double esize[];private float estroke[];private double maxSize = 0;private final int NUMBER_OF_ELLIPSES = 25;private final int DELAY = 30;private final int INITIAL_DELAY = 150;private Timer timer;public Surface() {initSurface();initEllipses();initTimer();}private void initSurface() {setBackground(Color.black);ellipses = new Ellipse2D.Float[NUMBER_OF_ELLIPSES];esize = new double[ellipses.length];estroke = new float[ellipses.length];}private void initEllipses() {int w = 350;int h = 250;maxSize = w / 10;for (int i = 0; i < ellipses.length; i++) {ellipses[i] = new Ellipse2D.Float();posRandEllipses(i, maxSize * Math.random(), w, h);}}private void initTimer() {timer = new Timer(DELAY, this);timer.setInitialDelay(INITIAL_DELAY);timer.start();}private void posRandEllipses(int i, double size, int w, int h) {esize[i] = size;estroke[i] = 1.0f;double x = Math.random() * (w - (maxSize / 2));double y = Math.random() * (h - (maxSize / 2));ellipses[i].setFrame(x, y, size, size);}private void doStep(int w, int h) {for (int i = 0; i < ellipses.length; i++) {estroke[i] += 0.025f;esize[i]++;if (esize[i] > maxSize) {posRandEllipses(i, 1, w, h);} else {ellipses[i].setFrame(ellipses[i].getX(), ellipses[i].getY(),esize[i], esize[i]);}}}private void drawEllipses(Graphics2D g2d) {for (int i = 0; i < ellipses.length; i++) {g2d.setColor(colors[i % colors.length]);g2d.setStroke(new BasicStroke(estroke[i]));g2d.draw(ellipses[i]);}}private void doDrawing(Graphics g) {Graphics2D g2d = (Graphics2D) g.create();RenderingHints rh= new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);rh.put(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);g2d.setRenderingHints(rh);Dimension size = getSize();doStep(size.width, size.height);drawEllipses(g2d);g2d.dispose();}@Overridepublic void paintComponent(Graphics g) {super.paintComponent(g);doDrawing(g);}@Overridepublic void actionPerformed(ActionEvent e) {repaint();}}public class BubblesEx extends JFrame {public BubblesEx() {initUI();}private void initUI() {add(new Surface());setTitle("Bubbles");setSize(350, 250);setLocationRelativeTo(null);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}public static void main(String[] args) {EventQueue.invokeLater(new Runnable() {@Overridepublic void run() {BubblesEx ex = new BubblesEx();ex.setVisible(true);}});}}
这是泡泡的示例。
private final Color colors[] = {Color.blue, Color.cyan, Color.green,Color.magenta, Color.orange, Color.pink,Color.red, Color.yellow, Color.lightGray, Color.white};
这些颜色用于绘制气泡。
private void initSurface() {setBackground(Color.black);ellipses = new Ellipse2D.Float[NUMBER_OF_ELLIPSES];esize = new double[ellipses.length];estroke = new float[ellipses.length];}
initSurface()方法为面板设置黑色背景。 我们创建三个数组。 椭圆数组(椭圆是椭圆的一种特殊情况),每个椭圆大小的数组以及椭圆描边的数组。 动画期间,气泡的大小和笔触都会增加。
private void initEllipses() {int w = 350;int h = 250;maxSize = w / 10;for (int i = 0; i < ellipses.length; i++) {ellipses[i] = new Ellipse2D.Float();posRandEllipses(i, maxSize * Math.random(), w, h);}}
ellipses数组填充有椭圆对象。 posRandEllipses()方法将椭圆对象随机放置在窗口上。 椭圆的初始大小也是随机选择的。
private void initTimer() {timer = new Timer(DELAY, this);timer.setInitialDelay(INITIAL_DELAY);timer.start();}
将创建并启动一个计时器对象。 用于创建动画。
private void posRandEllipses(int i, double size, int w, int h) {esize[i] = size;estroke[i] = 1.0f;double x = Math.random() * (w - (maxSize / 2));double y = Math.random() * (h - (maxSize / 2));ellipses[i].setFrame(x, y, size, size);}
posRandEllipses()方法将椭圆随机放置在窗口上。 esize和estroke数组填充有值。 setFrame()方法设置椭圆框架矩形的位置和大小。
private void doStep(int w, int h) {for (int i = 0; i < ellipses.length; i++) {estroke[i] += 0.025f;esize[i]++;if (esize[i] > maxSize) {posRandEllipses(i, 1, w, h);} else {ellipses[i].setFrame(ellipses[i].getX(), ellipses[i].getY(),esize[i], esize[i]);}}}
动画包括步骤。 在每个步骤中,我们增加每个椭圆的笔触和大小值。 气泡达到最大大小后,将其重置为最小大小,并在面板上随机重新放置。 否则,将显示增加的值。
private void drawEllipses(Graphics2D g2d) {for (int i = 0; i < ellipses.length; i++) {g2d.setColor(colors[i % colors.length]);g2d.setStroke(new BasicStroke(estroke[i]));g2d.draw(ellipses[i]);}}
drawEllipses()方法从面板上的数组绘制所有椭圆。
Dimension size = getSize();doStep(size.width, size.height);
在doDrawing()方法中,我们计算面板的大小。 如果调整窗口大小,气泡将随机分布在窗口的整个区域。
@Overridepublic void actionPerformed(ActionEvent e) {repaint();}
计时器对象以指定的时间间隔触发动作事件。 repaint()方法重新绘制面板组件。

图:泡泡
星星
下一个示例显示了一个旋转和缩放的星星。
StarDemoEx.java
package com.zetcode;import java.awt.EventQueue;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.RenderingHints;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.geom.GeneralPath;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.Timer;class Surface extends JPanelimplements ActionListener {private final int points[][] = {{0, 85}, {75, 75}, {100, 10}, {125, 75},{200, 85}, {150, 125}, {160, 190}, {100, 150},{40, 190}, {50, 125}, {0, 85}};private Timer timer;private double angle = 0;private double scale = 1;private double delta = 0.01;private final int DELAY = 10;public Surface() {initTimer();}private void initTimer() {timer = new Timer(DELAY, this);timer.start();}private void doDrawing(Graphics g) {int h = getHeight();int w = getWidth();Graphics2D g2d = (Graphics2D) g.create();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);g2d.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);g2d.translate(w / 2, h / 2);GeneralPath star = new GeneralPath();star.moveTo(points[0][0], points[0][1]);for (int k = 1; k < points.length; k++) {star.lineTo(points[k][0], points[k][1]);}g2d.rotate(angle);g2d.scale(scale, scale);g2d.fill(star);g2d.dispose();}@Overridepublic void paintComponent(Graphics g) {super.paintComponent(g);doDrawing(g);}private void step() {if (scale < 0.01) {delta = -delta;} else if (scale > 0.99) {delta = -delta;}scale += delta;angle += 0.01;}@Overridepublic void actionPerformed(ActionEvent e) {step();repaint();}}public class StarDemoEx extends JFrame {public StarDemoEx() {initUI();}private void initUI() {add(new Surface());setTitle("Star");setSize(420, 250);setLocationRelativeTo(null);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}public static void main(String[] args) {EventQueue.invokeLater(new Runnable() {@Overridepublic void run() {StarDemoEx ex = new StarDemoEx();ex.setVisible(true);}});}}
在此演示中,我们有一颗星星。 星星旋转生长然后收缩。
private final int points[][] = {{0, 85}, {75, 75}, {100, 10}, {125, 75},{200, 85}, {150, 125}, {160, 190}, {100, 150},{40, 190}, {50, 125}, {0, 85}};
这些点用于绘制星形。
private double angle = 0;private double scale = 1;private double delta = 0.01;
当我们旋转星星时使用angle。 scale因子确定星星的大小。 最后,delta因子是刻度的变化量。
g2d.translate(w / 2, h / 2);
使用translate()方法将坐标系移到窗口的中间。
GeneralPath star = new GeneralPath();star.moveTo(points[0][0], points[0][1]);for (int k = 1; k < points.length; k++) {star.lineTo(points[k][0], points[k][1]);}
GeneralPath用于创建星形。 通过moveTo()方法将第一点添加到路径。 通过lineTo()方法添加星星的后续点。
g2d.rotate(angle);g2d.scale(scale, scale);
我们执行旋转和缩放操作。
g2d.fill(star);
fill()方法填充星形的内部。
if (scale < 0.01) {delta = -delta;} else if (scale > 0.99) {delta = -delta;}
该代码控制星的收缩和增长量。
泡芙
接下来,我们显示粉扑效果。 这种效果在 Flash 动画或电影介绍中很常见。 文本在屏幕上逐渐增长,一段时间后它逐渐消失。
PuffEx.java
package com.zetcode;import java.awt.AlphaComposite;import java.awt.Dimension;import java.awt.EventQueue;import java.awt.Font;import java.awt.FontMetrics;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.RenderingHints;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.Timer;class Surface extends JPanelimplements ActionListener {private Timer timer;private int x = 1;private float alpha = 1;private final int DELAY = 15;private final int INITIAL_DELAY = 200;public Surface() {initTimer();}private void initTimer() {timer = new Timer(DELAY, this);timer.setInitialDelay(INITIAL_DELAY);timer.start();}private void doDrawing(Graphics g) {Graphics2D g2d = (Graphics2D) g.create();RenderingHints rh =new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);rh.put(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);g2d.setRenderingHints(rh);Font font = new Font("Dialog", Font.PLAIN, x);g2d.setFont(font);FontMetrics fm = g2d.getFontMetrics();String s = "ZetCode";Dimension size = getSize();int w = (int) size.getWidth();int h = (int) size.getHeight();int stringWidth = fm.stringWidth(s);AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);g2d.setComposite(ac);g2d.drawString(s, (w - stringWidth) / 2, h / 2);g2d.dispose();}@Overridepublic void paintComponent(Graphics g) {super.paintComponent(g);doDrawing(g);}private void step() {x += 1;if (x > 40)alpha -= 0.01;if (alpha <= 0.01)timer.stop();}@Overridepublic void actionPerformed(ActionEvent e) {step();repaint();}}public class PuffEx extends JFrame {public PuffEx() {initUI();}private void initUI() {setTitle("Puff");add(new Surface());setSize(400, 300);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLocationRelativeTo(null);}public static void main(String[] args) {EventQueue.invokeLater(new Runnable() {@Overridepublic void run() {PuffEx ex = new PuffEx();ex.setVisible(true);}});}}
该示例在窗口上绘制了一个不断增长的文本,从某个角度看,该文本变得越来越透明,直到看不见为止。
Font font = new Font("Dialog", Font.PLAIN, x);g2d.setFont(font);
这是我们用于文本的字体。
FontMetrics fm = g2d.getFontMetrics();
getFontMetrics()返回FontMetrics类。 该类存储有关在特定屏幕上呈现特定字体的信息。
int stringWidth = fm.stringWidth(s);
我们使用FontMetrics对象的stringWidth()方法来获取字符串的宽度。
AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);g2d.setComposite(ac);
在这里,我们设置所绘制文本的透明度。
g2d.drawString(s, (w - stringWidth) / 2, h / 2);
此代码行在窗口的(水平)中间绘制字符串。
if (x > 40)alpha -= 0.01;
琴弦高于 40 点后,琴弦开始褪色。
在 Java 2D 教程的这一部分中,我们做了一些视觉效果。
