原文: http://zetcode.com/tutorials/javaswingtutorial/painting/

Swing 的绘图系统能够渲染向量图形,图像和轮廓基于字体的文本。

Tweet

当我们想要更改或增强现有的小部件,或者要从头开始创建自定义小部件时,应用中需要绘图。要进行绘图,我们使用 Swing 工具箱提供的绘图 API。

绘图是在paintComponent()方法中完成的。 在绘图过程中,我们使用Graphics2D对象。

Swing 2D 向量图形

有两种不同的计算机图形:向量图形和栅格图形。 栅格图形将图像表示为像素的集合。 向量图形是使用诸如点,线,曲线或多边形之类的几何图元来表示图像。 这些基元是使用数学方程式创建的。

两种类型的计算机图形都有优点和缺点。 向量图形优于栅格的优点是:

  • 较小的大小
  • 无限放大的能力
  • 移动,缩放,填充或旋转不会降低图像质量

基本类型

  • 线
  • 折线
  • 多边形
  • 圆圈
  • 椭圆
  • 样条

Swing 绘制点

最简单的图形原语是点。 它是窗口上的一个点。 在 Swing 中没有方法可以画点。 要画点,我们使用drawLine()方法。 我们使用一分两次。

PointsEx.java

  1. package com.zetcode;
  2. import javax.swing.JFrame;
  3. import javax.swing.JPanel;
  4. import java.awt.Color;
  5. import java.awt.EventQueue;
  6. import java.awt.Graphics;
  7. import java.awt.Graphics2D;
  8. import java.util.Random;
  9. class DrawPanel extends JPanel {
  10. private void doDrawing(Graphics g) {
  11. var g2d = (Graphics2D) g;
  12. g2d.setColor(Color.blue);
  13. for (int i = 0; i <= 1000; i++) {
  14. var size = getSize();
  15. var insets = getInsets();
  16. int w = size.width - insets.left - insets.right;
  17. int h = size.height - insets.top - insets.bottom;
  18. var r = new Random();
  19. int x = Math.abs(r.nextInt()) % w;
  20. int y = Math.abs(r.nextInt()) % h;
  21. g2d.drawLine(x, y, x, y);
  22. }
  23. }
  24. @Override
  25. public void paintComponent(Graphics g) {
  26. super.paintComponent(g);
  27. doDrawing(g);
  28. }
  29. }
  30. public class PointsEx extends JFrame {
  31. public PointsEx() {
  32. initUI();
  33. }
  34. private void initUI() {
  35. var drawPanel = new DrawPanel();
  36. add(drawPanel);
  37. setSize(350, 250);
  38. setTitle("Points");
  39. setLocationRelativeTo(null);
  40. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  41. }
  42. public static void main(String[] args) {
  43. EventQueue.invokeLater(() -> {
  44. var ex = new PointsEx();
  45. ex.setVisible(true);
  46. });
  47. }
  48. }

