透明度
最后修改于 2023 年 7 月 17 日
在 Java 2D 的这一部分,我们讨论透明度。我们提供了一些基本定义和几种有趣的透明度效果。
透明度解释
透明度是能够看穿物质的性质。理解透明度最简单的方法是想象一块玻璃或水。从技术上讲,光线可以穿过玻璃,这样我们就能看到玻璃后面的物体。
在计算机图形学中,我们可以使用*alpha 混合*来实现透明度效果。Alpha 混合是结合图像和背景以创建部分透明外观的过程。混合过程使用*alpha 通道*。Alpha 通道是图形文件格式中一个 8 位图层,用于表达半透明性(透明度)。每像素的额外八位作为掩码,代表 256 级半透明性。
AlphaComposite
类用于处理 Java 2D 中的透明度。它实现了基本的 alpha 混合规则,用于组合源像素和目标像素,以实现图形和图像的混合和透明度效果。要创建 AlphaComposite
,我们需要提供两个值:规则指示符和 alpha 值。规则指定我们如何组合源像素和目标像素。最常见的是 AlphaComposite.SRC_OVER
。alpha 值可以从 0.0f(完全透明)到 1.0f(完全不透明)。
透明矩形
第一个示例绘制了十个具有不同透明度级别的矩形。
package com.zetcode; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JFrame; import javax.swing.JPanel; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); g2d.setPaint(Color.blue); for (int i = 1; i <= 10; i++) { float alpha = i * 0.1f; AlphaComposite alcom = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha); g2d.setComposite(alcom); g2d.fillRect(50 * i, 20, 40, 40); } g2d.dispose(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class TransparentRectanglesEx extends JFrame { public TransparentRectanglesEx() { initUI(); } private void initUI() { add(new Surface()); setTitle("Transparent rectangles"); setSize(590, 120); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { TransparentRectanglesEx ex = new TransparentRectanglesEx(); ex.setVisible(true); } }); } }
在我们的示例中,我们绘制了 10 个应用了各种透明度级别的蓝色矩形。
float alpha = i * 0.1f;
alpha 值在 for 循环中动态变化。
AlphaComposite alcom = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha);
AlphaComposite.getInstance
方法创建一个 AlphaComposite
对象,该对象具有指定的规则和要乘以源 alpha 的常量 alpha。
g2d.setComposite(alcom);
setComposite
方法为 Graphics2D
对象设置 composite 属性。

淡出演示
在下一个示例中,我们将淡出一个图像。图像将逐渐变得更透明,直到完全不可见。
package com.zetcode; import java.awt.AlphaComposite; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; class Surface extends JPanel implements ActionListener { private Image img; private Timer timer; private float alpha = 1f; private final int DELAY = 40; private final int INITIAL_DELAY = 500; public Surface() { loadImage(); setSurfaceSize(); initTimer(); } private void loadImage() { img = new ImageIcon("mushrooms.jpg").getImage(); } private void setSurfaceSize() { int h = img.getHeight(this); int w = img.getWidth(this); setPreferredSize(new Dimension(w, h)); } private void initTimer() { timer = new Timer(DELAY, this); timer.setInitialDelay(INITIAL_DELAY); timer.start(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); AlphaComposite acomp = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha); g2d.setComposite(acomp); g2d.drawImage(img, 0, 0, null); g2d.dispose(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } private void step() { alpha += -0.01f; if (alpha <= 0) { alpha = 0; timer.stop(); } } @Override public void actionPerformed(ActionEvent e) { step(); repaint(); } } public class FadeOutEx extends JFrame { public FadeOutEx() { initUI(); } private void initUI() { add(new Surface()); pack(); setTitle("Fade out"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { FadeOutEx ex = new FadeOutEx(); ex.setVisible(true); } }); } }
使用 AlphaComposite
,我们逐渐淡出一个面板上的图像。
private void setSurfaceSize() { int h = img.getHeight(this); int w = img.getWidth(this); setPreferredSize(new Dimension(w, h)); }
setSurfaceSize
方法确定图像的大小并将首选大小设置为面板。首选大小与 pack
方法结合使用,将窗口显示得足够大以显示整个图像。
private void initTimer() { timer = new Timer(DELAY, this); timer.setInitialDelay(INITIAL_DELAY); timer.start(); }
initTimer
方法启动一个计时器。计时器在指定的初始延迟后触发操作事件。连续的操作事件在事件之间延迟生成。作为对操作事件的响应,我们将更改 alpha 值并重绘面板。
AlphaComposite acomp = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha); g2d.setComposite(acomp); g2d.drawImage(img, 0, 0, null);
此代码在面板上绘制一个透明度级别不断增加的图像。
private void step() { alpha += -0.01f; if (alpha <= 0) { alpha = 0; timer.stop(); } }
step
方法代表一个淡出周期。alpha
值逐渐减小。请注意,alpha 值不能为负数。当它达到零时,计时器停止。
repaint();
repaint
方法重绘组件。它调用面板组件的 paint
方法,该方法又调用 paintComponent
方法。
等待演示
在此示例中,我们使用透明度效果创建了一个等待演示。我们绘制了 8 条逐渐淡出的线条,营造出一条线条正在移动的错觉。此类效果通常用于通知用户后台正在进行一项耗时任务。例如,在互联网上流式传输视频时。
package com.zetcode; import java.awt.AlphaComposite; import java.awt.BasicStroke; 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 javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; class Surface extends JPanel implements ActionListener { private Timer timer; private int count; private final int INITIAL_DELAY = 200; private final int DELAY = 80; private final int NUMBER_OF_LINES = 8; private final int STROKE_WIDTH = 3; private final double[][] trs = { {0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0}, {1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9}, {0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8}, {0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65}, {0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5}, {0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3}, {0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15}, {0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0} }; 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(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); int width = getWidth(); int height = getHeight(); g2d.setStroke(new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); g2d.translate(width / 2, height / 2); for (int i = 0; i < NUMBER_OF_LINES; i++) { float alpha = (float) trs[count % NUMBER_OF_LINES][i]; AlphaComposite acomp = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha); g2d.setComposite(acomp); g2d.rotate(Math.PI / 4f); g2d.drawLine(0, -10, 0, -40); } g2d.dispose(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } @Override public void actionPerformed(ActionEvent e) { repaint(); count++; } } public class WaitingEx extends JFrame { public WaitingEx() { initUI(); } private void initUI() { add(new Surface()); setTitle("Waiting"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { WaitingEx ex = new WaitingEx(); ex.setVisible(true); } }); } }
我们绘制八条线,每条线有八个不同的 alpha 值。
private final double[][] trs = { ... };
这是一个用于此演示的二维透明度值数组。有 8 行,每行代表一种状态。这 8 条线将连续使用这些值。
g2d.setStroke(new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
我们将线条画得稍粗一些,以便更好地显示。我们用圆角绘制线条。
g2d.rotate(Math.PI/4f); g2d.drawLine(0, -10, 0, -40);
此代码绘制了八条线中的每一条。rotate
方法用于沿圆周旋转线条。

在 Java 2D 教程的这一部分,我们讨论了透明度。