2016-07-22 18 views
2

Ich arbeite an einem Skript, das JDialogs nacheinander auftaucht, um den Benutzer zu bitten, Informationen auf dem Bildschirm (z. B. Felder um ein Menü) zu erfassen usw.), so dass das Programm danach automatisch an die Stelle klickt, an der ich es angegeben habe.Java - Benutzerdefinierte JDialog dispose-Methode mehr als einmal aufgerufen - das ist nicht beabsichtigt

Das Problem, auf dem ich derzeit feststecke, ist, dass, wenn ich meinen benutzerdefinierten JDialog schließe, die dispose-Methode anscheinend mehrmals aufgerufen wird, obwohl ich keine Ahnung habe, warum.

Hier ist ein komplettes Arbeitsbeispiel der Ausgabe:

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

public class Main { 

    // Creates the main frame (MainForm extends JFrame) 
    private static MainForm mainForm = new MainForm(); 

    public static void main(String[] args) { 
     mainForm.setVisible(true); 
    } 

    static class BaseDialog extends JDialog { 
     BaseDialog() { 
      super(); 
      setModal(true); 
     } 

     // Overrides and calls (super)dispose method of JDialog - Nothing unusual 
     @Override 
     public void dispose() { 
      Exception e = new Exception(); 
      e.printStackTrace(); 
      System.out.println("disposing"); 
      super.dispose(); 
     } 
    } 

    static class CaptureDialog extends BaseDialog implements ActionListener { 
     CaptureDialog() { 
      super(); 
      JButton btnInventory = new JButton("Close Me"); 
      btnInventory.addActionListener(this); 
      add(btnInventory); 
      setTitle("Recapture"); 
      setModalityType(ModalityType.APPLICATION_MODAL); 
      setLocationRelativeTo(null); 
      setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
      setResizable(false); 
      setSize(200, 80); 
      setVisible(true); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      System.out.println("Clicked the button!"); 
      dispose(); 
     } 
    } 

    static class MainForm extends JFrame implements ActionListener { 
     MainForm() { 
      super("Example"); 
      JButton btnCapture = new JButton(); 
      btnCapture.setText("Capture"); 
      btnCapture.addActionListener(this); 
      add(btnCapture); 
      setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
      setLocationRelativeTo(null); 
      setSize(200, 80); 
     } 

     // Only one button is added to action listener ('if' not necessary) 
     @Override 
     public void actionPerformed(ActionEvent e){ 
      new CaptureDialog(); 
     } 
    } 
} 

Das Problem ist offensichtlich, wenn ich die Anwendung ausführen und dann öffnen und schließen/den Dialog entsorgen. Mit der Platzierung des Exception e unter BaseDialog ‚s‚entsorgen‘Methode, die ich folgende Ausgabe beim Schließen durch das Fenster‚X‘Taste (Weglassen interne Anrufe):

java.lang.Exception 
    at Main$BaseDialog.dispose(Main.java:24) 
    at javax.swing.JDialog.processWindowEvent(JDialog.java:691) 
    at java.awt.Window.processEvent(Window.java:2017) 
    at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:184) 
    at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:229) 
    at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:227) 
    at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:227) 
    at java.awt.Dialog.show(Dialog.java:1084) 
    at java.awt.Component.show(Component.java:1673) 
    at java.awt.Component.setVisible(Component.java:1625) 
    at java.awt.Window.setVisible(Window.java:1014) 
    at java.awt.Dialog.setVisible(Dialog.java:1005) 
    at Main$CaptureDialog.<init>(Main.java:46) 
    at Main$MainForm.actionPerformed(Main.java:71) 
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252) 
disposing 
disposing 
java.lang.Exception 
    at Main$BaseDialog.dispose(Main.java:24) 
    at java.awt.Window.disposeImpl(Window.java:1161) 
    at java.awt.Window$1DisposeAction.run(Window.java:1189) 
    at java.awt.Window.doDispose(Window.java:1210) 
    at java.awt.Window.dispose(Window.java:1151) 
    at javax.swing.SwingUtilities$SharedOwnerFrame.dispose(SwingUtilities.java:1814) 
    at javax.swing.SwingUtilities$SharedOwnerFrame.windowClosed(SwingUtilities.java:1792) 
    at java.awt.Window.processWindowEvent(Window.java:2061) 
    at javax.swing.JDialog.processWindowEvent(JDialog.java:683) 
    at java.awt.Window.processEvent(Window.java:2017) 

Beachten Sie auch, dass, wenn Wenn Sie die Anwendung ausführen und das Dialogfeld wiederholt öffnen, wird die Methode um 1 inkrementiert (am besten sichtbar, wenn Sie die ersten beiden Zeilen unter der überschriebenen Methode "dispose" in Bezug auf die Ausnahme stacktrace drucken auskommentieren und die Ausgabe beobachten).

