带有 controlsfx 通知组件的 javafx NullPointerException

标签 java javafx javafx-2 system-tray controlsfx

我想开发一个应用程序,它使用 controlsfx Notifications 在系统托盘模式下显示一些通知。在正常模式下,我的应用程序运行良好,通知可以成功显示。但是当我在系统托盘中隐藏阶段时,会发生 NullPointerException。我不知道如何解决这个问题。

import java.awt.AWTException;
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.application.Platform;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

public class TryIconNotification extends Application {
    private boolean firstTime;
    private TrayIcon trayIcon;

    @Override
    public void start(Stage stage) throws Exception {
        firstTime = true;
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

        Scene scene = new Scene(root);

        createTrayIcon(stage);
        firstTime = true;
        Platform.setImplicitExit(false);

        stage.setScene(scene);
        stage.show();
    }

    public void createTrayIcon(final Stage stage) {
        if (SystemTray.isSupported()) {
            // get the SystemTray instance
            SystemTray tray = SystemTray.getSystemTray();
            // load an image
            java.awt.Image image = null;
            image = Toolkit.getDefaultToolkit().getImage("icons\\iconify.png");

            stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
                @Override
                public void handle(WindowEvent t) {
                    hide(stage);
                }
            });

            // create a action listener to listen for default action executed on the tray icon
            final ActionListener closeListener = new ActionListener() {
                @Override
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    stage.hide();

                }
            };

            ActionListener showListener = new ActionListener() {
                @Override
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            stage.show();
                        }
                    });
                }
            };
            // create a popup menu
            PopupMenu popup = new PopupMenu();

            MenuItem showItem = new MenuItem("Open app");

            showItem.addActionListener(showListener);
            popup.add(showItem);

            MenuItem closeItem = new MenuItem("Exit");
            closeItem.addActionListener(closeListener);
            popup.add(closeItem);
            /// ... add other items
            // construct a TrayIcon
            trayIcon = new TrayIcon(image, "Systray", popup);
            // set the TrayIcon properties
            trayIcon.addActionListener(showListener);
            // ...
            // add the tray image
            try {
                tray.add(trayIcon);
            } catch (AWTException e) {
                System.err.println(e);
            }
            // ...
        }
    }

    public void showProgramIsMinimizedMsg() {
        //only in first time show the message
        if (firstTime) {
            trayIcon.displayMessage("System Tray",
                    "Iconified",
                    TrayIcon.MessageType.INFO);
            firstTime = false;
        }
    }

    private void hide(final Stage stage) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                if (SystemTray.isSupported()) {
                    stage.hide();
                    showProgramIsMinimizedMsg();
                } else {
                    System.exit(0);
                    System.out.println("Not Support Sys Tray");
                }
            }
        });
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}

这是我的 Controller 类:

import java.net.URL;
import java.util.ResourceBundle;
import java.util.Timer;
import java.util.TimerTask;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import org.controlsfx.control.Notifications;

public class FXMLDocumentController  implements Initializable   {

    @FXML
    private Label label;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        Stage stage = (Stage) label.getScene().getWindow();
        stage.hide();
    }

    public void createNotification() {
        Notifications.create()
                .text("This is a Notification")
                .title("Notifications")
                .showInformation();
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        Timer timer = new Timer();

        timer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
              Platform.runLater(()->createNotification());
            }

        }, 5000, 10000);
    }
}

最佳答案

我意识到当舞台进入隐藏模式并且当通知需要在舞台上显示时通知组件找不到舞台时会发生此异常。在互联网上搜索后,我找到了解决此问题的两种方法。

解决方案一:

打开舞台并显示通知。 通过这种方式,我们应该检查舞台是否被隐藏,打开它,并显示通知。 为此,我们必须在 CreateNotification 方法中添加此条件:

Stage stage = (Stage)  button.getScene().getWindow();
if (!stage.isShowing()){
    stage.show();
}

解决方案 2:

在这个解决方案中,我们创建了一个虚拟舞台并将其不透明度设置为零,然后隐藏主舞台。我在这个链接找到这个解决方案并将代码放入 here :

public void createDummyStage() {
    Stage dummyPopup = new Stage();
    dummyPopup.initModality(Modality.NONE);
    // set as utility so no iconification occurs
    dummyPopup.initStyle(StageStyle.UTILITY);
    // set opacity so the window cannot be seen
    dummyPopup.setOpacity(0d);
    // not necessary, but this will move the dummy stage off the screen
    final Screen screen = Screen.getPrimary();
    final Rectangle2D bounds = screen.getVisualBounds();
    dummyPopup.setX(bounds.getMaxX());
    dummyPopup.setY(bounds.getMaxY());
    // create/add a transparent scene
    final Group root = new Group();
    dummyPopup.setScene(new Scene(root, 1d, 1d, Color.TRANSPARENT));
    // show the dummy stage
    dummyPopup.show();
}

正如我在下面提到的,我们应该在隐藏主舞台之前调用这个方法:

 @FXML
public void handleSysTryAction(ActionEvent event) {
    Stage stage = (Stage) button.getScene().getWindow();
    createDummyStage();
    stage.hide();
}

我实现了这两个解决方案,并且一切正常。如果您对这个问题有更好的解决方案,请放在这里

您可以从我的 Dropbox 下载完整的 Netbeans 项目

关于带有 controlsfx 通知组件的 javafx NullPointerException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34444167/

相关文章:

java - Java-GSS启动失败异常,由以下原因引起:org.apache.thrift.transport.TTransportException:GSS启动失败

java - 使用 Java 连接到 Amazon RDS 实例时如何解决 ORA-12514

java - Eclipse->File->New-Project 中没有可用的 Android 项目类型

java - 在JavaFx Gui中添加图片

JavaFX - 水平字幕文本

java - 通过Servlet或JSP将JasperReport的报告导出为pdf

java - 从 cmd 运行 JavaFX

layout - 如何获取按钮来填充 javafx 网格 Pane ?

javafx-2 - JavaFX 2 刷新表格单元格以更改样式

java - 从 DB 检索图像到 imageView