Java:点击窗口(包括文本/图像)

我想在Java中创建一个透明的覆盖层, 始终在顶部 ,并且我可以点击 。 我发现了一些关于这个问题的类似 post ,但即使在听完他们的答案后,我也遇到了一个问题。

我的问题是让整个窗口点击。 我在使用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没有被点击

使窗口“点击”的问题在于它是在系统级别上处理的,超出了标准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; } 

为什么不利用现有的JLayeredPane ? 此博客文章演示了在JFrame上放置各种叠加层,包括文本,图像和动态绘制的像素。

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

检查此窗口示例:

 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组件,如标签,按钮等,它们可能有自己的事件监听器,可以阻止事件。