Сворачивание/разворачивание из трея

 
 
 
Сообщения:9
Доброго времени суток. По нажатию на баттон окно сворачивается, добавляясь в трей, по даблклику мышью по иконке разворачивается (должно, если точнее). С первым этапом все нормально, а вот второй меня с ума просто свел.

В моей фантазии при даблклике по иконке окно должно молча возвращаться на свои предыдущие позиции на момент минимизации. Однако в реальности окно вместо запланированного объявления совершает таки невиданный пируэт:


public class Controller extends Application
{
    public static void main( String[] args )
    {
        launch( args );
    }

    public void start( final Stage primaryStage ) throws Exception
    {
        Platform.setImplicitExit( false );
        primaryStage.setResizable( false );
        final Parent root = FXMLLoader.load( getClass().getResource( "Form.fxml" ) );
        primaryStage.setTitle( "Time Use Efficiency Indicator" );
        final Scene scene = new Scene( root );
        primaryStage.setScene( scene );
        primaryStage.show();

        installTray( primaryStage );

        primaryStage.setOnCloseRequest( windowEvent -> System.exit( 1 ) );
    }

    private void installTray( final Stage stage ) throws AWTException
    {
        // сворачиваемся в трей по нажатию на баттон
        stage.iconifiedProperty().addListener( ( ov, t, t1 ) -> {
            Platform.runLater( stage::hide );
        });

        final SystemTray tray = SystemTray.getSystemTray();
        final TrayIcon trayIcon = new TrayIcon( getIcon(), "Демонстрация сворачивания в трей" );
        // при даблклике на иконке пытаемся развернуться (и сосем хер)
        trayIcon.addActionListener( event -> {
            Platform.runLater( () -> {
                stage.show();
                stage.toFront();
            } );
        });
        tray.add( trayIcon );
    }

    private java.awt.Image getIcon()
    {
        final URL iconUrl = Controller.class.getResource( "icon16x16.png" );
        return Toolkit.getDefaultToolkit().getImage( iconUrl );
    }
}


Уже который час безуспешно бьюсь головой об стену. Силы мои на исходе. В шаге от смены профессии. Спасите меня.

Заранее благодарю всех спасителей.
Изменен:06 июл 2018 18:48
 
 
Сообщения:297
Если правильно понял проблему, то вот немного изменённый код отсюда:
public class JavaFXTrayIconSample extends Application {
    private static final String iconImageLoc = "http://icons.iconarchive.com/icons/scafer31000/bubble-circle-3/16/GameCenter-icon.png";
    private Stage stage;
    private Timer notificationTimer = new Timer();
    private DateFormat timeFormat = SimpleDateFormat.getTimeInstance();
    //
    private Double stageX;
    private Double stageY;
    //Два следующих поля введены для корректного восстановления позиции окна при нажатии кнопки Restore после того,
    // как окно было спрятано в режиме maximized (так как сначала происходят события изменения координат X и Y, а уже
    //потом событие Maximized).
    private Double stageXFix;
    private Double stageYFix;
    //
    private Double stageHeight;
    private Double stageWidth;
    private boolean isStageMaximized;

    public static void main(String[] args) throws IOException, java.awt.AWTException {
        launch(args);
    }

