node mouseTransparent
property只是使 node 鼠标在JavaFX应用程序的上下文中是透明的,与JavaFX应用程序和窗口系统的其余部分无关.要做到这一点,需要在本机窗口系统中更改窗口样式.
Windows 11的解决方案
以下是一个仅适用于Windows的解决方案,它基于以下项目的 idea :
使用VM参数运行:
--add-opens javafx.graphics/javafx.stage=com.example.demo --add-opens javafx.graphics/com.sun.javafx.tk.quantum=com.example.demo
src/main/java/module-info.java个
module com.example.demo {
requires javafx.controls;
requires com.sun.jna;
requires com.sun.jna.platform;
exports com.example.demo;
}
src/main/java/com/example/demo/TransparentApplication.java个
package com.example.demo;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.SVGPath;
import javafx.stage.*;
import java.lang.reflect.Method;
public class TransparentApplication extends Application {
private static final String CROSSHAIR_SVG_PATH =
"""
M 14,8 A 6,6 0 0 1 8,14 6,6 0 0 1 2,8 6,6 0 0 1 8,2 6,6 0 0 1 14,8 Z M 8 0 L 8 6.5 M 0 8 L 6.5 8 M 8 9.5 L 8 16 M 9.5 8 L 16 8
""";
@Override
public void start(Stage stage) {
StackPane layout = new StackPane(
new Group(
createCrosshair()
)
);
layout.setBackground(Background.fill(Color.TRANSPARENT));
layout.setMouseTransparent(true);
Scene scene = new Scene(layout, Color.TRANSPARENT);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setAlwaysOnTop(true);
stage.setScene(scene);
stage.show();
makeMouseTransparent(stage);
}
private static Node createCrosshair() {
SVGPath path = new SVGPath();
path.setContent(CROSSHAIR_SVG_PATH);
path.setFill(Color.TRANSPARENT);
path.setStroke(
Color.BLUEVIOLET.deriveColor(
0, 1, 1, .6
)
);
path.setScaleX(10);
path.setScaleY(10);
return path;
}
private static void makeMouseTransparent(Stage stage) {
WinDef.HWND hwnd = getNativeHandleForStage(stage);
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);
}
private static WinDef.HWND getNativeHandleForStage(Stage stage) {
try {
final Method getPeer = Window.class.getDeclaredMethod("getPeer", (Class<?>[]) null);
getPeer.setAccessible(true);
final Object tkStage = getPeer.invoke(stage);
final Method getRawHandle = tkStage.getClass().getMethod("getRawHandle");
getRawHandle.setAccessible(true);
final Pointer pointer = new Pointer((Long) getRawHandle.invoke(tkStage));
return new WinDef.HWND(pointer);
} catch (Exception ex) {
System.err.println("Unable to determine native handle for window");
ex.printStackTrace();
return null;
}
}
public static void main(String[] args) {
launch();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.demo</groupId>
<artifactId>TransparentApp</artifactId>
<version>1.0-SNAPSHOT</version>
<name>TransparentApp</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.14.0</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
执行命令
从项目根目录的命令行运行的命令示例.为Windows 11和JDK 21提供,并假设您已经运行了mvn clean install
来构建应用程序.
set JAVA_HOME=%userprofile%\.jdks\openjdk-21.0.1
set PATH=%PATH%;%JAVA_HOME%\bin
set M2=%userprofile%\.m2\repository
java --add-opens javafx.graphics/javafx.stage=com.example.demo --add-opens javafx.graphics/com.sun.javafx.tk.quantum=com.example.demo -p %M2%\net\java\dev\jna\jna\5.14.0\jna-5.14.0.jar;%M2%\org\openjfx\javafx-base\21.0.1\javafx-base-21.0.1-win.jar;%M2%\net\java\dev\jna\jna-platform\5.14.0\jna-platform-5.14.0.jar;%M2%\org\openjfx\javafx-graphics\21.0.1\javafx-graphics-21.0.1-win.jar;%M2%\org\openjfx\javafx-controls\21.0.1\javafx-controls-21.0.1-win.jar;target\classes -m com.example.demo/com.example.demo.TransparentApplication
这些命令仅用于测试目的.通常,您可以在IDE运行配置中配置VM参数.或者,使用jlink
或jpackage
(可能通过Maven或Gradle的构建工具插件)链接和打包应用程序,并在应用程序打包中包含启动脚本中指定的VM参数,然后通过双击即可运行安装的应用程序.
从IntelliJ IDEA中跑出来
提供虚拟机(VM)参数not program arguments.需要向before要运行的类提供VM参数. Select Modify options | Add VM options
,然后在显示"VM Options"的框中添加VM参数,如以下答案所示:
替代解决方案
此建议不会 destruct 模块化,并依赖于从应用程序代码访问JNA(尽管我没有try 过).
除非您更改窗口设置,否则显示JavaFX内容的窗口将拦截鼠标操作.也许你可以做一些棘手的事情,比如捕获鼠标输入,然后暂时隐藏stage,并使用Robot触发鼠标动作,也许与一些Platform.runLater
调用结合,但这有点黑客.
Additional info
正如斯劳在 comments 中指出的那样:
因为我发誓让stage变得透明/没有装饰,让场景变得透明,让鼠标下的所有 node 要么是鼠标透明的,要么是没有背景/填充的(甚至是部分,例如,带有透明像素的图像),这些都允许你与过go stage背后的任何东西互动.至少,我相信它适用于Windows 10(尽管不适用于MacOS等其他平台).
我判断了Windows 11 Pro和JavaFX 21.0.1. 它的工作原理基本上和Slaw记忆中的一样. 如果你点击了stage的透明区域,鼠标会与stage下的窗口进行交互.
但是,如果您单击了stage的非透明区域,则鼠标操作不会在stage下方的窗口中注册,除非使用了此答案中提供的JNA代码.
常见问题解答
错误:
module javafx.graphics does not "opens javafx.stage" to module com.example.demo
这意味着你的VM参数是错误的.
当您运行应用程序时,未采用此VM参数:
--add-opens javafx.graphics/javafx.stage=com.example.demo
在本例中,com.example.demo
是模块名称,而不是包名称.除非您的模块也被命名为com.example.demo
,否则它将无法工作.有关详细信息,请参阅java man page for the --add-opens
switch.