gnome 上的 javafx 应用程序名称

Posted

技术标签:

【中文标题】gnome 上的 javafx 应用程序名称【英文标题】:javafx application name on gnome 【发布时间】:2015-07-04 09:40:27 【问题描述】:

我帮助开发适用于 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 做到这一点?

【问题讨论】:

找到解决办法了吗? 我发现这个bugs.openjdk.java.net/browse/JDK-8097949 似乎固定在 OpenJFX 上 不,它并没有以一种对我们开发人员有用的方式“修复”。请参阅下面的答案。 【参考方案1】:
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);
    


【讨论】:

您能否添加更多文字来解释答案? 尽管缺乏解释。这实际上非常有效。在扩展 javafx.application.Preloader 的类中,您可以设置应用程序名称。但是您不能在 Application 类中这样做,因为为时已晚。 对我来说非常完美。而不是 Application.launch(Test.class, args),使用 com.sun.javafx.application.LauncherImpl.launchApplication(Test.class, TestPre.class, args) 解决了我的问题。非常感谢@Eugene Smirnov【参考方案2】:

这个错误已经被报告了here,但我无法让它工作,所以最后的手段是创建一个awt 应用程序作为JavaFx 的引导程序,它就像魅力一样工作

阅读有关将 JavaFX 集成到 Swing 应用程序中的信息Integrating JavaFX into Swing Applications

代码 sn-p @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);
    

【讨论】:

谢谢,不完全是我想要的,但这可能会有所帮助 如果您有更好的解决方案,请与我们分享。我渴望看到这个有更多的进展 我想出了一个解决方法/解决方案。看我的回答。【参考方案3】:

这是一个变通方法/解决方案,对于任何使用某种支持 Maven 依赖样式坐标的构建工具(例如 Maven、Gradle、Leiningen)将 JavaFX 应用程序打包到 jar 中的人来说,它应该相对容易使用:

一个小班加入您的项目 两个小依赖 清单中有两个值 为您的 java 启动命令增加一个参数 启动脚本中的一个环境变量

在此处查看/获取源代码: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 中

但是,深入研究 OpenJFX 源代码,您会在“com.sun.class.ui.Application”中找到:

    private final static String DEFAULT_NAME = "java";
    protected String name = DEFAULT_NAME;

看到这个,你的第一个想法可能是做com.sun.javafx.application.PlatformImpl.setApplicationName(..)。 但这行不通,因为为时已晚! name 变量在类实例化时设置,然后在调用您的代码之前传递到底层 GTK 系统。

然后您尝试使用自省将常量DEFAULT_NAME 设置为您想要的,这样就可以用来实例化变量name。但这也行不通!为什么?

因为关键字final(这是工具包和应用程序实现之间最显着的区别)! 因为它是最终静态的并且被分配了值"java",所以该值在编译时是“内联的”。 IE。它被插入到“常量池”中,并且所有对常量DEFAULT_NAME 的引用都被替换为对常量池的引用。因此,即使您在运行时设置常量,它也会被忽略,并且任何代码(在本例中为构造函数)都会简单地将变量 name 设置为编译到常量池中的值。

我认为您不能通过自省来更改常量池或构造函数的主体。如果可以的话,我想知道怎么做!

输入 ASM

但是,如果你可以在类被 JVM 加载之前重写它的实际字节码,那么你几乎可以做任何事情!

所以我选择编写一个小型的“Java Agent”(将其视为 JVM 中间件)。它所做的只是查找环境变量“APPLICATION_NAME”,如果找到,它将查找类“com.sun.glass.ui.Application”并在将其传递给 JVM 和类加载器之前稍微对其进行转换:

首先,它将常量DEFAULT_NAME 设置为“APPLICATION_NAME”的值。然后,它更改 Application 构造函数的主体,以便为变量 name 分配 DEFAULT_NAME 的值(而不是常量池中的值)。最后,它禁用了setName 方法(通过移除它的主体)。

希望这很有趣,并且可能对某人有用。请务必查看(并使用)我在此答案顶部链接的解决方案。欢迎反馈。

【讨论】:

有趣的搜索,但不适用于大多数应用程序:( 是的,你是对的@Tutul。我已经更新了初始部分以阐明此答案/解决方案的范围和适用性。

以上是关于gnome 上的 javafx 应用程序名称的主要内容,如果未能解决你的问题,请参考以下文章

错误: 在类中找不到 main 方法, 请将 main 方法定义为:    public static void main(String[] args) 否则 JavaFX 应用程

如何将 Java 应用程序捆绑到 Gnome/Ubuntu 中的关联 .desktop 文件?

Linux 上的 JavaFX

JavaFX - 加载图像和内存问题

如何避免不在 FX 应用程序线程上; currentThread = JavaFX 应用程序线程错误?

尝试使用 GTKLookAndFeel 在 JavaFX 中设置 SwingNode 的样式会冻结应用程序