Hier ist eine hard-stripped-Version, wenn Sie im Voraus verwenden/sehen und danken möchten!

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

public class Main { 
    public static void main(String[] args) { 
     new MainForm(); 
    } 

    static class BaseDialog extends JDialog { 
     BaseDialog() { 
      super(); 
      setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
      setVisible(true); 
     } 

     @Override 
     public void dispose() { 
      new Exception().printStackTrace(); 
      System.out.println("disposing"); 
      super.dispose(); 
     } 
    } 

    static class MainForm extends JFrame implements ActionListener { 
     MainForm() { 
      super(); 
      JButton btnCapture = new JButton(); 
      btnCapture.setText("Capture"); 
      btnCapture.addActionListener(this); 
      add(btnCapture); 
      setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
      setSize(200, 80); 
      setVisible(true); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e){ 
      new BaseDialog(); 
     } 
    } 
} 

Möglicherweise nützliche Informationen:

  1. Weglassen ‚super.dispose()‘ Ursachen ‚verfügen‘ nur einmal aufgerufen werden - der Anwendungscode nicht das Problem sein sollte.
  2. Wenn ich JFrame anstelle von JDialog (in Bezug auf BaseDialog-Klasse) erweitern, wird die dispose-Methode ordnungsgemäß (mit super.dispose()) aufgerufen.
  3. Sortieren von irrelevanten, aber ein 'Hack' ist ein privates Feld unter der BaseDialog Klasse zu machen:

-> Privater boolean angeordnet = false;

Und dann einen Scheck, wenn die Dispose-Methode aufrufen:

@Override 
public void dispose() { 
    if (!disposed) { 
     disposed = true; 
     super.dispose(); 
    } 
} 

Während es das Problem lösen kann aber weit von einer guten Antwort ist und zukünftige Probleme existieren können leicht, da der Kern nicht richtig festgelegt ist.

+0

Ich konnte nicht antworten, aber jetzt bin ich hier. Ich habe den Code neu formatiert (was ich gestern nicht schnell genug machen konnte, da ich aufstehen musste und es satt hatte, das Problem zu beheben) und ein kurzes, funktionierendes Beispiel hochgeladen. Jetzt sollte es nicht mehr so ​​verwirrend sein wie zuvor. – Smokesick

+0

Drucken Sie den 'hashCode()' aus der Dispose-Methode aus, um zu sehen, welche Instanzen aufgerufen werden. Jedes Mal, wenn die neue Instanz zweimal aufgerufen wird und jede ältere Instanz einmal aufgerufen wird. –

+0

Möglicherweise müssen Sie in den Quellcode eintauchen, um dies zu lösen, da ich noch keinen Grund für sein Auftreten sehe. –

Antwort

1

Das übergeordnete Fenster enthält einen Verweis auf den Dialog und wird das Dialogfeld erneut bereitstellen, wenn es selbst entsorgt wird.

Ihr Problem besteht darin, dass Sie kein übergeordnetes Fenster für den Dialog zuweisen. Aus diesem Grund werden beim erneuten Ablegen des Dialogfelds alle zuvor erstellten Dialogverweise neu sortiert, als ob die Basisklasse die Verweise auf den enthält Kind Fenster. Sie können, indem man das übergeordnete Fenster in den Dialog Super Konstruktor, wie so das Problem lösen:

import javax.swing.*; 

import java.awt.Window; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

public class Main { 

    // Creates the main frame (MainForm extends JFrame) 
    private static MainForm mainForm = new MainForm(); 

    public static void main(String[] args) { 
     mainForm.setVisible(true); 
    } 

    static class BaseDialog extends JDialog { 
     BaseDialog(Window win) { 
      super(win); 
      setModal(true); 
     } 

     // Overrides and calls (super)dispose method of JDialog - Nothing 
     // unusual 
     @Override 
     public void dispose() { 
      Exception e = new Exception(); 
      // e.printStackTrace(); 
      String text = String.format("Disposing. This hashCode: %08X", hashCode()); 
      System.out.println(text); 
      super.dispose(); 
     } 
    } 

    static class CaptureDialog extends BaseDialog implements ActionListener { 
     CaptureDialog(Window win) { 
      super(win); 
      JButton btnInventory = new JButton("Close Me"); 
      btnInventory.addActionListener(this); 
      add(btnInventory); 
      setTitle("Recapture"); 
      setModalityType(ModalityType.APPLICATION_MODAL); 
      setLocationRelativeTo(null); 
      setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
      setResizable(false); 
      setSize(200, 80); 
      setVisible(true); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      System.out.println("Clicked the button!"); 
      dispose(); 
     } 
    } 

