如何创建半透明和成形的窗口

从 Java 平台标准版 6(Java SE 6)Update 10 版本开始,您可以向 Swing 应用程序中添加半透明和成形的窗口。此页面包含以下主题:

Supported Capabilities

此功能是 JDK 7 版本中公共 AWT 程序包的一部分,采用以下三种形式:

  • 您可以创建具有统一半透明性的窗口,其中每个像素具有相同的半透明(或 alpha)值。以下屏幕截图显示了具有 45%半透明性的窗口。

半透明的窗口

Try this:

单击启动按钮以使用Java™Web 开始运行 TranslucentWindowDemo 示例。此示例需要JDK 7或更高版本。另外,要自己编译和运行示例,请查阅example index

  • 您可以创建具有“每像素”半透明性的窗口,其中每个像素都有自己的 alpha 值。例如,使用此功能,您可以创建一个窗口,该窗口通过在 alpha 值中定义渐变来逐渐消失。以下屏幕截图显示了一个从顶部(完全透明)到底部(完全不透明)具有渐变半透明性的窗口。

具有每个像素半透明性的窗口。

Try this:

单击启动按钮以使用Java™Web 开始运行 GradientTranslucentWindowDemo 示例。此示例需要JDK 7或更高版本。另外,要自己编译和运行示例,请查阅example index

  • 您可以使用任何可以定义的Shape对象创建一个窗口。成形窗口可以是不透明的,也可以使用均匀的或按像素的半透明。以下屏幕截图显示了具有 30%半透明性的椭圆形窗口。

椭圆形的窗户。

Try this:

单击启动按钮以使用Java™Web 开始运行 ShapedWindowDemo 示例。此示例需要JDK 7或更高版本。另外,要自己编译和运行示例,请查阅example index

确定平台的功能

并非所有平台都支持所有这些功能。当代码try在不支持这些功能的平台上调用setShapesetOpacity方法时,将引发UnsupportedOperationException异常。因此,最佳实践是首先检查平台是否支持您要实现的功能。 GraphicsDevice类提供了可用于此 Object 的isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency)方法。您将在GraphicsDevice.WindowTranslucency中定义的三个枚举值之一传递给此方法:

  • TRANSLUCENT –基础平台支持具有统一半透明性的窗口,其中每个像素具有相同的 alpha 值。

  • PERPIXEL_TRANSLUCENT –基础平台支持具有逐像素半透明性的窗口。实现消失的窗口需要此功能。

  • PERPIXEL_TRANSPARENT –基础平台支持成形窗口。

GraphicsConfiguration类还提供isTranslucencyCapable方法,以确定给定的GraphicsConfiguration对象是否支持PERPIXEL_TRANSLUCENT半透明。

Version note:

半透明和成形的窗口 API 首先作为私有 API 添加到 Java SE 6 Update 10 发行版中。此功能已移至 JDK 7 版本中的公共 AWT 包中。本教程描述了 JDK 7 版本中可用的 API。有关 Java SE 6 Update 10 发行版中的私有 API 到 JDK 7 发行版中的公共 API 的 Map,请参见Java SE 6 Update 10 API

以下代码显示了如何检查所有三种功能:

import static java.awt.GraphicsDevice.WindowTranslucency.*;

// Determine what the default GraphicsDevice can support.
GraphicsEnvironment ge =
    GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();

boolean isUniformTranslucencySupported =
    gd.isWindowTranslucencySupported(TRANSLUCENT);
boolean isPerPixelTranslucencySupported =
    gd.isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT);
boolean isShapedWindowSupported =
    gd.isWindowTranslucencySupported(PERPIXEL_TRANSPARENT);

Note:

这些功能均无法在全屏 Pattern 下的 Windows 上运行。在全屏 Pattern 下调用任何相关方法都会引发IllegalComponentStateException异常。

如何实现统一的半透明

您可以通过调用Window类中的setOpacity(float)方法来创建一个窗口,使每个像素具有相同的半透明性。传递给此方法的float参数表示窗口的半透明性,其值应介于 0 和 1 之间(含 0 和 1)。数字越小,窗口越透明。还有一个相应的getOpacity方法。

