2015-12-23 1 views
3

Je veux développer une application qui utilise controlsfx Notifications pour afficher certaines notifications en mode System tray. En mode normal, mon application fonctionne bien et la notification peut être affichée avec succès. Mais lorsque je masque une étape dans la barre d'état système, une exception NullPointerException se produit. Je ne sais pas comment je peux résoudre ce problème.javafx NullPointerException avec controlsfx Notifications componnets

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

} 

Et ceci est mon contrôleur Classe:

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

} 
+0

Chercher dans ce site pour « Qu'est-ce qu'un NullPointerException et comment puis-je résoudre ce problème? » – csmckelvey

Répondre

2

Je me rends compte que cette exception se produit lorsque la scène va se cacher le mode et le composant de notification ne peut pas trouver étape lorsque la notification doit montrer au stade. Après avoir cherché sur Internet, je trouve deux solutions à ce problème.

Solution 1:

Ouvrir la scène et la notification du spectacle. De cette façon, nous devrions vérifier que si la scène était cachée, ouvrez-la et affichez une notification. Pour ce faire, il faut ajouter cette condition dans CreateNotification Méthode:

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

Solution 2:

Dans cette solution, nous créons une étape factice et définissez son opacité à zéro et après cela, cacher la principale étape. Je trouve cette solution à ce lien et placez le code dans 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(); 
} 

Comme je l'ai mentionné ci-dessous, nous devrions appeler cette méthode avant de se cacher la scène principale:

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

je mets en œuvre cette solution à deux et tout fonctionne bien. Si vous avez une meilleure solution à ce problème s'il vous plaît mettre ici

Vous pouvez télécharger le projet complet Netbeans de mon Dropbox

+0

En utilisant la deuxième solution, faites-vous quelque chose pour vous débarrasser des popups factices? – Tommo