代码之家  ›  专栏  ›  技术社区  ›  Nate Parsons

关闭模态框架

  •  3
  • Nate Parsons  · 技术社区  · 15 年前

    我遵循了方法2 this guide ,所以现在我有了一个modalinternalframe,它可以按我的要求阻止所有其他帧的输入。但是,我对示例做了一个更改,现在有两个问题。

    变化

    我删除了joptionpane,因为整个要点是显示我自己的窗格。为了接近它,我把 closeable true ,并添加了一个InternalFrameListener,其代码与joptionPane的示例侦听器相同。这不起作用,所以我还在doDefaultCloseAction的末尾添加了代码。

    问题

    1. 内部框架永远不会消失。我想有人抛出了一个例外但是…
    2. 我看不到任何抛出的异常,也不知道它们要去哪里。通常在调试模式下,eclipse会在异常被赋予uncaughtexceptionhandler之前停止,但在这种情况下不会发生这种情况。

    守则

    如果我对这个问题的描述没有帮助,这是我的modalinternalframe版本。如果你想要更多的代码,我也可以发布。很抱歉这么长,但我尽量使它简洁。

    public class ModalInternalFrame extends JInternalFrame {
        public ModalInternalFrame(String title, JRootPane rootPane,
                Component desktop) {
            super(title, false, true, false, false);
    
            // create opaque glass pane
            final JPanel glass = new JPanel();
            glass.setOpaque(false);
    
            // Attach mouse listeners
            MouseInputAdapter adapter = new MouseInputAdapter() { };
            glass.addMouseListener(adapter);
            glass.addMouseMotionListener(adapter);
    
            this.addInternalFrameListener(new InternalFrameListenerAdapter() {
                public void internalFrameClosed(InternalFrameEvent e) { close(); }
                public void internalFrameClosing(InternalFrameEvent e){ close(); }
            });
    
            // Change frame border
            putClientProperty("JInternalFrame.frameType", "optionDialog");
    
            // Size frame
            Dimension size = getPreferredSize();
            Dimension rootSize = desktop.getSize();
    
            setBounds((rootSize.width - size.width) / 2,
                    (rootSize.height - size.height) / 2, size.width, size.height);
            desktop.validate();
            try { setSelected(true); } 
            catch (PropertyVetoException ignored) { }
    
            glass.add(this);              // Add modal internal frame to glass pane
            rootPane.setGlassPane(glass); // Change glass pane to our panel
            glass.setVisible(true);       // Show glass pane, then modal dialog
        }
    
        private void close(){
            if (isVisible()) {
                try { setClosed(true); } 
                catch (PropertyVetoException ignored) { }
                setVisible(false);
                rootPane.getGlassPane().setVisible(false);
            }
        }
    
        @Override public void doDefaultCloseAction() {
            super.doDefaultCloseAction();
            close();
        }
    
        @Override public void setVisible(boolean flag) {
            super.setVisible(flag);
            if (flag) startModal();
            else stopModal();
        }
    
        private synchronized void startModal() {
            try {
                if (SwingUtilities.isEventDispatchThread()) {
                    EventQueue theQueue = getToolkit().getSystemEventQueue();
                    while (isVisible()) {
                        AWTEvent event = theQueue.getNextEvent();
                        Object source = event.getSource();
                        if (event instanceof ActiveEvent) {
                            ((ActiveEvent) event).dispatch();
                        } else if (source instanceof Component) {
                            ((Component) source).dispatchEvent(event);
                        } else if (source instanceof MenuComponent) {
                            ((MenuComponent) source).dispatchEvent(event);
                        } else {
                            System.err.println("Unable to dispatch: " + event);
                        }
                    }
                } else { while (isVisible()) { wait(); } }
            } catch (InterruptedException ignored) {
            }
    
        }
    
        private synchronized void stopModal() { notifyAll(); }
    
    }
    

    更新: 我发现模态对话框很适合我的需求,但是如果有人真的有主意,我很高兴听到。有一件事我没有尝试过,那就是用try{}catch(exception e){}包装每个方法,这可能会有很大帮助。

    5 回复  |  直到 15 年前
        1
  •  2
  •   Barett ShuftY    9 年前

    试试这个。我是从Jinternal Frames上的Webby IT博客上得到的: http://webbyit.blogspot.com/2011/03/managing-jinternalframes-within.html

    import java.awt.*;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyVetoException;
    import java.beans.VetoableChangeListener;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.swing.*;
    import javax.swing.event.InternalFrameAdapter;
    import javax.swing.event.InternalFrameEvent;
    
    /**
     * An extended <code>JInternalFrame</code> that provides modality in a child/parent hierarchy.
     * <a href="From http://webbyit.blogspot.com/2011/03/managing-jinternalframes-within.html">source: webby it internal frames blog post</a>
     *
     * @author webbyit
     */
    public class ModalityInternalFrame extends JInternalFrame {
    
        protected JDesktopPane desktopPane;
        protected JComponent parent;
        protected ModalityInternalFrame childFrame;
        protected JComponent focusOwner;
        private boolean wasCloseable;
    
        public ModalityInternalFrame() {
            init(); // here to allow netbeans to use class in gui builder
        }
    
        public ModalityInternalFrame(JComponent parent) {
            this(parent, null);
        }
    
        public ModalityInternalFrame(JComponent parent, String title) {
            this(parent, title, false);
        }
    
        public ModalityInternalFrame(JComponent parent, String title, boolean resizable) {
            this(parent, title, resizable, false);
        }
    
        public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable) {
            this(parent, title, resizable, closeable, false);
        }
    
        public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable,
                boolean maximizable) {
            this(parent, title, resizable, closeable, maximizable, false);
        }
    
        public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable,
                boolean maximizable,
                boolean iconifiable) {
            super(title, resizable, closeable, maximizable, iconifiable);
            setParentFrame(parent);
            //setFocusTraversalKeysEnabled(false);
            if (parent != null && parent instanceof ModalityInternalFrame) {
                ((ModalityInternalFrame) parent).setChildFrame(ModalityInternalFrame.this);
    
                /*
                 * set focus to the new frame and show the frame Code added by Jasir
                 */
                try {
                    ((ModalityInternalFrame) parent).setSelected(false);
                    setSelected(true);
                    setVisible(true);
                } catch (PropertyVetoException ex) {
                    Logger.getLogger(ModalityInternalFrame.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
    
            // Add glass pane
            ModalityInternalGlassPane glassPane = new ModalityInternalGlassPane(this);
            setGlassPane(glassPane);
    
    
            // Add frame listeners
            addFrameListener();
    
            // Add frame veto listenr
            addFrameVetoListener();
    
            init();
    
    
            // calculate size and position
    
    
        }
    
        private void setParentFrame(JComponent parent) {
            desktopPane = JOptionPane.getDesktopPaneForComponent(parent);
            this.parent = parent == null ? JOptionPane.getDesktopPaneForComponent(parent) : parent; // default to desktop if no parent given
        }
    
        public JComponent getParentFrame() {
            return parent;
        }
    
        public void setChildFrame(ModalityInternalFrame childFrame) {
            this.childFrame = childFrame;
        }
    
        public ModalityInternalFrame getChildFrame() {
            return childFrame;
        }
    
        public boolean hasChildFrame() {
            return (childFrame != null);
        }
    
        protected void addFrameVetoListener() {
            addVetoableChangeListener(new VetoableChangeListener() {
    
                public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
                    if (evt.getPropertyName().equals(JInternalFrame.IS_SELECTED_PROPERTY)
                            && evt.getNewValue().equals(Boolean.TRUE)) {
                        if (hasChildFrame()) {
                            //childFrame.setSelected(true);
                            if (childFrame.isIcon()) {
                                childFrame.setIcon(false);
                            }
                            throw new PropertyVetoException("no!", evt);
                        }
                    }
                }
            });
        }
    
        /**
         * Method to control the display of the glass pane, dependant on the frame
         * being active or not
         */
        protected synchronized void addFrameListener() {
            addInternalFrameListener(new InternalFrameAdapter() {
    
                @Override
                public void internalFrameActivated(InternalFrameEvent e) {
                    if (hasChildFrame() == true) {
                        getGlassPane().setVisible(true);
                        grabFocus();
                    } else {
                        getGlassPane().setVisible(false);
                    }
                }
    
                @Override
                public void internalFrameOpened(InternalFrameEvent e) {
                    getGlassPane().setVisible(false);
                    try {
                        setSelected(true);
                    } catch (PropertyVetoException ex) {
                        Logger.getLogger(ModalityInternalFrame.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
    
                @Override
                public void internalFrameClosing(InternalFrameEvent e) {
                    if (parent != null && parent instanceof ModalityInternalFrame) {
                        ((ModalityInternalFrame) parent).childClosing();
                    }
                }
            });
        }
    
        /**
         * Method to handle child frame closing and make this frame available for
         * user input again with no glass pane visible
         */
        protected void childClosing() {
            setClosable(wasCloseable);
            getGlassPane().setVisible(false);
            if (focusOwner != null) {
                java.awt.EventQueue.invokeLater(new Runnable() {
    
                    @Override
                    public void run() {
                        try {
                            moveToFront();
                            setSelected(true);
                            focusOwner.grabFocus();
                        } catch (PropertyVetoException ex) {
                        }
                    }
                });
                focusOwner.grabFocus();
            }
            getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            setChildFrame(null);
            getDesktopPane().setSelectedFrame(this);
            System.out.println(getDesktopPane().getSelectedFrame());
        }
    
        /*
         * Method to handle child opening and becoming visible.
         */
        protected void childOpening() {
            // record the present focused component
            wasCloseable = isClosable();
            setClosable(false);
            focusOwner = (JComponent) getMostRecentFocusOwner();
            grabFocus();
            getGlassPane().setVisible(true);
            getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        }
    
        @Override
        public void show() {
            if (parent != null && parent instanceof ModalityInternalFrame) {
                // Need to inform parent its about to lose its focus due
                // to child opening
                ((ModalityInternalFrame) parent).childOpening();
            }
            calculateBounds();
            super.show();
        }
    
        protected void init() {
            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 394, Short.MAX_VALUE));
            layout.setVerticalGroup(
                    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 274, Short.MAX_VALUE));
    
            pack();
        }
    
        public void calculateBounds() {
            Dimension frameSize = getPreferredSize();
            Dimension parentSize = new Dimension();
            Dimension rootSize = new Dimension(); // size of desktop
            Point frameCoord = new Point();
    
            if (desktopPane != null) {
                rootSize = desktopPane.getSize(); // size of desktop
                frameCoord = SwingUtilities.convertPoint(parent, 0, 0, desktopPane);
                parentSize = parent.getSize();
            }
    
            //setBounds((rootSize.width - frameSize.width) / 2, (rootSize.height - frameSize.height) / 2, frameSize.width, frameSize.height);
    
            // We want dialog centered relative to its parent component
            int x = (parentSize.width - frameSize.width) / 2 + frameCoord.x;
            int y = (parentSize.height - frameSize.height) / 2 + frameCoord.y;
    
            // If possible, dialog should be fully visible
            int ovrx = x + frameSize.width - rootSize.width;
            int ovry = y + frameSize.height - rootSize.height;
            x = Math.max((ovrx > 0 ? x - ovrx : x), 0);
            y = Math.max((ovry > 0 ? y - ovry : y), 0);
            setBounds(x, y, frameSize.width, frameSize.height);
        }
    
        /**
         * Glass pane to overlay. Listens for mouse clicks and sets selected on
         * associated modal frame. Also if modal frame has no children make class
         * pane invisible
         */
        class ModalityInternalGlassPane extends JComponent {
    
            private ModalityInternalFrame modalFrame;
    
            public ModalityInternalGlassPane(ModalityInternalFrame frame) {
                modalFrame = frame;
                addMouseListener(new MouseAdapter() {
    
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        if (modalFrame.isSelected() == false) {
                            try {
                                modalFrame.setSelected(true);
                                if (modalFrame.hasChildFrame() == false) {
                                    setVisible(false);
                                }
                            } catch (PropertyVetoException e1) {
                                //e1.printStackTrace();
                            }
                        }
                    }
                });
            }
    
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.setColor(new Color(255, 255, 255, 100));
                g.fillRect(0, 0, getWidth(), getHeight());
            }
        }
    }
    
        2
  •  1
  •   David Moles paddy-p    15 年前

    我无法让您的代码运行,但这里有一个更简单的版本,基于sun示例,它确实有效——主框架中有一个按钮(占用了所有可用空间),但单击该按钮将被阻止,直到内部框架关闭。

    你看,我所做的就是 new JOptionPane().createInternalFrame() 用我自己的框架做生意。我的猜测是,当你试图做自己的事件调度时,你正在过度复杂化事情。

    还是我遗漏了什么?

    public class Foo {
    
      public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setSize(600, 400);
        frame.setLocationByPlatform(true);
    
        JButton desktop = new JButton(new AbstractAction("Click me if you can") {
          @Override
          public void actionPerformed(ActionEvent e) {
            System.out.println("I have been clicked");
          }
        });
        frame.getContentPane().add(desktop);
    
        frame.setVisible(true);
    
        JInternalFrame modal = 
          new JInternalFrame("Modal Popup", false, true, false, false);
        JLabel popupContent = new JLabel("I am the popup");
        popupContent.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        modal.add(popupContent);
        modal.pack();
    
        JPanel glass = new JPanel();
        glass.setOpaque(false);
        glass.add(modal);
        frame.setGlassPane(glass);
        glass.setVisible(true);
        modal.setVisible(true);
    
        modal.addInternalFrameListener(new ModalAdapter(glass));
      }
    }
    
    class ModalAdapter extends InternalFrameAdapter {
      Component glass;
    
      public ModalAdapter(Component glass) {
        this.glass = glass;
    
        // Associate dummy mouse listeners
        // Otherwise mouse events pass through
        MouseInputAdapter adapter = new MouseInputAdapter() {
        };
        glass.addMouseListener(adapter);
        glass.addMouseMotionListener(adapter);
      }
    
      public void internalFrameClosed(InternalFrameEvent e) {
        glass.setVisible(false);
      }
    }
    
        3
  •  1
  •   Mr. Zen    14 年前

    我只是为了一个项目才做这件事。我所做的就是把主窗口对象传递给jinternalframe。主对象有一个信号量,用于跟踪模态是否被锁定。在关闭jinternalframe(扩展)时,调用主对象的信号量。很简单。 这不是主要的代码,但你会想到:

    //called frame
    public CallingFrame parent;
    public void setParent(CallingFrame parent_){
        this.parent=parent_;
    }
    private void frameClosed(javax.swing.event.InternalFrameEvent evt) {
         parent.modalLocked=false; 
    }
    

    在我的例子中,应用程序使用带有图像部分的标签来调用内部框架,因此代码以

     //calling frame     
     CalledFrame cf=new CalledFrame();
     cf.setParent(this); 
     cf.setVisible(true);  
     modalLoacked=true;
     private void jLabel1MouseReleased(java.awt.event.MouseEvent evt) {
        if (modalLocked)
        return;
        else// (do your things)
      }
    

    我遵循了教程的内容,但大多数教程都过于复杂,因为一个信号量会在一个称为frame的区域没有关闭时,不让您单击任何区域。

        4
  •  0
  •   objects    15 年前
         public void internalFrameClosing(InternalFrameEvent e){ close(); }
    

    调用close()将导致再次调用internalFrameClosing(),直到堆栈溢出。

    试着完全删除那个侦听器。

        5
  •  -2
  •   BaCaRoZzo Indie Dev    9 年前

    你可以加上 setClosable(true); 在构造器中