有一点很难观察到。 因此,我们在面板表面上随机绘制了 1000 个点。

  1. class DrawPanel extends JPanel {

我们正在自定义绘图面板上绘图,该面板是JPanel组件。 绘图面板稍后将添加到JFrame组件。

  1. @Override
  2. public void paintComponent(Graphics g) {
  3. super.paintComponent(g);
  4. doDrawing(g);
  5. }

自定义绘图是在paintComponent()方法内部执行的,我们将其覆盖。 super.paintComponent()方法调用父类的方法。 准备用于绘图的组件需要做一些必要的工作。 实际图形委托给doDrawing()方法。

  1. var g2d = (Graphics2D) g;

Swing 中的绘制是在Graphics2D对象上完成的。

  1. g2d.setColor(Color.blue);

我们将点涂成蓝色。

  1. var size = getSize();
  2. var insets = getInsets();

窗口的大小包括边框和标题栏。 我们不在那画。

  1. int w = size.width - insets.left - insets.right;
  2. int h = size.height - insets.top - insets.bottom;

在这里,我们计算面积,在此我们将有效地绘制点。

  1. var r = new Random();
  2. int x = Math.abs(r.nextInt()) % w;
  3. int y = Math.abs(r.nextInt()) % h;

我们得到一个上面计算出的区域大小范围内的随机数。

  1. g2d.drawLine(x, y, x, y);

在这里,我们指出了这一点。 如前所述,我们使用drawLine()方法。 我们两次指定相同的点。

Swing 中的绘图 - 图1

图:点

Swing 绘制线

线是简单的图形基元。 它使用两点绘制。

LinesEx.java

  1. package com.zetcode;
  2. import java.awt.BasicStroke;
  3. import java.awt.EventQueue;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import javax.swing.JFrame;
  7. import javax.swing.JPanel;
  8. class DrawPanel extends JPanel {
  9. private void doDrawing(Graphics g) {
  10. var g2d = (Graphics2D) g;
  11. float[] dash1 = {2f, 0f, 2f};
  12. float[] dash2 = {1f, 1f, 1f};
  13. float[] dash3 = {4f, 0f, 2f};
  14. float[] dash4 = {4f, 4f, 1f};
  15. g2d.drawLine(20, 40, 250, 40);
  16. var bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT,
  17. BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f);
  18. var bs2 = new BasicStroke(1, BasicStroke.CAP_BUTT,
  19. BasicStroke.JOIN_ROUND, 1.0f, dash2, 2f);
  20. var bs3 = new BasicStroke(1, BasicStroke.CAP_BUTT,
  21. BasicStroke.JOIN_ROUND, 1.0f, dash3, 2f);
  22. var bs4 = new BasicStroke(1, BasicStroke.CAP_BUTT,
  23. BasicStroke.JOIN_ROUND, 1.0f, dash4, 2f);
  24. g2d.setStroke(bs1);
  25. g2d.drawLine(20, 80, 250, 80);
  26. g2d.setStroke(bs2);
  27. g2d.drawLine(20, 120, 250, 120);
  28. g2d.setStroke(bs3);
  29. g2d.drawLine(20, 160, 250, 160);
  30. g2d.setStroke(bs4);
  31. g2d.drawLine(20, 200, 250, 200);
  32. }
  33. @Override
  34. public void paintComponent(Graphics g) {
  35. super.paintComponent(g);
  36. doDrawing(g);
  37. }
  38. }
  39. public class LinesEx extends JFrame {
  40. public LinesEx() {
  41. initUI();
  42. }
  43. private void initUI() {
  44. var drawPanel = new DrawPanel();
  45. add(drawPanel);
  46. setSize(280, 270);
  47. setTitle("Lines");
  48. setLocationRelativeTo(null);
  49. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  50. }
  51. public static void main(String[] args) {
  52. EventQueue.invokeLater(() -> {
  53. var ex = new LinesEx();
  54. ex.setVisible(true);
  55. });
  56. }
  57. }

在示例中,我们绘制了五条线。 第一行使用默认值绘制。 其他将具有不同的粗细。 使用BasicStroke类创建描边。 它为图形基元的轮廓定义了一组基本的渲染属性。

  1. float[] dash1 = { 2f, 0f, 2f };

在这里,我们创建一个在描边对象中使用的笔划线。

  1. var bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT,
  2. BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f )

这段代码创建了一个笔画。 描边定义线宽,端盖,线连接,斜接限制,笔划线和笔划线阶段。

Swing 中的绘图 - 图2

图:直线

Swing 绘制矩形

要绘制矩形,我们使用drawRect()方法。 要使用当前颜色填充矩形,我们使用fillRect()方法。