    @Override
    public void start(final Stage stage) {
        this.stage = stage;

        stage.iconifiedProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue) {
                Platform.runLater(() -> {
                    stage.setIconified(false);
                    stage.hide();
                });
            }
        });

        stage.xProperty().addListener((observable, oldValue, newValue) -> {
            if(!isStageMaximized) {
                stageXFix = stageX;
                stageX = (Double) newValue;
            }
        });
        stage.yProperty().addListener((observable, oldValue, newValue) -> {
            if(!isStageMaximized) {
                stageYFix = stageY;
                stageY = (Double) newValue;
            }
        });
        stage.heightProperty().addListener((observable, oldValue, newValue) -> {
            if(!isStageMaximized) {
                stageHeight = (Double) newValue;
            }
        });
        stage.widthProperty().addListener((observable, oldValue, newValue) -> {
            if(!isStageMaximized) {
                stageWidth = (Double) newValue;
            }
        });
        stage.maximizedProperty().addListener((observable1, oldValue1, newValue1) -> {
            isStageMaximized = newValue1;
            stageX = stageXFix;
            stageY = stageYFix;
        });

        stage.setOnShowing(event -> {
            if (stageX != null) {
                stage.setX(stageX);
            }
            if (stageY != null) {
                stage.setY(stageY);
            }
            if (stageHeight != null) {
                stage.setHeight(stageHeight);
            }
            if (stageWidth != null) {
                stage.setWidth(stageWidth);
            }

            stage.setMaximized(isStageMaximized);
        });

        Platform.setImplicitExit(false);

        javax.swing.SwingUtilities.invokeLater(this::addAppToTray);

        // create the layout for the javafx stage.
        StackPane layout = new StackPane(createContent());
        layout.setStyle(
                "-fx-background-color: rgba(255, 255, 255, 0.5);"
        );
        layout.setPrefSize(300, 200);

        layout.setOnMouseClicked(event -> stage.hide());

        Scene scene = new Scene(layout);
        scene.setFill(Color.TRANSPARENT);

        stage.setScene(scene);
    }

    private Node createContent() {
        Label hello = new Label("hello, world");
        hello.setStyle("-fx-font-size: 40px; -fx-text-fill: forestgreen;");
        Label instructions = new Label("(click to hide)");
        instructions.setStyle("-fx-font-size: 12px; -fx-text-fill: orange;");

        VBox content = new VBox(10, hello, instructions);
        content.setAlignment(Pos.CENTER);

        return content;
    }

    private void addAppToTray() {
        try {
            java.awt.Toolkit.getDefaultToolkit();

            if (!java.awt.SystemTray.isSupported()) {
                System.out.println("No system tray support, application exiting.");
                Platform.exit();
            }

            java.awt.SystemTray tray = java.awt.SystemTray.getSystemTray();
            URL imageLoc = new URL(iconImageLoc);
            java.awt.Image image = ImageIO.read(imageLoc);
            java.awt.TrayIcon trayIcon = new java.awt.TrayIcon(image);

            trayIcon.addActionListener(event -> Platform.runLater(this::showStage));

            java.awt.MenuItem openItem = new java.awt.MenuItem("hello, world");
            openItem.addActionListener(event -> Platform.runLater(this::showStage));

            // the convention for tray icons seems to be to set the default icon for opening
            // the application stage in a bold font.
            java.awt.Font defaultFont = java.awt.Font.decode(null);
            java.awt.Font boldFont = defaultFont.deriveFont(java.awt.Font.BOLD);
            openItem.setFont(boldFont);

            java.awt.MenuItem exitItem = new java.awt.MenuItem("Exit");
            exitItem.addActionListener(event -> {
                notificationTimer.cancel();
                Platform.exit();
                tray.remove(trayIcon);
            });

            final java.awt.PopupMenu popup = new java.awt.PopupMenu();
            popup.add(openItem);
            popup.addSeparator();
            popup.add(exitItem);
            trayIcon.setPopupMenu(popup);

            // create a timer which periodically displays a notification message.
            notificationTimer.schedule(
                    new TimerTask() {
                        @Override
                        public void run() {
                            javax.swing.SwingUtilities.invokeLater(() -> trayIcon.displayMessage("hello",
                                                                                                 "The time is now " + timeFormat.format(new Date()),
                                                                                                 java.awt.TrayIcon.MessageType.INFO));
                        }
                    },
                    5_000,
                    60_000
            );

            // add the application tray icon to the system tray.
            tray.add(trayIcon);
        } catch (java.awt.AWTException | IOException e) {
            System.out.println("Unable to init system tray");
            e.printStackTrace();
        }
    }

    private void showStage() {
        if (stage != null) {
            stage.show();
            stage.toFront();
        }
    }
}
Изменен:07 июл 2018 19:54
 
 
Сообщения:9
Спасибо за попытку! Код этот видел, он фиксирует позицию окна. Но это не спасает.

Попробую уточнить. Если я делаю hide() вручную, без привязок к событиям (например, сразу после инициализации окна), а затем даблкликаю по иконке - окно сразу же объявляется. Однако если hide() вызывается из stage.iconifiedProperty().addListener (по нажатию на кнопочку сворачивания), то даблклик приводит к тому, что я описал в первом посту.
Изменен:07 июл 2018 10:19
 
 
Сообщения:297
Upd: изменил код в предыдущем ответе.
 
 
Сообщения:9
Спасибо, завелось! Оказывается надо было вызывать stage.setIconified( false ) при сворачивании. Играл с этим свойством, но не понял, что оно делает.
 
 
Сообщения:9
А не подскажете, как надежно определить, свернуто ли в трей приложение? как-то чекнуть на isHide реально? а то от ручной реализации аля isManuallyHide/isInTray меня слегка ломает...
Изменен:08 июл 2018 04:18
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет