声明:本文章中介绍的内容可以在GPF项目中找到相应的实现。
 
GPF计划使用MDI的处理模式,Java的JDesktopPane和JInternalFrame已经为我们实现了这样的功能。但是,在将JInternalFrame最大化的时候,JDesktopPane和我们熟知的其他软件,如Word等,具有不同的处理方式。此时可以通过这里介绍的内容弥补这一缺点,使Swing的程序更类似于本地实现。
 
首先先看一下怎么使用JDesktopPane和JInternalFrame实现MDI。其实实现的方式很简单,就是在一个窗口中添加JDesktopPane,然后使用JDesktopPane的add方法,添加JInternalFrame的实例即可。具体的代码片段如下所示:
 
//...
final JDesktopPane desktopPane = new JDesktopPane();
//...
Container content = getContentPane(); 
content.add(desktopPane, BorderLayout.CENTER);
//...
InnerFrame iFrame = new InnerFrame();
iFrame.setVisible( true);
desktopPane.add(iFrame);
//...
 
由此即可按照Java提供的方式添加MDI的实现。我们可以看到它的运行截图如下所示:
使用JInternalFrame实现MDI_第1张图片
 
使用JInternalFrame实现MDI_第2张图片
 
可以看到,当JInternalFrame最大化的时候,仅仅是充满了整个JDesktopPane,并不会像一般的MDI程序一样,将最大化最小化的按钮显示在菜单栏。这是因为JInternalFrame是一个轻量级组件,必须依附在容器之中,因此它的大小不可能超出JDesktopPane。为了获得我们想要的效果,我们对JInternalFrame的UI进行改写:
 
class InnerFrame extends JInternalFrame {
  
   /** The is hidden. */
   boolean isHidden = false;
  
   /** The old ui. */
  BasicInternalFrameUI oldUi = null;
  
   /**
   * Instantiates a new inner frame.
   */

   public InnerFrame() {
      oldUi = (BasicInternalFrameUI)getUI();
      setSize(200, 200);
      maximizable = true;
      closable = true;
      addComponentListener( new ComponentAdapter() { 
          public void componentResized(ComponentEvent e) {
            InnerFrame selectedFrame = (InnerFrame)e.getSource();
            if(selectedFrame.isMaximum()){
              selectedFrame.hideNorthPanel();
              opPane.setVisible( true); 
              try {
                 selectedFrame.setMaximum( true);
              } catch (PropertyVetoException ex) {
                 ex.printStackTrace();
              }
          }
      } 
    });
  }
  
   /**
   * Hide north panel.
   */

   public void hideNorthPanel(){
      ((BasicInternalFrameUI) this.getUI()).setNorthPane( null);
       this.putClientProperty( "JInternalFrame.isPalette", Boolean.TRUE);
      isHidden = true;
  }
  
   /**
   * Show north panel.
   */

   public void showNorthPanel() {
       this.setUI(oldUi);
       this.putClientProperty( "JInternalFrame.isPalette", Boolean.FALSE);
      isHidden = false;
  }
  
   /* (non-Javadoc)
   * @see javax.swing.JInternalFrame#updateUI()
   */

   public void updateUI() {
       super.updateUI();
       if (isHidden) {
        hideNorthPanel();
      }
  } 
}
 
InnerFrame类继承自JInternalFrame,由于JInternalFrame没有对于窗口最大化事件的监听。所以,我们把它添加了一个ComponentListener。当组件大小改变时,调用componentResized方法,然后在这里判断如果组件大小是isMaximum()的,则将NorthPane隐藏掉。这里的JInternalFrame的NorthPane就是绘有关闭按钮的那一条面板。由于定义了hideNorthPane()和showNorthPane()这两个方法,是得我们对于InnerFrame的控制加强了。
 
修改后的完整代码如下所示:
 
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.beans.PropertyVetoException;

import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicInternalFrameUI;

/**
* MDIFrame is a frame using JInternalFrame to implements MDI as Word on Windows.
*  
* @author Cheng
* @version 1.0.0 for GPF MDI test
*/

@SuppressWarnings( "serial")
public class MDIFrame extends JFrame {
  
     /** The desktop pane. */
     final JDesktopPane desktopPane = new JDesktopPane();
  
     /** The operation pane with restore and close buttons. */
     final JPanel opPane = new JPanel();
  
     /** 
     * Instantiates a new mDI frame.
     */