RectanglesEx.java

  1. package com.zetcode;
  2. import java.awt.Color;
  3. import java.awt.EventQueue;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import javax.swing.JFrame;
  7. import javax.swing.JPanel;
  8. class DrawPanel extends JPanel {
  9. private void doDrawing(Graphics g) {
  10. var g2d = (Graphics2D) g;
  11. g2d.setColor(new Color(212, 212, 212));
  12. g2d.drawRect(10, 15, 90, 60);
  13. g2d.drawRect(130, 15, 90, 60);
  14. g2d.drawRect(250, 15, 90, 60);
  15. g2d.drawRect(10, 105, 90, 60);
  16. g2d.drawRect(130, 105, 90, 60);
  17. g2d.drawRect(250, 105, 90, 60);
  18. g2d.drawRect(10, 195, 90, 60);
  19. g2d.drawRect(130, 195, 90, 60);
  20. g2d.drawRect(250, 195, 90, 60);
  21. g2d.setColor(new Color(125, 167, 116));
  22. g2d.fillRect(10, 15, 90, 60);
  23. g2d.setColor(new Color(42, 179, 231));
  24. g2d.fillRect(130, 15, 90, 60);
  25. g2d.setColor(new Color(70, 67, 123));
  26. g2d.fillRect(250, 15, 90, 60);
  27. g2d.setColor(new Color(130, 100, 84));
  28. g2d.fillRect(10, 105, 90, 60);
  29. g2d.setColor(new Color(252, 211, 61));
  30. g2d.fillRect(130, 105, 90, 60);
  31. g2d.setColor(new Color(241, 98, 69));
  32. g2d.fillRect(250, 105, 90, 60);
  33. g2d.setColor(new Color(217, 146, 54));
  34. g2d.fillRect(10, 195, 90, 60);
  35. g2d.setColor(new Color(63, 121, 186));
  36. g2d.fillRect(130, 195, 90, 60);
  37. g2d.setColor(new Color(31, 21, 1));
  38. g2d.fillRect(250, 195, 90, 60);
  39. }
  40. @Override
  41. public void paintComponent(Graphics g) {
  42. super.paintComponent(g);
  43. doDrawing(g);
  44. }
  45. }
  46. public class RectanglesEx extends JFrame {
  47. public RectanglesEx() {
  48. initUI();
  49. }
  50. private void initUI() {
  51. var drawPanel = new DrawPanel();
  52. add(drawPanel);
  53. setSize(360, 300);
  54. setTitle("Rectangles");
  55. setLocationRelativeTo(null);
  56. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  57. }
  58. public static void main(String[] args) {
  59. EventQueue.invokeLater(() -> {
  60. var ex = new RectanglesEx();
  61. ex.setVisible(true);
  62. });
  63. }
  64. }

在示例中,我们绘制了九个彩色矩形。

  1. g2d.setColor(new Color(212, 212, 212));
  2. g2d.drawRect(10, 15, 90, 60);
  3. ...

我们将矩形轮廓的颜色设置为柔和的灰色,以免干扰填充颜色。 要绘制矩形的轮廓,我们使用drawRect()方法。 前两个参数是 x 和 y 值。 第三和第四是宽度和高度。

  1. g2d.fillRect(10, 15, 90, 60);

为了用颜色填充矩形,我们使用fillRect()方法。

Swing 中的绘图 - 图3

图:矩形

Swing 使用纹理

纹理是应用于形状的位图图像。 要在 Java 2D 中使用纹理,我们使用TexturePaint类。

