gnome上的javafx应用程序名称

时间:2015-07-04 09:40:27

标签: java linux javafx gnome

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

3 个答案:

答案 0 :(得分:0)

已经报告了该错误here,但是我无法使其正常工作,因此mt最终的解决方法是创建一个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);
    }
}

答案 1 :(得分:0)

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);
    }

}

答案 2 :(得分:0)

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

  • 一个小班学生进入您的项目
  • 两个小依赖项
  • 清单中的两个值
  • 您的Java启动命令还有一个arg
  • 启动脚本中的一个环境变量

在此处查看/获取源:no.andante.george.Agent

(详细用法/帮助嵌入到源代码中。)

希望它有用。
(让我知道您是否能够在下面的注释中成功使用它。)


了解问题和解决方案

问题是双重打击!

深入研究OpenJDK源代码,您将在“ sun.awt.X11.XToolkit”(扩展了“ sun.awt.UnixToolkit”和“ sun.awt.SunToolkit”并扩展了“ java.awt.Toolkit”)中找到:

    private static String awtAppClassName = null;

如果您足够早地(例如,在main方法的开始)将此变量设置为所需的任何内容(例如,自省),则当工具包出现时,您的值将被提取并传递给基础的GTK系统窗口系统已初始化。这就是例如IntelliJcom.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",所以在编译时将该值“内联”。即它将被插入“常量池”,并且所有对常量DEFAULT_NAME的引用都将替换为对常量池的引用。因此,即使您在运行时设置了常量,该常量也将被忽略,并且任何代码(在这种情况下为构造函数)都将简单地将变量name设置为已编译到常量池中的值。

我认为您无法通过自省更改常量池或构造函数的主体。如果可以的话,我想知道如何!

输入ASM

但是,如果您可以在JVM加载该类之前重写该类的实际字节码,那么您可以做很多事情!

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

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

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