4

システム トレイ モードでいくつかの通知を表示するために、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);
    }
}

そして、これは私のコントローラークラスです:

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

2 に答える 2

5

この例外は、ステージが非表示モードになり、通知をステージに表示する必要があるときに通知コンポーネントがステージを見つけられないときに発生することを認識しています。インターネットで検索した後、この問題に対する2つの解決策を見つけました。

解決策 1:

ステージを開いて通知を表示します。このようにして、ステージが非表示になっていたかどうかを確認し、ステージを開いて通知を表示する必要があります。これを行うには、CreateNotification メソッドに次の条件を追加する必要があります。

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

解決策 2:

このソリューションでは、ダミー ステージを作成し、その不透明度をゼロに設定してから、メイン ステージを非表示にします。私はこのリンクでこの解決策を見つけ、 ここにコードを入れます:

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

この2つのソリューションを実装すると、すべてがうまく機能します。この問題のより良い解決策がある場合は、ここに入力してください

完全な Netbeans プロジェクトを私のDropboxからダウンロードできます

于 2015-12-25T12:13:43.483 に答える
0

通知の作成をデバッグするまで、hamid の最初のソリューションが機能しなかった理由がわかりませんでした。私は、ウィンドウの必要性に加えて、それもそうisShowingでなければならないことを発見しましたisFocused!

私の解決策は、前にこのメソッドのようなものを呼び出すことですNotifications.show():

private void focusStage() {
    final Stage stage = (Stage) button.getScene().getWindow();
    if (!stage.isShowing()) {
        stage.show();
    }
    if (!stage.isFocused()) {
        stage.requestFocus();
    }
}
于 2018-05-31T19:15:32.403 に答える