【问题标题】:JavaFX app in System Tray系统托盘中的 JavaFX 应用程序
【发布时间】:2012-09-16 06:49:46
【问题描述】:

我正在使用 JavaFX UI 制作一个简单的应用程序,该应用程序只是这样做:

  • 有一个系统托盘图标,单击时会显示一个窗口,再次单击时会隐藏它,右键单击时会显示一个带有 1 个“退出”项的菜单

我已经制作了 UI 并将应用程序放入系统托盘中,但我无法使用 Normal Actionlistener 方法显示/隐藏它,但出现此错误:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0

这是代码:

import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionListener;


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 Main extends Application{
    public static void main(String[] args) { 
        launch(args);       
    }

    @Override
    public void start(final Stage primaryStage) {
        primaryStage.setTitle("Hello World!");
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();

        if (SystemTray.isSupported()) {         
            SystemTray tray = SystemTray.getSystemTray();
            Image image = Toolkit.getDefaultToolkit().getImage("Germany-politcal-map.jpg");
            PopupMenu popup = new PopupMenu();
            MenuItem item = new MenuItem("Exit");

            popup.add(item);

            TrayIcon trayIcon = new TrayIcon(image, "Amr_Trial", popup);

            ActionListener listener = new ActionListener() {                
                @Override
                public void actionPerformed(java.awt.event.ActionEvent arg0) {
                    // TODO Auto-generated method stub
                    System.exit(0);                 
                }               
            };                       

            ActionListener listenerTray = new ActionListener() {                
                @Override
                public void actionPerformed(java.awt.event.ActionEvent arg0) {
                    // TODO Auto-generated method stub                  
                    primaryStage.hide();
                }                   
            };            

            trayIcon.addActionListener(listenerTray);
            item.addActionListener(listener);

            try{
              tray.add(trayIcon);
            }catch (Exception e) {
              System.err.println("Can't add to tray");
            }
          } else {
            System.err.println("Tray unavailable");
          } 
        //
    }
}

【问题讨论】:

    标签: awt javafx systray


    【解决方案1】:

    您应该只修改 javafx 线程上的 javafx 类,托盘图标上的侦听器很可能在 swing 线程上运行。您可以通过向 Platform#runLater 发布一个可运行对象来做到这一点,如下所示:

    Platform.runLater(new Runnable() {
        public void run() {
            primaryStage.hide();
        }
    });
    

    【讨论】:

      【解决方案2】:

      Platform.runLater 中回调JavaFX 的actionListener 中的代码。这将在 JavaFX 应用程序线程上执行与 JavaFX 系统接口的代码,而不是尝试在 Swing 事件线程上执行它(这是导致您出现问题的原因)。

      例如:

      ActionListener listenerTray = new ActionListener() {                
        @Override public void actionPerformed(java.awt.event.ActionEvent event) {
          Platform.runLater(new Runnable() {
            @Override public void run() {
              primaryStage.hide();
            }
          });
        }                   
      };            
      

      默认情况下,应用程序将shutdown when it's last window is hidden。要覆盖此默认行为,请在显示第一个应用程序阶段之前调用 Platform.setImplicitExit(false)。然后,当您需要应用程序真正关闭时,您需要显式调用Platform.exit()


      我创建了一个demo for using the AWT system tray within a JavaFX application

      【讨论】:

      • 是的,我刚刚在文档页面中发现了这一点,非常感谢。但是当我隐藏它时,即使我使用 show() 也不会再次出现
      • 更新了解释隐式/显式退出行为的答案。
      • 真的,谢谢,我一直在寻找这个!!
      • 如果你真的需要一个单独的窗口并且它并不复杂,你应该坚持使用 awt/swing 并为自己节省 JavaFx 开销,直到它支持托盘图标。
      【解决方案3】:

      JavaFX 尚不支持系统托盘。您可以在以下 JIRA 问题下跟踪此任务的进度:https://bugs.openjdk.java.net/browse/JDK-8090475
      该问题还提供了一种解决方法,可用于 JavaFX 8 以获得基本支持。

      JavaFX 8 没有计划该功能,因此它可能会在以下更新之一甚至 JavaFX 9 中发布。

      【讨论】:

      • 不用注册就能看到jira内容就好了。
      【解决方案4】:

      我解决了您的问题。带有 AWT 的 JavaFX。我有一个应用程序示例,该应用程序在您进行左单击时显示和隐藏。我真的希望对你有用

      import java.awt.AWTException;
      import java.awt.Image;
      import java.awt.SystemTray;
      import java.awt.Toolkit;
      import java.awt.TrayIcon;
      import java.awt.event.MouseAdapter;
      import java.awt.event.MouseEvent;
      import java.net.URL;
      import javafx.application.Application;
      import static javafx.application.Application.launch;
      import javafx.application.Platform;
      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 MainApp2 extends Application {
      
      int stateWindow = 1;
      
      @Override
      public void start(final Stage stage) throws Exception {
      
      //Check the SystemTray is supported
      if (!SystemTray.isSupported()) {
          System.out.println("SystemTray is not supported");
          return;
      }
      
      URL url = System.class.getResource("/image/yourImage.png");
      Image image = Toolkit.getDefaultToolkit().getImage(url);
      
      //image dimensions must be 16x16 on windows, works for me
      final TrayIcon trayIcon = new TrayIcon(image, "application name");
      
      final SystemTray tray = SystemTray.getSystemTray();
      
      //Listener left clic XD
      trayIcon.addMouseListener(new MouseAdapter() {
          @Override
          public void mouseClicked(MouseEvent event) {
              if (event.getButton() == MouseEvent.BUTTON1) {
                  Platform.runLater(new Runnable() {
                      @Override
                      public void run() {
                          if (stateWindow == 1) {
                              stage.hide();
                              stateWindow = 0;
                          } else if (stateWindow == 0) {
                              stage.show();
                              stateWindow = 1;
                          }
                      }
                  });
              }
      
          }
      });
      
      try {
          tray.add(trayIcon);
      } catch (AWTException e) {
          System.out.println("TrayIcon could not be added.");
      }
      
      stage.setTitle("Hello man!");
      Button btn = new Button();
      btn.setText("Say 'Hello man'");
      btn.setOnAction(new EventHandler<ActionEvent>() {
      
          @Override
          public void handle(ActionEvent event) {
              System.out.println("Hello man!");
          }
      });
      StackPane root = new StackPane();
      root.getChildren().add(btn);
      stage.setScene(new Scene(root, 300, 250));
      Platform.setImplicitExit(false);
      stage.show();
      
      }
      
      /**
       * The main() method is ignored in correctly deployed JavaFX application.
       * main() serves only as fallback in case the application can not be
       * launched through deployment artifacts, e.g., in IDEs with limited FX
       * support. NetBeans ignores main().
       *
       * @param args the command line arguments
       */
      public static void main(String[] args) {
      launch(args);
      }
      }
      

      【讨论】:

        【解决方案5】:

        无耻的自插拔,但我为 JavaFX 图标开发了一个小型包装库,它使用名为 FXTrayIcon 的 SystemTray。

        它抽象出所有讨厌的 AWT 位,无需猜测您应该在哪个线程上运行代码。它可以作为 Maven Central 的依赖项使用。

        【讨论】:

        • 太棒了,这比所有的awt混乱都要好。迄今为止我在这个问题上的最佳解决方案。
        猜你喜欢
        • 2013-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-06
        • 1970-01-01
        • 1970-01-01
        • 2010-10-19
        • 1970-01-01
        相关资源
        最近更新 更多