1. 程式人生 > >JFrame的組成結構

JFrame的組成結構

在學習如何向 JFrame 新增控制元件時,很多人都會在教科書上看到這樣的語句:
getContentPane().add(……);
然後有的人就產生了疑問,為什麼要這麼寫呢?
好像我直接寫 add(……); 也可以啊?
的確,現在直接寫 add(……); 是可以的,但是在 java 1.4 還是更早的年代,
這一句 getContentPane() 卻是必不可少的,現在不用了add與getContentPane都是向著contentPane上畫的

這裡我就不得不吐槽一下當年的 Swing 的開發人員,
明明過載一下 JFrame 的 add 方法就能解決的問題,非要我們多寫這麼一句……
好在後來他們終於醒悟,過載了一下 JFrame 的 addImpl 方法:
protected void addImpl(Component comp, Object constraints, int index)
{
if(isRootPaneCheckingEnabled()) {
getContentPane().add(comp, constraints, index);
}
else {
super.addImpl(comp, constraints, index);
}
}
終於,我們不用再去 getContentPane() 了……

那這個神奇的 ContentPane 究竟是什麼呢?

我們通過兩個圖來說明一下 JFrame 的層次結構:
從視覺效果來看(從 View 層來看),一個 JFrame 的結構是這樣的:
Java JFrame中從裡到外由frame-rootpane-layeredPane-contentPane-menuBar(optional)-glassPane構成


下面是類之間的關係,及類中包含引用關係
 

可以看出,
Frame 的最底層是 RootPane,
然後是 LayeredPane
再上面就是 ContentPane
最頂層是 GlassPane

最頂層的 GlassPane 預設是透明的,
關於 GlassPane 這個層次,其實有很多可以利用的技巧,以後我再慢慢告訴大家,
今天說我們的重點:ContentPane
可以看出,這個 ContentPane 就是預設盛放控制元件的那個層次,
那 ContentPane 在預設的情況下又是什麼呢?

我們來看兩個方法:
JFrame 中的 getContentPane:

public Container getContentPane() {
return getRootPane().getContentPane();
}

JRootPane 中的 createContentPane:

protected Container createContentPane() {
JComponent c = new JPanel();
……
……
return c;
}

可以明顯的看出,預設的 ContentPane 就是一個 JPanel,
現在我們再來看另一張圖,從模型的角度來看 JFrame 的層次:



可以看出,其實 ContentPane 是新增在 LayeredPane 上的一個控制元件。
而 LayeredPane 和 GlassPane 是直接新增在 RootPane 上的,
RootPane 直接新增在 JFrame 上。

其實你只要記住:
1、你現在不再需要去 getContentPane(),
2、ContentPane 預設是一個 JPanel ,
這兩個結論就可以了……

終於成功實現瞭如何為jframe視窗設定背景圖片了。下面是示例,請初學swring的朋友們參考學習!

  1. import java.awt.FlowLayout;  
  2. import javax.swing.ImageIcon;  
  3. import javax.swing.JButton;  
  4. import javax.swing.JFrame;  
  5. import javax.swing.JLabel;  
  6. import javax.swing.JPanel;  
  7. publicclass JFrameBackground {  
  8.  private JFrame frame = new JFrame("背景圖片測試");  
  9.  private JPanel imagePanel;  
  10.  private ImageIcon background;  
  11.  publicstaticvoid main(String[] args) {  
  12.   new JFrameBackground();  
  13.  }  
  14.  public JFrameBackground() {  
  15.   background = new ImageIcon("003.jpg");// 背景圖片
  16.   JLabel label = new JLabel(background);// 把背景圖片顯示在一個標籤裡面
  17.   // 把標籤的大小位置設定為圖片剛好填充整個面板
  18.   label.setBounds(00, background.getIconWidth(),  
  19.     background.getIconHeight());  
  20.   // 把內容窗格轉化為JPanel,否則不能用方法setOpaque()來使內容窗格透明
  21.   imagePanel = (JPanel) frame.getContentPane();  
  22.   imagePanel.setOpaque(false);  
  23.   // 內容窗格預設的佈局管理器為BorderLayout
  24.   imagePanel.setLayout(new FlowLayout());  
  25.   imagePanel.add(new JButton("測試按鈕"));  
  26.   frame.getLayeredPane().setLayout(null);  
  27.   // 把背景圖片新增到分層窗格的最底層作為背景
  28.   frame.getLayeredPane().add(label, new Integer(Integer.MIN_VALUE));  
  29.   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  30.   frame.setSize(background.getIconWidth(), background.getIconHeight());  
  31.   frame.setResizable(false);  
  32.   frame.setVisible(true);  
  33.  }  
  34. }  

  1. publicstaticvoid main (String[] args) {         
  2. JFrame frame=new JFrame("背景圖設定");          
  3. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);         
  4. ImageIcon img = new ImageIcon("bg\\1.gif");//這是背景圖片   
  5. JLabel imgLabel = new JLabel(img);//將背景圖放在標籤裡。      
  6. frame.getLayeredPane().add(imgLabel, new Integer(Integer.MIN_VALUE));//注意這裡是關鍵,將背景標籤新增到jfram的LayeredPane面板裡。    
  7. imgLabel.setBounds(0,0,img.getIconWidth(), img.getIconHeight());//設定背景標籤的位置   
  8. Container cp=frame.getContentPane();     
  9. cp.setLayout(new BorderLayout());      
  10. JButton but=new JButton("anniu");//建立按鈕  
  11. cp.add(but,"North");//將按鈕新增入視窗的內容面板      
  12. ((JPanel)cp).setOpaque(false); //注意這裡,將內容面板設為透明。這樣LayeredPane面板中的背景才能顯示出來。      
  13. frame.setSize(500,300);   frame.setVisible(true);                      
  14. }       