TranslucentWindowDemo.java示例创建一个不透明度为 55%(半透明为 45%)的窗口。如果基础平台不支持半透明窗口,则该示例退出。与不透明度相关的代码以粗体显示。

import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class TranslucentWindowDemo extends JFrame {
    public TranslucentWindowDemo() {
        super("TranslucentWindow");
        setLayout(new GridBagLayout());

        setSize(300,200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Add a sample button.
        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine if the GraphicsDevice supports translucency.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();

        //If translucent windows aren't supported, exit.
        if (!gd.isWindowTranslucencySupported(TRANSLUCENT)) {
            System.err.println(
                "Translucency is not supported");
                System.exit(0);
        }
        
        JFrame.setDefaultLookAndFeelDecorated(true);

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                TranslucentWindowDemo tw = new TranslucentWindowDemo();

                // Set the window to 55% opaque (45% translucent).
                tw.setOpacity(0.55f);

                // Display the window.
                tw.setVisible(true);
            }
        });
    }
}

请注意,按钮也受均匀的半透明性影响。设置不透明度会影响整个窗口,包括该窗口包含的所有组件。

如何实现每像素半透明

创建使用每像素半透明性的窗口涉及在该窗口占据的矩形区域上定义 alpha 值。当像素的 alpha 值为零时,该像素是完全透明的。当像素的 Alpha 值为 255 时,该像素是完全不透明的。当像素的 Alpha 值为 128 时,该像素为 50%半透明,依此类推。在 alpha 值之间创建平滑插值的一种简单方法是使用GradientPaint类。包含的示例使用此方法。

在窗口上调用setBackground(new Color(0,0,0,0))会使软件使用 alpha 值来渲染每个像素的半透明性。实际上,在alpha小于 255 的情况下调用setBackground(new Color(0,0,0,alpha)会安装每个像素的透明度。因此,如果调用setBackground(new Color(0,0,0,128))而不执行其他任何操作,则为每个背景像素渲染的窗口具有 50%的半透明性。但是,如果要创建自己的 alpha 值范围,则很可能希望 alpha 值为 0.

尽管不受公共 API 的禁止,但是您通常会希望在未装饰的窗口上启用每个像素的半透明性。在大多数情况下,在装饰后的窗户上使用逐像素半透明是没有意义的。这样做会禁用装饰,或引起其他与平台有关的副作用。

要确定窗口是否正在使用每个像素的半透明性,可以使用isOpaque方法。

下面是一个示例。首先,这是实现示例所需的步骤:

  • 在窗口上调用setBackground(new Color(0,0,0,0))

  • 创建一个覆盖paintComponent方法的JPanel实例。

  • paintComponent方法中,创建GradientPaint实例。

  • 在此示例中,矩形的顶部的 alpha 值为 0(最透明),底部的 alpha 值为 255(最不透明)。 GradientPaint类从矩形的顶部到底部平滑地插入 alpha 值。

  • GradientPaint实例设置为面板的绘制方法。

这是GradientTranslucentWindowDemo.java示例的代码。如果基础平台不支持每像素半透明,则此示例退出。与创建渐变窗口特别相关的代码以粗体显示。

import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class GradientTranslucentWindowDemo extends JFrame {
    public GradientTranslucentWindowDemo() {
        super("GradientTranslucentWindow");

        setBackground(new Color(0,0,0,0));
        setSize(new Dimension(300,200));
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                if (g instanceof Graphics2D) {
                    final int R = 240;
                    final int G = 240;
                    final int B = 240;

                    Paint p =
                        new GradientPaint(0.0f, 0.0f, new Color(R, G, B, 0),
                            0.0f, getHeight(), new Color(R, G, B, 255), true);
                    Graphics2D g2d = (Graphics2D)g;
                    g2d.setPaint(p);
                    g2d.fillRect(0, 0, getWidth(), getHeight());
                }
            }
        };
        setContentPane(panel);
        setLayout(new GridBagLayout());
        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine what the GraphicsDevice can support.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        boolean isPerPixelTranslucencySupported = 
            gd.isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT);

        //If translucent windows aren't supported, exit.
        if (!isPerPixelTranslucencySupported) {
            System.out.println(
                "Per-pixel translucency is not supported");
                System.exit(0);
        }

        JFrame.setDefaultLookAndFeelDecorated(true);

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                GradientTranslucentWindowDemo gtw = new
                    GradientTranslucentWindowDemo();

                // Display the window.
                gtw.setVisible(true);
            }
        });
    }
}

