9

我想在 Java 中创建一个透明的叠加层,始终位于顶部,并且我可以点击。我发现了一些关于这个问题的类似 帖子,但即使按照他们的回答,我也遇到了一个问题。

我的问题是让整个窗口点击通过。让它与 JFrame 一起工作没有任何问题,但是一旦我向它添加任何组件(JLabel 或 ImagePanel),点击属性就不会传递给它们。

因为我想为我的应用程序创建一个背景图像,所以这基本上使我无法查看每当我单击文本/图像覆盖的区域时窗口如何聚焦的代码。

在我展示我正在使用的代码之前,我首先想参考这些 线程,它们基本上准确地描述了我想要的东西,除了 C#。

我的目标是创建一个带有透明 .png 图像的叠加层,顶部的一些文本会随关键事件发生变化。如果它使用 JFrame 或任何其他库都没关系。我只需要它与 Windows 兼容。

我还想提一下,我对 Java 有一些经验,但我是使用 JFrame 的新手。

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;

import com.sun.jna.platform.WindowUtils;


public class Overlay {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Overlay Window");
        frame.setUndecorated(true);
        frame.setAlwaysOnTop(true);
        frame.getRootPane().putClientProperty("apple.awt.draggableWindowBackground", false);
        frame.setLocation(400, 400);
        frame.getContentPane().setLayout(new java.awt.BorderLayout());

        JLabel textLabel = new JLabel("I'm a label in the window", SwingConstants.CENTER);
        frame.getContentPane().add(textLabel, BorderLayout.CENTER); 
        frame.pack();

        System.setProperty("sun.java2d.noddraw", "true");
        WindowUtils.setWindowTransparent(frame, true);
        WindowUtils.setWindowAlpha(frame, 1.0f);

        //Using AWTUtilities gives the same result as WindowUtils
        //AWTUtilities.setWindowOpaque(frame, false);
        //AWTUtilities.setWindowOpacity(frame, 1.0f);

        frame.setVisible(true);
    }
}

请注意,问题不在于窗口被聚焦(尽管这是问题的结果),而在于JLabel 和 ImagePanel 不是 click-through

4

3 回答 3

4

让窗口“点击通过”的问题在于它是在系统级别上处理的,超出了标准 API 的范围。这意味着为使窗口“点击”而编写的任何代码都将依赖于系统。话虽如此,在 Windows 上完成此操作的过程相当简单。

在 Windows 2000 和更高版本上,通过在窗口上设置标志WS_EX_LAYERED 和 WS_EX_TRANSPARENT,然后单击该窗口。示例代码使用JNA来完成此操作:

public static void main(String[] args) {
    Window w = new Window(null);

    w.add(new JComponent() {
        /**
         * This will draw a black cross on screen.
         */
        protected void paintComponent(Graphics g) {
            g.setColor(Color.BLACK);
            g.fillRect(0, getHeight() / 2 - 10, getWidth(), 20);
            g.fillRect(getWidth() / 2 - 10, 0, 20, getHeight());
        }

        public Dimension getPreferredSize() {
            return new Dimension(100, 100);
        }
    });
    w.pack();
    w.setLocationRelativeTo(null);
    w.setVisible(true);
    w.setAlwaysOnTop(true);
    /**
     * This sets the background of the window to be transparent.
     */
    AWTUtilities.setWindowOpaque(w, false);
    setTransparent(w);
}

private static void setTransparent(Component w) {
    WinDef.HWND hwnd = getHWnd(w);
    int wl = User32.INSTANCE.GetWindowLong(hwnd, WinUser.GWL_EXSTYLE);
    wl = wl | WinUser.WS_EX_LAYERED | WinUser.WS_EX_TRANSPARENT;
    User32.INSTANCE.SetWindowLong(hwnd, WinUser.GWL_EXSTYLE, wl);
}

/**
 * Get the window handle from the OS
 */
private static HWND getHWnd(Component w) {
    HWND hwnd = new HWND();
    hwnd.setPointer(Native.getComponentPointer(w));
    return hwnd;
}
于 2015-02-27T18:57:04.253 回答
1

为什么不直接利用现有的JLayeredPane? 这篇博客文章演示了在 JFrame 上放置各种覆盖,包括文本、图像和动态绘制的像素。

于 2015-03-01T20:21:37.557 回答
1

我试图制作完全“事件透明”(您称之为点击)窗口,但该技巧似乎有一些本机限制。

检查此窗口示例:

public static void main ( String[] args )
{
    Window w = new Window ( null );

    w.add ( new JComponent ()
    {
        protected void paintComponent ( Graphics g )
        {
            g.setColor ( Color.BLACK );
            g.fillRect ( 0, getHeight () / 2 - 10, getWidth (), 20 );
            g.fillRect ( getWidth () / 2 - 10, 0, 20, getHeight () );
        }

        public Dimension getPreferredSize ()
        {
            return new Dimension ( 100, 100 );
        }

        public boolean contains ( int x, int y )
        {
            return false;
        }
    } );

    AWTUtilities.setWindowOpaque ( w, false );
    AWTUtilities.setWindowOpacity ( w, 0.5f );

    w.pack ();
    w.setLocationRelativeTo ( null );
    w.setVisible ( true );
}

窗口和组件没有任何:

  1. 鼠标监听器
  2. 鼠标运动监听器
  3. 鼠标滚轮监听器
  4. 关键听众

contains即使由于修改的方法而存在任何侦听器,组件也应该忽略任何类型的鼠标事件。

如您所见 - 组件上未绘制任何内容的区域是事件透明的,但填充区域不是。不幸的是,我没有找到任何解决方法来改变这种行为。似乎一些“低级”java方法正在阻止事件。

这只是一个基于 JComponent 的基本示例。我什至不说更复杂的 Swing 组件,如标签、按钮等,它们可能有自己的事件侦听器,可以阻止事件。

于 2012-06-27T14:52:20.430 回答