我帮助用Linux和OSX开发JavaFX应用程序。在Linux上,我们在gnome顶部栏上没有应用程序名称。我们有JavaFX的入口点。窗口有好名字,但在gnome上我们有类似“com.myApp.javaFXMainClass”的东西。
我对swing有同样的问题,我能用这些代码纠正它:
// Set name in system menubar for Gnome (and Linux)
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
try {
Toolkit xToolkit = Toolkit.getDefaultToolkit();
Field awtAppClassNameField = xToolkit.getClass().getDeclaredField("awtAppClassName");
awtAppClassNameField.setAccessible(true);
awtAppClassNameField.set(xToolkit, "MyApp");
} catch (Exception e) {
// TODO
}
}
如何用JavaFX做到这一点?
package test;
import javafx.application.Preloader;
import javafx.stage.Stage;
public class TestPre extends Preloader {
@Override
public void start(Stage stage) throws Exception {
com.sun.glass.ui.Application.GetApplication().setName("app test");
}
}
package test;
import java.io.ByteArrayInputStream;
import java.util.Base64;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Test __app = new Test();
Stage __stage = new Stage();
__app.start(__stage);
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
String __simage = "iVBORw0K.....";
ByteArrayInputStream __imgstream = new ByteArrayInputStream(Base64.getDecoder().decode(__simage));
javafx.scene.image.Image __image = new javafx.scene.image.Image(__imgstream);
primaryStage.getIcons().add(__image);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
com.sun.javafx.application.LauncherImpl.launchApplication(Test.class, TestPre.class, args);
}
}
这个bug已经被报道了here但我无法让它工作所以最后的手段是创建一个awt
应用程序作为引导到JavaFx
它的工作就像魅力
代码片段@gitlab
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javax.swing.*;
import java.awt.*;
import java.lang.reflect.Field;
public class CustomJavaFxAppName {
private void display() {
JFrame f = new JFrame("CustomJavaFxAppName");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JFXPanel jfxPanel = new JFXPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
};
initJFXPanel(jfxPanel);
f.add(jfxPanel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private void initJFXPanel(JFXPanel jfxPanel) {
Platform.runLater(() -> {
javafx.scene.control.Label label = new javafx.scene.control.Label(
System.getProperty("os.name") + " v"
+ System.getProperty("os.version") + "; Java v"
+ System.getProperty("java.version"));
StackPane root = new StackPane(label);
Scene scene = new Scene(root);
jfxPanel.setScene(scene);
});
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
try {
Toolkit xToolkit = Toolkit.getDefaultToolkit();
Field awtAppClassNameField = xToolkit.getClass().getDeclaredField("awtAppClassName");
awtAppClassNameField.setAccessible(true);
awtAppClassNameField.set(xToolkit, "MyApp");
} catch (Exception ignored) { }
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new CustomJavaFxAppName()::display);
}
}
这是一个解决方法/解决方案,对于使用某种支持Maven依赖样式坐标的构建工具(例如Maven,Gradle,Leiningen)将其JavaFX应用程序打包在jar中的人来说应该相对容易使用:
在这里查看/获取来源:no.andante.george.Agent
(源中嵌入的详细用法/帮助。)
希望它有用。 (如果你能在下面的评论中成功使用它,请告诉我。)
了解问题和这个解决方案
问题是双重打击!
深入研究OpenJDK源代码,您可以在'sun.awt.X11.XToolkit'中找到它(它扩展了'sun.awt.UnixToolkit',它扩展了'sun.awt.SunToolkit',它扩展了'java.awt.Toolkit'):
private static String awtAppClassName = null;
如果您足够早地将此变量设置为您想要的内容(使用内省)(例如,在main
方法的开头),则在初始化Toolkit窗口系统时,您的值将被拾取并传递给基础GTK系统。这就是例如IntelliJ在com.intellij.ui.AppUIUtil
但是,您可以在'com.sun.class.ui.Application'中找到OpenJFX源代码:
private final static String DEFAULT_NAME = "java";
protected String name = DEFAULT_NAME;
看看这个,你的第一个想法可能是做
com.sun.javafx.application.PlatformImpl.setApplicationName(..)
。但这不会奏效,因为为时已晚! name
变量在类实例化时设置,然后在调用代码之前传递给底层的GTK系统。
那么你尝试使用内省将常量DEFAULT_NAME
设置为你想要的,这样就可以用来实例化变量name
。但那也行不通!为什么?
因为关键字final
(这是Toolkit和Application实现之间最重要的区别)!
因为它是最终静态的并且被赋值为"java"
,所以在编译时该值是“内联的”。即它被插入“常量池”,并且对常量DEFAULT_NAME
的所有引用都被替换为对常量池的引用。因此,即使您在运行时设置常量,它也将被忽略,并且任何代码(在本例中为构造函数)都只是将变量name
设置为编译到常量池中的值。
我不认为你可以通过内省改变常量池或构造函数的主体。如果可以,我想知道如何!
输入ASM
但是如果你可以在JVM加载之前重写类的实际字节码,那么你几乎可以做任何事情!
所以我选择编写一个小的“Java代理”(将其视为JVM中间件)。所有这一切都是寻找环境变量'APPLICATION_NAME',如果找到它将查找类'com.sun.glass.ui.Application'并在将其传递给JVM和类加载器之前稍微转换它:
首先,它将常量DEFAULT_NAME
设置为'APPLICATION_NAME'的值。然后,它改变Application构造函数的主体,使变量name
被赋值为DEFAULT_NAME
(而不是常量池中的值)。最后,它禁用方法setName
(通过删除它的身体)。
希望这很有趣,对某人可能有用。请查看(并使用)我在此答案顶部链接的解决方案。欢迎反馈。