Swing 分层 - 透明组件忽略底层 AWT 元素

Swing layering - transparent component ignores underlying AWT element

首先,为了解决这个问题,我绝对需要在 swing 应用程序中使用重量级 AWT 组件。我需要他们两个的功能。

任务很简单 - 渲染重量级 AWT Canvas(或任何其他元素),直接在其上渲染 OpenGL 场景,然后在其上方显示 UI 的 Swing 按钮。

我的问题是它半途而废。 Z 排序似乎没有问题。我正在为此使用 jLayeredPanes,我可以在层之间移动 Canvas,它确实有效,弹出在其他元素的顶部或下方。

问题在于透明度。 问题是,Swing 元素有 Opaque 参数,当它设置为 false(非不透明)时 - 它基本上应该是透明的,您应该会看到它下面的下一个元素。然而,在我的例子中,AWT Canvas 被忽略了,而你只会看到下一个底层 SWING 元素。

这里有几个屏幕截图。它们取自我的一个独立测试项目。 canvas 被拉伸到框架的大小,左上角有一个 JLayeredPane 虚拟元素,它是菜单的简化版本。

第一个截图中,JLayeredPane的Opaque设置为true,你可以看到它的背景属性设置为蓝色。

second 屏幕截图中,一切都完全相同,但 Opaque 设置为 false。而不是显示 Canvas 上的任何内容 - 在空灰色 jFrame 背景中绘制的内容。

最后,在 third 屏幕截图中,我将 Canvas 放入 jPanel 而不是单独放置。如您所见,通过透明的 jLayeredPane 可以看到 Panel 的橙色,但是 Canvas 再次被隐藏。

这是框架布局的代码。我现在不会 post 我的 rendering/context 代码

frame = new JFrame("AWT test");
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setLayout(new BorderLayout());
    frame.setPreferredSize(new Dimension(width, height));

    canvas = new Canvas();
    canvas.setSize(width,height);

    //this part exists only in the third example
    JPanel p = new JPanel();
    p.setSize(width,height);
    p.setBackground(Color.orange);
    p.add(canvas);
    // third example end

    JLayeredPane pane = new JLayeredPane();
    JLayeredPane paneMenu = new JLayeredPane();
    JButton button = new JButton();
    button.setSize(20,20);
    paneMenu.setSize(200,200);
    paneMenu.add(button, new Integer(1));
    paneMenu.setBackground(Color.BLUE);
    paneMenu.setOpaque(false);           //True for the first example

    pane.add(p, new Integer(1));    // canvas for the first two examples
    pane.add(paneMenu, new Integer(2));

    pane.setOpaque(false);

    frame.add(pane);
    frame.pack();
    frame.setVisible(true);
    frame.transferFocus();

任何人都可以向我解释发生了什么以及如何做我需要做的事情。 我将再次重复 - 我必须使用重量级组件作为渲染目标。我知道像 JOGL 的 GLPanel 这样的解决方案,它是一个轻量级的 Swing 兼容组件。但我尝试了该方法,但性能确实很慢,因为它不是直接将其作为上下文目标渲染到它上面 - 它从内存中读取 FrameBuffer,翻转它,然后将其绘制为 BufferedImage。此路径不适合我将 运行 使用的嵌入式系统的有限资源。

c0der 说:请post 最小可重现示例 Errrrm....我不是吗? 在这里,如果需要,您可以使用完整的 java class 形式,但我确实将一些变量更改为常量。

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

public class Main
{
    public static void main(String[] args)
    {
        JFrame frame = new JFrame("AWT test");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.setPreferredSize(new Dimension(500, 500));

        Canvas canvas = new Canvas();
        canvas.setSize(500,500);
        canvas.setBackground(Color.RED);

        //this part exists only in the third example
        JPanel p = new JPanel();
        p.setSize(500,500);
        p.setBackground(Color.orange);
        p.add(canvas);
        // third example end

        JLayeredPane pane = new JLayeredPane();
        JLayeredPane paneMenu = new JLayeredPane();
        JButton button = new JButton();
        button.setSize(20,20);
        paneMenu.setSize(200,200);
        paneMenu.add(button, new Integer(1));
        paneMenu.setBackground(Color.BLUE);
        paneMenu.setOpaque(false);           //True for the first example

        pane.add(p, new Integer(1));    // canvas for the first two examples
        pane.add(paneMenu, new Integer(2));

        pane.setOpaque(false);

        frame.add(pane);
        frame.pack();
        frame.setVisible(true);
        frame.transferFocus();
    }
}

一点更新:

我最初怀疑是因为 Swing 元素将它们的所有绘图委托给底层重量级元素(在我的例子中是 JFrame),然后发生的事情是框架为自己生成一个 frameBuffer,然后显示在 Canvas. Canvas 本身在这一代没有处理,因此帧 "covers" 在 canvas.

之上

好像不是这样的。我尝试使 JFrame 未修饰,所有面板均不透明,并显示图片。结果 - canvas 仍然是 "cut",通过这个洞你可以看到下面的 IDE 菜单。 这让我想到在绘图期间的某处,Canvas 本身检测到它被另一个元素遮挡,并且不需要绘制该区域。所以它 "optimizes" 本身并不更新这些像素。

也许我错了。但这是另一个屏幕截图。这是与之前相同的示例,但我取出了 3d 渲染并简单地尝试显示背景设置为红色的 Canvas。

再次回答我自己的问题。

原来我需要做

setComponentMixingCutoutShape(paneMenu, new Rectangle());

用于按钮下方的菜单窗格。这基本上告诉 java 不要从重量级底层组件中删除元素。