Comment fermer une application Java Swing à partir du code

94

Quelle est la bonne façon de mettre fin à une application Swing à partir du code et quels sont les pièges?

J'avais essayé de fermer mon application automatiquement après le déclenchement d'une minuterie. Mais le simple fait d'appeler dispose()le JFramen'a pas fait l'affaire - la fenêtre a disparu mais l'application ne s'est pas arrêtée. Cependant, lors de la fermeture de la fenêtre avec le bouton de fermeture, l'application se termine. Que devrais-je faire?

hstoerr
la source
Veuillez publier un extrait de code de votre minuterie.
James Schek

Réponses:

106

Votre action de fermeture par défaut JFrame peut être définie sur " DISPOSE_ON_CLOSE" au lieu de EXIT_ON_CLOSE(pourquoi les gens continuent à utiliser EXIT_ON_CLOSE me dépasse).

Si vous avez des fenêtres non éliminées ou des threads non démon, votre application ne se terminera pas. Cela devrait être considéré comme une erreur (et le résoudre avec System.exit est une très mauvaise idée).

Les coupables les plus courants sont java.util.Timer et un thread personnalisé que vous avez créé. Les deux doivent être définis sur daemon ou doivent être explicitement supprimés.

Si vous souhaitez vérifier toutes les images actives, vous pouvez utiliser Frame.getFrames(). Si tous les Windows / Frames sont supprimés, utilisez un débogueur pour rechercher tous les threads non démon en cours d'exécution.

James Schek
la source
Dans mon cas, j'ai découvert avec le débogueur que j'avais un Swingworker toujours actif. J'ai appelé sa méthode cancel (true) dans le WindowClose Eventlistener et le programme se termine maintenant. Merci!
Hans-Peter Störr
Notez que vous devrez peut-être appeler dispose sur chaque trame, ce qui est généralement "suffisant" (bien que définir l'action de fermeture par défaut sur EXIT_ON_CLOSE ne soit probablement pas une mauvaise idée non plus).
rogerdpack
Que faire si vous avez une fenêtre en ouvrant une autre, mais sans se débarrasser d'elle-même, afin de pouvoir l'utiliser pour une backfenêtre? Si la deuxième fenêtre est ensuite fermée et que vous utilisez DISPOSE_ON_CLOSEle programme ne se termine pas parce que la première fenêtre est toujours "non disposée" ... y a-t-il un moyen de résoudre cela sans utiliser DISPOSE_ON_CLOSE?
Svend Hansen
La première fenêtre doit s'ajouter en tant qu'auditeur à la deuxième fenêtre et prendre les mesures appropriées.
James Schek
3
L'utilisation de EXIT_ON_CLOSE mettra fin de force à l'application. Si vous devez prendre des mesures avant la fermeture du programme (comme enregistrer les données de travail), vous devez alors accéder aux gestionnaires d'événements JVM au lieu d'utiliser simplement les gestionnaires d'événements swing normaux. Les deux "fonctionneront", mais EXIT_ON_CLOSE posera des problèmes pour les applications plus complexes.
James Schek
34

Je suppose que EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

avant System.exit(0)c'est mieux car vous pouvez écrire un écouteur de fenêtre pour effectuer certaines opérations de nettoyage avant de quitter réellement l'application.

Cet écouteur de fenêtre vous permet de définir:

public void windowClosing(WindowEvent e) {
    displayMessage("WindowListener method called: windowClosing.");
    //A pause so user can see the message before
    //the window actually closes.
    ActionListener task = new ActionListener() {
        boolean alreadyDisposed = false;
        public void actionPerformed(ActionEvent e) {
            if (frame.isDisplayable()) {
                alreadyDisposed = true;
                frame.dispose();
            }
        }
    };
    Timer timer = new Timer(500, task); //fire every half second
    timer.setInitialDelay(2000);        //first delay 2 seconds
    timer.setRepeats(false);
    timer.start();
}

public void windowClosed(WindowEvent e) {
    //This will only be seen on standard output.
    displayMessage("WindowListener method called: windowClosed.");
}
VonC
la source
16

Essayer:

System.exit(0);

Brut, mais efficace.

Daniel Spiewak
la source
Malheureusement, c'est trop grossier pour moi. Je veux que les événements de fermeture de fenêtre soient traités pour certaines actions de nettoyage. OK, je pourrais faire un System.exit avec un SwingUtils.invokeLater, mais je préfère faire la bonne chose.
Hans-Peter Störr
5
System.exit (0) n'est pas seulement grossier, c'est mal. Vérifiez toutes les trames, appelez disposer. Si cela échoue, exécutez un débogueur et voyez quels threads non démon sont toujours actifs.
James Schek
Il y a eu beaucoup de bogues même dans le JDK qui ont laissé le processus. Donc +1 pour exit (), mais vous voudrez peut-être le désactiver dans les versions de débogage.
Tom Hawtin - tackline
11

Peut-être que le moyen le plus sûr est quelque chose comme:

    private JButton btnExit;
    ...
    btnExit = new JButton("Quit");      
    btnExit.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e){
            Container frame = btnExit.getParent();
            do 
                frame = frame.getParent(); 
            while (!(frame instanceof JFrame));                                      
            ((JFrame) frame).dispose();
        }
    });
