Вопрос

I am Making a Simple App using JavaFX UI, The app simply just do that:

  • has a systray icon, which when clicked shows a window, when clicked again hides it, on rightclick shows a menu with 1 "exit" item

I already Made the UI and put the App in the Sys Tray, but i can't show/hide it using Normal Actionlistener method, but i got this error:

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

here is the Code:

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");
          } 
        //
    }
}
Это было полезно?

Решение

Wrap the code in the actionListener which calls back to JavaFX in Platform.runLater. This will execute the code which interfaces with the JavaFX system on the JavaFX application thread rather than trying to do it on the Swing event thread (which is what is causing you issues).

For example:

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

By default the application will shutdown when it's last window is hidden. To override this default behaviour, invoke Platform.setImplicitExit(false) before you show the first application Stage. You will then need to explicitly call Platform.exit() when you need the application to really shutdown.


I created a demo for using the AWT system tray within a JavaFX application.

Другие советы

You should only modify the javafx classes on the javafx thread, the listeners on the tray icon are likely to be running on the swing thread. You can do this by posting a runnable to Platform#runLater like so:

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

The system tray is not supported in JavaFX yet. You could track the progress on this task under the following JIRA issue: https://bugs.openjdk.java.net/browse/JDK-8090475
The issue also provides a workaround, which could be used in JavaFX 8 to get the basic support.

The feature is not planned for JavaFX 8, so it might be released in one of the following updates or even in JavaFX 9.

I resolved your issue. JavaFX with AWT. I have one example of a application that shows and hides when you make left clic. i really hope works for you

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

Shameless self-plug, but I developed a small wrapper library for JavaFX icons that use the SystemTray called FXTrayIcon.

It abstracts away all of the nasty AWT bits and eliminates having to guess which thread you should be running code on. It's available as a dependency on Maven Central.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top