Remove Top-Level Container on Runtime

后端 未结 5 1296
深忆病人
深忆病人 2020-11-21 13:46

Unfortunately, it looks like this recently closed question was not well understood. Here is the typical output:

run:
    Trying to Remove JDialog
    Remove          


        
5条回答
  •  感情败类
    2020-11-21 14:21

    I have completely reworked your example:

    • I have simplified what was not needed (setLocation(), unused constructor...)
    • I have removed the code that triggers a WINDOW_CLOSING event (useless)
    • I have removed code that resets all windows to visible again (which would prevent GC on them)
    • I have used a javax.swing.Timer instead of a Thread for disposing of the dialog
    • I have used a Thread for forcing GC (not a good idea in the EDT)
    • I have modified the final success criterion to check that Window.getWindows() is 2 (not 1), because in Swing, if you open a dialog with no parent, then a special invisible frame will be created to use it as parent (for all ownerless dialogs actually), once created, that frame cannot be removed.

    The resulting snippet follows:

    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.*;
    
    public class RemoveDialogOnRuntime extends JFrame {
    
        private static final long serialVersionUID = 1L;
        private boolean runProcess;
        private int maxLoop = 0;
        private Timer timer;
    
        public RemoveDialogOnRuntime() {
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setPreferredSize(new Dimension(300, 300));
            setTitle("Remove Dialog On Runtime");
            setLocation(150, 150);
            pack();
            setVisible(true);
            addNewDialog();
        }
    
        private void addNewDialog() {
            DialogRemove firstDialog = new DialogRemove();
            remWins();
        }
    
        private void remWins() {
            runProcess = true;
            timer = new Timer(1000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (runProcess) {
                        for (Window win: Window.getWindows()) {
                            if (win instanceof JDialog) {
                                System.out.println("    Trying to Remove JDialog");
                                win.dispose();
                            }
                        }
                        System.out.println("    Remove Cycle Done :-)");
                        runProcess = false;
                        new Thread() {
                            @Override
                            public void run() {
                                try {
                                    Thread.sleep(100);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                Runtime.getRuntime().gc();
                            }
                        }.start();
                    } else {
                        pastRemWins();
                        runProcess = true;
                    }
                }
            });
            timer.setRepeats(true);
            timer.start();
        }
    
        private void pastRemWins() {
            System.out.println("    Checking if still exists any of TopLayoutContainers");
            Window[] wins = Window.getWindows();
            for (int i = 0; i < wins.length; i++) {
                if (wins[i] instanceof JFrame) {
                    System.out.println("JFrame");
                } else if (wins[i] instanceof JDialog) {
                    System.out.println("JDialog");
                } else {
                    System.out.println(wins[i].getClass().getSimpleName());
                }
            }
            // We must expect 2 windows here: this (RemoveDialogOnRuntime) and the parent of all parentless dialogs
            if (wins.length > 2) {
                wins = null;
                maxLoop++;
                if (maxLoop <= 3) {
                    System.out.println("    Will Try Remove Dialog again, CycleNo. " + maxLoop);
                    System.out.println(" -----------------------------------------------------------");
                    remWins();
                } else {
                    System.out.println(" -----------------------------------------------------------");
                    System.out.println("*** End of Cycle Without Success, Exit App ***");
                    closeMe();
                }
            } else {
                timer.stop();
            }
        }
    
        private void closeMe() {
            System.exit(0);
        }
    
        private class DialogRemove extends JDialog {
    
            private static final long serialVersionUID = 1L;
    
            private DialogRemove() {
                setTitle("SecondDialog");
                setPreferredSize(new Dimension(200, 200));
                setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
                setModalityType(Dialog.ModalityType.MODELESS);
                pack();
                setVisible(true);
            }
        }
    
        public static void main(String args[]) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    RemoveDialogOnRuntime superConstructor = new RemoveDialogOnRuntime();
                }
            });
        }
    }
    

    The important conclusions are:

    • You can't remove the invisible frame created by Swing as parent of all ownerless dialogs
    • You have to force a GC for the disposed dialog to be removed from Window.getWindows() (that one looks like a bug to me but I think the reason is that Swing keeps a WeakReference to all windows, and this WeakReference is not released until a GC has occurred.

    Hope this gives a clear and complete answer to your problem.

提交回复
热议问题