Kachwahed
la source
3
Assez mauvais style pour nommer une variable Frameavec une majuscule, exactement comme le nom d'une classe…
Jean-Philippe Pellet
Merci beaucoup! C'est l'un des meilleurs moyens!
rzaaeeff
9

Le programme suivant inclut du code qui mettra fin à un programme dépourvu de threads superflus sans appeler explicitement System.exit (). Afin d'appliquer cet exemple aux applications utilisant des threads / écouteurs / minuteries / etc, il suffit d'insérer un code de nettoyage demandant (et, le cas échéant, en attente) leur terminaison avant que WindowEvent ne soit lancé manuellement dans actionPerformed ().

Pour ceux qui souhaitent copier / coller du code capable de fonctionner exactement comme indiqué, une méthode principale légèrement moche mais autrement non pertinente est incluse à la fin.

public class CloseExample extends JFrame implements ActionListener {

    private JButton turnOffButton;

    private void addStuff() {
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        turnOffButton = new JButton("Exit");
        turnOffButton.addActionListener(this);
        this.add(turnOffButton);
    }

    public void actionPerformed(ActionEvent quitEvent) {
        /* Iterate through and close all timers, threads, etc here */
        this.processWindowEvent(
                new WindowEvent(
                      this, WindowEvent.WINDOW_CLOSING));
    }

    public CloseExample() {
        super("Close Me!");
        addStuff();
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                CloseExample cTW = new CloseExample();
                cTW.setSize(200, 100);
                cTW.setLocation(300,300);
                cTW.setVisible(true);
            }
        });
    }
}
Eric Sadler
la source
4

Si je vous comprends bien, vous souhaitez fermer l'application même si l'utilisateur n'a pas cliqué sur le bouton de fermeture. Vous devrez peut-être enregistrer WindowEvents avec addWindowListener () ou enableEvents () selon ce qui convient le mieux à vos besoins.

Vous pouvez ensuite appeler l'événement avec un appel à processWindowEvent (). Voici un exemple de code qui créera un JFrame, attendra 5 secondes et fermera le JFrame sans intervention de l'utilisateur.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class ClosingFrame extends JFrame implements WindowListener{

public ClosingFrame(){
    super("A Frame");
    setSize(400, 400);
            //in case the user closes the window
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setVisible(true);
            //enables Window Events on this Component
            this.addWindowListener(this);

            //start a timer
    Thread t = new Timer();
            t.start();
    }

public void windowOpened(WindowEvent e){}
public void windowClosing(WindowEvent e){}

    //the event that we are interested in
public void windowClosed(WindowEvent e){
    System.exit(0);
}

public void windowIconified(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowActivated(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}

    //a simple timer 
    class Timer extends Thread{
           int time = 10;
           public void run(){
     while(time-- > 0){
       System.out.println("Still Waiting:" + time);
               try{
                 sleep(500);                     
               }catch(InterruptedException e){}
             }
             System.out.println("About to close");
    //close the frame
            ClosingFrame.this.processWindowEvent(
                 new WindowEvent(
                       ClosingFrame.this, WindowEvent.WINDOW_CLOSED));
           }
    }

    //instantiate the Frame
public static void main(String args[]){
          new ClosingFrame();
    }

}

Comme vous pouvez le voir, la méthode processWindowEvent () provoque le déclenchement de l'événement WindowClosed lorsque vous avez l'opportunité de faire du code de nettoyage si vous en avez besoin avant de fermer l'application.

Vincent Ramdhanie
la source
windowClosed doit appeler dispose () et non System.exit (0).
James Schek
2

Jetez un œil à la documentation Oracle .

À partir du JDK 1.4, une application se termine si:

  • Il n'y a pas de composants AWT ou Swing affichables.
  • Il n'y a aucun événement natif dans la file d'attente des événements native.
  • Il n'y a pas d'événements AWT dans java EventQueues.

Cornercases:

Le document indique que certains packages créent des composants affichables sans les libérer. Un programme qui appelle Toolkit.getDefaultToolkit () ne se terminera pas. est entre autres donné à titre d'exemple.

D'autres processus peuvent également maintenir AWT en vie lorsqu'ils, pour une raison quelconque, envoient des événements dans la file d'attente d'événements native.

J'ai également remarqué que sur certains systèmes, il faut quelques secondes avant que l'application se termine réellement.

Reiner Lange
la source
0

Je pense que l'idée est ici le WindowListener - vous pouvez y ajouter n'importe quel code que vous souhaitez exécuter avant que la chose ne s'arrête

Tamas Rev
la source
0

En réponse à d'autres commentaires, DISPOSE_ON_CLOSE ne semble pas quitter correctement l'application - il détruit seulement la fenêtre, mais l'application continuera de fonctionner. Si vous souhaitez mettre fin à l'application, utilisez EXIT_ON_CLOSE.

JSideris
la source
Cette réponse suggère l'exact opposé de la réponse de James Schek. Laquelle est correcte? (quand je teste avec une application simple, les deux semblent fonctionner ...)
OR Mapper
Je ne me souviens pas comment j'ai testé ça maintenant.
JSideris