TexturesEx.java

  1. package com.zetcode;
  2. import javax.imageio.ImageIO;
  3. import javax.swing.JFrame;
  4. import javax.swing.JOptionPane;
  5. import javax.swing.JPanel;
  6. import java.awt.EventQueue;
  7. import java.awt.Graphics;
  8. import java.awt.Graphics2D;
  9. import java.awt.Rectangle;
  10. import java.awt.TexturePaint;
  11. import java.awt.image.BufferedImage;
  12. import java.io.File;
  13. import java.io.IOException;
  14. class DrawingPanel extends JPanel {
  15. private BufferedImage slate;
  16. private BufferedImage java;
  17. private BufferedImage pane;
  18. public DrawingPanel() {
  19. loadImages();
  20. }
  21. private void loadImages() {
  22. try {
  23. slate = ImageIO.read(new File("src/resources/slate.png"));
  24. java = ImageIO.read(new File("src/resources/java.png"));
  25. pane = ImageIO.read(new File("src/resources/pane.png"));
  26. } catch (IOException ex) {
  27. JOptionPane.showMessageDialog(this,
  28. "Could not load images", "Error", JOptionPane.ERROR_MESSAGE);
  29. System.exit(1);
  30. }
  31. }
  32. private void doDrawing(Graphics g) {
  33. var g2d = (Graphics2D) g.create();
  34. var slateTp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60));
  35. var javaTp = new TexturePaint(java, new Rectangle(0, 0, 90, 60));
  36. var paneTp = new TexturePaint(pane, new Rectangle(0, 0, 90, 60));
  37. g2d.setPaint(slateTp);
  38. g2d.fillRect(10, 15, 90, 60);
  39. g2d.setPaint(javaTp);
  40. g2d.fillRect(130, 15, 90, 60);
  41. g2d.setPaint(paneTp);
  42. g2d.fillRect(250, 15, 90, 60);
  43. g2d.dispose();
  44. }
  45. @Override
  46. public void paintComponent(Graphics g) {
  47. super.paintComponent(g);
  48. doDrawing(g);
  49. }
  50. }
  51. class TexturesEx extends JFrame {
  52. public TexturesEx() {
  53. initUI();
  54. }
  55. private void initUI() {
  56. var drawingPanel = new DrawingPanel();
  57. add(drawingPanel);
  58. setTitle("Textures");
  59. setSize(360, 120);
  60. setLocationRelativeTo(null);
  61. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  62. }
  63. public static void main(String[] args) {
  64. EventQueue.invokeLater(() -> {
  65. var ex = new TexturesEx();
  66. ex.setVisible(true);
  67. });
  68. }
  69. }

在代码示例中,我们用三个不同的纹理填充三个矩形。

  1. private BufferedImage slate;
  2. private BufferedImage java;
  3. private BufferedImage pane;

BufferedImage是存储在内存中的像素矩形。 它是 Swing 中最重要的图像类型之一。 许多 Swing 方法都返回BufferedImage以供使用。

  1. slate = ImageIO.read(new File("src/resources/slate.png"));

在这里,我们使用ImageIO.read()方法将图像读取到缓冲图像中。 它接受File对象并返回BufferedImage

  1. slateTp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60));

我们从缓冲图像中创建一个TexturePaint类。

  1. g2d.setPaint(slateTp);
  2. g2d.fillRect(10, 15, 90, 60);

我们用纹理填充一个矩形。

Swing 中的绘图 - 图4

图:纹理

Swing 使用渐变

在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘图程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。

GradientsEx.java

  1. package com.zetcode;
  2. import java.awt.Color;
  3. import java.awt.EventQueue;
  4. import java.awt.GradientPaint;
  5. import java.awt.Graphics;
  6. import java.awt.Graphics2D;
  7. import javax.swing.JFrame;
  8. import javax.swing.JPanel;
  9. class DrawPanel extends JPanel {
  10. private void doDrawing(Graphics g) {
  11. var g2d = (Graphics2D) g;
  12. var gp1 = new GradientPaint(5, 5,
  13. Color.red, 20, 20, Color.black, true);
  14. g2d.setPaint(gp1);
  15. g2d.fillRect(20, 20, 300, 40);
  16. var gp2 = new GradientPaint(5, 25,
  17. Color.yellow, 20, 2, Color.black, true);
  18. g2d.setPaint(gp2);
  19. g2d.fillRect(20, 80, 300, 40);
  20. var gp3 = new GradientPaint(5, 25,
  21. Color.green, 2, 2, Color.black, true);
  22. g2d.setPaint(gp3);
  23. g2d.fillRect(20, 140, 300, 40);
  24. var gp4 = new GradientPaint(25, 25,
  25. Color.blue, 15, 25, Color.black, true);
  26. g2d.setPaint(gp4);
  27. g2d.fillRect(20, 200, 300, 40);
  28. var gp5 = new GradientPaint(0, 0,
  29. Color.orange, 0, 20, Color.black, true);
  30. g2d.setPaint(gp5);
  31. g2d.fillRect(20, 260, 300, 40);
  32. }
  33. @Override
  34. public void paintComponent(Graphics g) {
  35. super.paintComponent(g);
  36. doDrawing(g);
  37. }
  38. }
  39. public class GradientsEx extends JFrame {
  40. public GradientsEx() {
  41. initUI();
  42. }
  43. private void initUI() {
  44. var drawPanel = new DrawPanel();
  45. add(drawPanel);
  46. setSize(350, 350);
  47. setTitle("Gradients");
  48. setLocationRelativeTo(null);
  49. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  50. }
  51. public static void main(String[] args) {
  52. EventQueue.invokeLater(() -> {
  53. var ex = new GradientsEx();
  54. ex.setVisible(true);
  55. });
  56. }
  57. }