     public MDIFrame(){
      setTitle( "MDI Frame");
      setSize(600, 550);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
      final JMenuBar bar = new JMenuBar();
      JMenuItem exit = new JMenuItem( "Exit");
          exit.addActionListener( new ActionListener(){

              @Override
               public void actionPerformed(ActionEvent e) {
                   System.exit(0);
              } 
    
      });
      bar.add(exit);
      // operation pane with two button, set invisible at first
      opPane.setLayout( new FlowLayout(FlowLayout.TRAILING));
      JButton restore = new JButton( "#");
      restore.setMargin( new Insets(0, 0, 0, 0));
      restore.setPreferredSize( new Dimension(15, 15));
      restore.addActionListener( new ActionListener(){

      @Override
       public void actionPerformed(ActionEvent e) { 
            InnerFrame i = (InnerFrame)desktopPane.getSelectedFrame();
            try {
                // notice this method, when JInternalFrame set maximun false
                 // this internal frame will be set as old size
                i.setMaximum( false);
            } catch (PropertyVetoException ex) {
                ex.printStackTrace();
            }
            i.showNorthPanel();
            opPane.setVisible( false);
          }
    
      });
      opPane.add(restore); 
      JButton close = new JButton( "X");
      close.setMargin( new Insets(0, 0, 0, 0));
      close.setPreferredSize( new Dimension(15, 15));
      close.addActionListener( new ActionListener(){

          @Override
           public void actionPerformed(ActionEvent e) {
            JInternalFrame i = desktopPane.getSelectedFrame();
            i.dispose();
            opPane.setVisible( false);
          }
    
      });
      opPane.add(close);
      bar.add(opPane);
      opPane.setVisible( false);
      setJMenuBar(bar);
  
      Container content = getContentPane();
      content.add(desktopPane, BorderLayout.CENTER);
       final JPanel ctrlPane = new JPanel(); 
      JButton add = new JButton( "add");
      add.addActionListener( new ActionListener(){

          @Override
           public void actionPerformed(ActionEvent e) {
              InnerFrame iFrame = new InnerFrame();
              iFrame.setVisible( true);
              desktopPane.add(iFrame);
          } 
    
      });
      ctrlPane.add(add);
      content.add(ctrlPane, BorderLayout.SOUTH);
      setVisible( true);
    }
  
     /** 
     * The Class InnerFrame.
     */

     class InnerFrame extends JInternalFrame {
  
       /** The is hidden. */
       boolean isHidden = false;
  
      /** The old ui. */
      BasicInternalFrameUI oldUi = null;
  
       /**
       * Instantiates a new inner frame.
       */

      public InnerFrame() {
          oldUi = (BasicInternalFrameUI)getUI();
          setSize(200, 200);
          maximizable = true;
          closable = true;
          addComponentListener( new ComponentAdapter() {
               public void componentResized(ComponentEvent e) {
                  InnerFrame selectedFrame = (InnerFrame)e.getSource();
                   if(selectedFrame.isMaximum()){
                      selectedFrame.hideNorthPanel();
                      opPane.setVisible( true);
                       try {
                           selectedFrame.setMaximum( true); 
                      } catch (PropertyVetoException ex) {
                           ex.printStackTrace();
                       }
                   } 
               }
          });
      }
  
      /**
       * Hide north panel.
       */

      public void hideNorthPanel(){ 
          ((BasicInternalFrameUI) this.getUI()).setNorthPane( null);
           this.putClientProperty( "JInternalFrame.isPalette", Boolean.TRUE);
          isHidden = true;
      }
  
       /**
       * Show north panel.
       */

      public void showNorthPanel() {
          this.setUI(oldUi);
           this.putClientProperty( "JInternalFrame.isPalette", Boolean.FALSE);
          isHidden = false;
      }
  
      /* (non-Javadoc)
       * @see javax.swing.JInternalFrame#updateUI()
       */

      public void updateUI() {
          super.updateUI();
           if (isHidden) {
              hideNorthPanel();
          }
       }
    }
  
     /** 
     * The main method.
     *  
     * @param args the arguments 
     */

     public static void main(String[] args) {
          SwingUtilities.invokeLater( new Runnable(){

              @Override
               public void run() {
                   new MDIFrame(); 
              } 
    
          }); 
     }
}
 
修改之后的最大化可以是如图样子:
 
使用JInternalFrame实现MDI_第3张图片
 
当然,按钮之类的很难看,不过修改那个的话就是很容易的了,在这里不再赘述。
 
本程序代码在Java 6 下编译通过。由于Java文件不允许上传,所以将后缀名改为txt,编译时请将文件名改为MDIFrame.java即可。