请注意,该按钮不受逐像素半透明性的影响。设置每个像素的半透明性仅影响背景像素。如果要使窗口仅对背景像素具有均匀的半透明效果,则可以调用setBackground(new Color(0,0,0,alpha)),其中alpha指定所需的半透明性。

如何实现成形窗口

您可以通过调用Window类中的setShape(Shape)方法来创建成形窗口。传递给该方法的Shape参数确定如何裁剪窗口。在窗口上设置形状时,窗口装饰不会重新形成为新形状,因此在未装饰的窗口上设置形状效果最佳。

设置窗口形状的最佳实践是在组件事件侦听器的componentResized方法中调用setShape。这种做法将确保为窗口的实际大小正确计算形状。下面的示例使用这种方法。

ShapedWindowDemo.java示例创建了一个不透明度为 70%的椭圆形窗口。如果基础平台不支持成形窗口,则该示例退出。如果基础平台不支持半透明,则该示例使用标准的不透明窗口。您可以修改此示例以创建一个也使用每个像素半透明的成形窗口。

与塑造窗口有关的代码以粗体显示。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class ShapedWindowDemo extends JFrame {
    public ShapedWindowDemo() {
        super("ShapedWindow");
        setLayout(new GridBagLayout());

        // It is best practice to set the window's shape in
        // the componentResized method.  Then, if the window
        // changes size, the shape will be correctly recalculated.
        addComponentListener(new ComponentAdapter() {
            // Give the window an elliptical shape.
            // If the window is resized, the shape is recalculated here.
            @Override
            public void componentResized(ComponentEvent e) {
                setShape(new Ellipse2D.Double(0,0,getWidth(),getHeight()));
            }
        });

        setUndecorated(true);
        setSize(300,200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine what the GraphicsDevice can support.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        final boolean isTranslucencySupported = 
            gd.isWindowTranslucencySupported(TRANSLUCENT);

        //If shaped windows aren't supported, exit.
        if (!gd.isWindowTranslucencySupported(PERPIXEL_TRANSPARENT)) {
            System.err.println("Shaped windows are not supported");
            System.exit(0);
        }

        //If translucent windows aren't supported, 
        //create an opaque window.
        if (!isTranslucencySupported) {
            System.out.println(
                "Translucency is not supported, creating an opaque window");
        }

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ShapedWindowDemo sw = new ShapedWindowDemo();

                // Set the window to 70% translucency, if supported.
                if (isTranslucencySupported) {
                    sw.setOpacity(0.7f);
                }

                // Display the window.
                sw.setVisible(true);
            }
        });
    }
}

Java SE 6 Update 10 API

不允许在更新版本中更改公共 API,因此在 Java SE 6 Update 10 版本中添加了半透明和成形窗口功能时,该功能已在私有com.sun.awt.AWTUtilities类中实现。对于 JDK 7 版本,此功能已移至公共 AWT 包中。下表显示了私有方法如何 Map 到公共方法。

Java SE 6 Update 10 中的方法相当于 JDK 7
AWTUtilities.isTranslucencySupported(Translucency)GraphicsDevice.isWindowTranslucencySupported(WindowTranslucency)
AWTUtilities.isTranslucencyCapable(GraphicsConfiguration)GraphicsConfiguration.isTranslucencyCapable()
AWTUtilities.setWindowOpacity(Window, float)Window.setOpacity(float)
AWTUtilities.setWindowShape(Window, Shape)Window.setShape(Shape)
AWTUtilities.setWindowOpaque(boolean)Window.setBackground(Color)new Color(0,0,0,alpha)传递给alpha小于 255 的此方法,将安装每像素半透明性。