我们的代码示例展示了五个带有渐变的矩形。

  1. var gp4 = new GradientPaint(25, 25,
  2. Color.blue, 15, 25, Color.black, true);

要使用渐变,我们使用 Java Swing 的GradientPaint类。 通过操纵颜色值和起点,终点,我们可以获得不同类型的渐变。

  1. g2d.setPaint(gp5);

调用setPaint()方法激活渐变。

Swing 中的绘图 - 图5

图:渐变

Swing 绘制文字

使用drawString()方法进行绘制。 我们指定我们要绘制的字符串以及文本在窗口区域中的位置。

DrawingTextEx.java

  1. package com.zetcode;
  2. import javax.swing.JFrame;
  3. import javax.swing.JPanel;
  4. import java.awt.EventQueue;
  5. import java.awt.Font;
  6. import java.awt.Graphics;
  7. import java.awt.Graphics2D;
  8. import java.awt.RenderingHints;
  9. class DrawPanel extends JPanel {
  10. private void doDrawing(Graphics g) {
  11. var g2d = (Graphics2D) g;
  12. var rh = new RenderingHints(
  13. RenderingHints.KEY_ANTIALIASING,
  14. RenderingHints.VALUE_ANTIALIAS_ON);
  15. rh.put(RenderingHints.KEY_RENDERING,
  16. RenderingHints.VALUE_RENDER_QUALITY);
  17. g2d.setRenderingHints(rh);
  18. var font = new Font("URW Chancery L", Font.BOLD, 21);
  19. g2d.setFont(font);
  20. g2d.drawString("Not marble, nor the gilded monuments", 20, 30);
  21. g2d.drawString("Of princes, shall outlive this powerful rhyme;", 20, 60);
  22. g2d.drawString("But you shall shine more bright in these contents",
  23. 20, 90);
  24. g2d.drawString("Than unswept stone, besmear'd with sluttish time.",
  25. 20, 120);
  26. g2d.drawString("When wasteful war shall statues overturn,", 20, 150);
  27. g2d.drawString("And broils root out the work of masonry,", 20, 180);
  28. g2d.drawString("Nor Mars his sword, nor war's quick "
  29. + "fire shall burn", 20, 210);
  30. g2d.drawString("The living record of your memory.", 20, 240);
  31. g2d.drawString("'Gainst death, and all oblivious enmity", 20, 270);
  32. g2d.drawString("Shall you pace forth; your praise shall still "
  33. + "find room", 20, 300);
  34. g2d.drawString("Even in the eyes of all posterity", 20, 330);
  35. g2d.drawString("That wear this world out to the ending doom.", 20, 360);
  36. g2d.drawString("So, till the judgment that yourself arise,", 20, 390);
  37. g2d.drawString("You live in this, and dwell in lovers' eyes.", 20, 420);
  38. }
  39. @Override
  40. public void paintComponent(Graphics g) {
  41. super.paintComponent(g);
  42. doDrawing(g);
  43. }
  44. }
  45. public class DrawingTextEx extends JFrame {
  46. public DrawingTextEx() {
  47. initUI();
  48. }
  49. private void initUI() {
  50. var drawPanel = new DrawPanel();
  51. add(drawPanel);
  52. setSize(500, 470);
  53. setTitle("Sonnet55");
  54. setLocationRelativeTo(null);
  55. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  56. }
  57. public static void main(String[] args) {
  58. EventQueue.invokeLater(() -> {
  59. var ex = new DrawingTextEx();
  60. ex.setVisible(true);
  61. });
  62. }
  63. }