通過為jframe設定背景圖片,讓我明白了以下的知識要點:  
(1)jframe視窗的組成部分,最底層是jrootpane面板。(這一點恐怕很多初學者都沒有注意吧!)  
(2)jframe的組成如下:  jrootpane中包含glasspane和layeredpane兩個面板。而layeredpane面板包含contentpane和jmenubar。(沒想到吧contentpane是放在contentpane中的?)                            
(3)在jframe上新增元件,往往是新增在contentpane中。。但是在contentpane的下面還有兩層面板,那就是layeredpane和jrootpane。  

(4)任何一本關於java的書中都會介紹contentpane,卻很少提到layeredpane和jrootpane,因此使得很多的初學者產生:jframe中只要一個contentpane的錯誤認識。 通過解決背景設定的問題,讓我對jframe中容器的“層”結構,

更多參考:

  從網上搜索了有關設定背景圖片的文章,但是因為我每次設計視窗程式的時候,喜歡利用“Degsin”按鈕,將所有的視窗進行佈局後,在進行相關原始碼的填寫,因此,網頁提供的答案是直接在主函式中編寫,而我選擇了在建構函式中編寫,故有一定的不同。相關程式碼如下:

主函式:

public static void main(String[] args) {
  EventQueue.invokeLater(new Runnable() {
   public void run() {
    try {
     HAPPY frame = new HAPPY();
     //frame.setVisible(true);      這行程式碼,可加可不加,並不會影響最終結果,但是在建構函式中一定要新增;
    } catch (Exception e) {
     e.printStackTrace();
    }
   }
  });
 }

建構函式(關鍵程式碼):

JFrame frame=new JFrame("\設\置\背\景\圖\片 ");   
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
     ImageIcon img = new ImageIcon("src/images/1.jpg");//這是背景圖片   
     JLabel imgLabel = new JLabel(img);//將背景圖放在標籤裡。   
   
     frame.getLayeredPane().add(imgLabel, new Integer(Integer.MIN_VALUE));//注意這裡是關鍵,將背景標籤新增到jfram的     LayeredPane面板裡。   
     imgLabel.setBounds(0,0,img.getIconWidth(), img.getIconHeight());//設定背景標籤的位置   
     Container cp=frame.getContentPane();   
     cp.setLayout(null);      //這裡選擇絕對佈局管理器,對於邊界佈局管理器,放入控制元件後,無法顯示背景圖片;因為將整個面板都填充滿了;
     ((JPanel)cp).setOpaque(false); //這樣就能顯示出背景圖片出來了。

剩下的就是在面板中新增相關的控制元件,新增語句可以用:

(1)frame.getContentPane().add(panel);        (2)cp.add(panel)            

效果一樣;


另一種方法則是直接為面板設定背景圖片,原始碼如下:

contentPane = new JPanel(){
   private static final long serialVersionUID=-1588458291133087637L;
   public void paint(Graphics g){
    ImageIcon icon=new ImageIcon("src/images/5.jpg");
    Image image=icon.getImage();
    g.drawImage(image, 0, 0, null);
   }
  };

但在實驗中發現,顯示效果不如前一種方法,不知為何,面板上設定的標籤文字顯示不出來,所以,後一種方法雖然更簡便,但似乎前一種方法效果更好!

第三種方法:

contentPane.setOpaque(false);

JLabel backgroundLabel = new JLabel("");
        ImageIcon background = new ImageIcon(BalloonMove.class.getResource("/images/background.jpg"));
        backgroundLabel.setBounds(0, 0, background.getIconWidth(),background.getIconHeight());
        backgroundLabel.setIcon(background);
        getLayeredPane().add(backgroundLabel, new Integer(Integer.MIN_VALUE));

視窗中的標籤,可以直接新增到contentPane面板中,很顯然,最後一種方法顯示效果很好,且程式碼簡便。