    static class MainForm extends JFrame implements ActionListener { 
     MainForm() { 
      super("Example"); 
      JButton btnCapture = new JButton(); 
      btnCapture.setText("Capture"); 
      btnCapture.addActionListener(this); 
      add(btnCapture); 
      setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
      setLocationRelativeTo(null); 
      setSize(200, 80); 
     } 

     // Only one button is added to action listener ('if' not necessary) 
     @Override 
     public void actionPerformed(ActionEvent e) { 
      new CaptureDialog(MainForm.this); 
     } 
    } 
} 

Ich selbst vermeiden neu zu erstellen, den Dialog, wenn ich denke, dass ich es brauchen könnte, was unnötige Anhäufung verhindern würde Verweise.Siehe Code unten und kommentieren und Kommentar- der Abschnitt angegeben, um zu sehen, was ich meine:

import java.awt.Window; 
import java.awt.Component; 
import java.awt.event.ActionEvent; 
import java.awt.event.KeyEvent; 

import javax.swing.*; 

public class MainSimple extends JPanel { 
    private JDialog dialog; 

    public MainSimple() { 
     add(new JButton(new OpenDialogAction("Open Dialog", KeyEvent.VK_O))); 
     add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X))); 
    } 

    private class OpenDialogAction extends AbstractAction { 
     public OpenDialogAction(String name, int mnemonic) { 
      super(name); 
      putValue(MNEMONIC_KEY, mnemonic); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      boolean test = true; 
      // test = (dialog == null); // ***** comment or uncomment this line ***** 
      if (test) { 
       Window win = SwingUtilities.getWindowAncestor(MainSimple.this); 
       dialog = new MyDialog(win); 
       dialog.pack(); 
       dialog.setLocationRelativeTo(win); 
      } 
      dialog.setVisible(true);    
     } 
    } 

    private class MyDialog extends JDialog { 

     public MyDialog(Window win) { 
      super(win, "My Dialog", ModalityType.APPLICATION_MODAL); 
      add(new JButton(new DisposeAction("Close", KeyEvent.VK_C))); 
      setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
     } 

     @Override 
     public void dispose() { 
      String text = String.format("Disposing. This hashCode: %08X", hashCode()); 
      System.out.println(text); 
      super.dispose(); 
     } 
    } 

    private class DisposeAction extends AbstractAction { 

     public DisposeAction(String name, int mnemonic) { 
      super(name); 
      putValue(MNEMONIC_KEY, mnemonic); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      Component source = (Component) e.getSource(); 
      Window win = SwingUtilities.getWindowAncestor(source); 
      win.dispose(); 
     } 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(() -> createAndShowGui()); 
    } 

    private static void createAndShowGui() { 
     MainSimple mainPanel = new MainSimple(); 
     JFrame frame = new JFrame("Main"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(mainPanel); 
     frame.pack(); 
     frame.setLocationByPlatform(true); 
     frame.setVisible(true); 
    } 

} 

Auch hier beachten Sie, dass die Mutter JFrame Verweise auf alle Dialoge hält erstellt, ob sie entsorgt worden sind oder nicht, und wieder entsorgt sie alle beim Schließen.

+0

Ja, das scheint das Problem jetzt ziemlich gut zu lösen. Auch als Nebenfrage, wie würden Sie beraten, wenn es darum geht, Dialoge sequenziell zu zeigen? Wie gehen Sie verschiedene Schritte durch, damit Benutzereingaben stattfinden und die Sichtbarkeit von übergeordneten Dialogen verwalten? Oder ist das wahrscheinlich für einen anderen Thread? So oder so, danke für die Hilfe! – Smokesick

+0

Nur ein einfacher Fall, an den ich dachte, ist, dass ich einen Dialog mit einem Knopf habe. Wenn ich auf den Knopf klicke, will ich, dass ein zweiter Dialog geöffnet wird, während der erste versteckt wird (damit er visuell nicht stört) und wenn ich den zweiten schließe, um den ersten wieder anzuzeigen, ohne dass die Ausführung außerhalb des Bereichs des ersten Dialogs fortgesetzt wird zwischen? Auch das ist wahrscheinlich am besten mit einem separaten Thread, wenn Sie das glauben. – Smokesick

+0

@Smokesick: Wahrscheinlich wäre der Basis-JFrame das übergeordnete Fenster dieses Beispiels, da der erste Dialog nicht sichtbar ist. Aber ich selbst würde wahrscheinlich einen Dialog zeigen und Ansichten über ein CardLayout austauschen. –