在我们的示例中,我们在面板组件上绘制了十四行诗。

  1. var rh = new RenderingHints(
  2. RenderingHints.KEY_ANTIALIASING,
  3. RenderingHints.VALUE_ANTIALIAS_ON);
  4. rh.put(RenderingHints.KEY_RENDERING,
  5. RenderingHints.VALUE_RENDER_QUALITY);
  6. g2d.setRenderingHints(rh);

这段代码是为了使我们的文本看起来更好。 我们使用RenderingHints应用称为抗锯齿的技术。

  1. var font = new Font("URW Chancery L", Font.BOLD, 21);
  2. g2d.setFont(font);

我们为文本选择特定的字体。

  1. g2d.drawString("Not marble, nor the gilded monuments", 20, 30);

这是绘制文本的代码。

Swing 绘制图像

工具包最重要的功能之一就是显示图像的能力。 图像是像素数组。 每个像素代表给定位置的颜色。 我们可以使用JLabel之类的组件来显示图像,也可以使用 Java 2D API 绘制图像。

DrawImageEx.java

  1. package com.zetcode;
  2. import javax.swing.ImageIcon;
  3. import javax.swing.JFrame;
  4. import javax.swing.JPanel;
  5. import java.awt.Dimension;
  6. import java.awt.EventQueue;
  7. import java.awt.Graphics;
  8. import java.awt.Graphics2D;
  9. import java.awt.Image;
  10. class DrawPanel extends JPanel {
  11. private Image myImage;
  12. public DrawPanel() {
  13. initPanel();
  14. }
  15. private void initPanel() {
  16. loadImage();
  17. var dm = new Dimension(myImage.getWidth(null), myImage.getHeight(null));
  18. setPreferredSize(dm);
  19. }
  20. private void loadImage() {
  21. myImage = new ImageIcon("src/resources/icesid.jpg").getImage();
  22. }
  23. private void doDrawing(Graphics g) {
  24. var g2d = (Graphics2D) g;
  25. g2d.drawImage(myImage, 0, 0, null);
  26. }
  27. @Override
  28. public void paintComponent(Graphics g) {
  29. super.paintComponent(g);
  30. doDrawing(g);
  31. }
  32. }
  33. public class DrawImageEx extends JFrame {
  34. public DrawImageEx() {
  35. initUI();
  36. }
  37. private void initUI() {
  38. var drawPanel = new DrawPanel();
  39. add(drawPanel);
  40. setTitle("Image");
  41. pack();
  42. setLocationRelativeTo(null);
  43. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  44. }
  45. public static void main(String[] args) {
  46. EventQueue.invokeLater(() -> {
  47. var ex = new DrawImageEx();
  48. ex.setVisible(true);
  49. });
  50. }
  51. }

本示例将在面板上绘制图像。 图像适合JFrame窗口。

  1. private void initPanel() {
  2. loadImage();
  3. var dm = new Dimension(img.getWidth(null), img.getHeight(null));
  4. setPreferredSize(dm);
  5. }

initPanel()方法中,我们称为loadImage()方法。 我们确定图像大小并设置面板组件的首选大小。 这将与pack()方法一起显示完全适合窗口的图像。

  1. private void loadImage() {
  2. myImage = new ImageIcon("src/resources/icesid.jpg").getImage();
  3. }

该方法从磁盘加载映像。 我们使用ImageIcon类。 此类简化了 Java Swing 中图像的处理。

  1. g2d.drawImage(this.img, 0, 0, null);

使用drawImage()方法绘制图像。

在本章中,我们使用 Java Swing 进行了一些绘图。 请访问 ZetCode 的 Java 2D 教程,以获取有关在 Swing 中绘图的其他信息。