天天看點

[轉]探究 Z-Order

近日做一個GUI的東東,混用了AWT和Swing,結果出現菜單層疊錯誤、螢幕閃爍(當時試了手動雙緩沖也依舊)等問題,終于發現,“「盡量不要」混合使用 Swing 和 AWT 的元件”。。。

原文作者及出處找不到了

[技術短文]

探究 Z-Order

Z-order,也有人稱為 Zorder,指的是對象之間的層次關系。舉個簡單的例子:在使用 PowerPoint 制作檔案時,當你把檔案上被其它對象蓋住的某個對象「上推至最頂層」,你就是在改變它的 Z-order。通常 Z-order 高者置于 Z-order 低者的「上面」。

通常 GUI 程式設計都會用到 Z-order 的觀念,是以 Borland OWL 有 Z-order,Java AWT 有 Z-order,Java Swing 也有 Z-order。這些 GUI 連結庫都提供了良好的 Z-order 自動管理機制,貼心地幫我們管理 Z-order,大部分的情況之下,我們不會直接使用到 Z-order。但是,當你需要處理到對象之間的層次關系時,如果你不知道 Z-order,你可就麻煩了。我曾經用 Java 設計過一個簡單的 UML 繪圖軟體,當時并未善加使用 Swing 的 Z-order 管理機制(即 JLayeredPane),結果多寫了好些程式代碼就隻是為了控制層次關系。這篇文章簡單地為您介紹 Java 的 Z-order 觀念以及 JLayeredPane,希望你不要重蹈我的覆轍。

許多 Swing 的書都會再三告誡讀者,「盡量不要」混合使用 Swing 和 AWT 的元件,原因就在于 Swing 和 AWT 的 Z-order 系統是不一樣的。Swing 元件大都是 lightweight 的,而 AWT 的元件則一律是 heavyweight 的。我有個不錯的比喻:Swing 元件是在 AWT 元件之内徑自切割出來的層次,就好比 green thread 是在 process 之内徑自切割出來的排程機關。你可能會問:「現在 green thread 已經「進化」成 native thread 了,以後 Swing 的 Z-order 會不會也「進化」成和 AWT 的 Z-order 同地位?」我認為不會,因為如果這樣做的話,Swing 元件都必須繼承自 AWT 元件,而目前 Swing 正緻力于減少 CPU 和記憶體資源的消耗量來提升速度,是以不可能還走回頭路 heavyweight 化。

AWT 和 Swing 的 Z-order 規則一樣,如下:

1. 元件的 Z-order 一定比其容器來得高,元件一定位于容器上層。

2. 同一個容器的兩個元件中,越早加入容器者其 Z-order 越高,位置越上層。

但是如果混合使用 AWT 和 Swing,上述第二條規則就不一定了。比方說,在某容器内先加入一個 Swing 的元件,再加入一個 AWT 的元件,且此二元件有重疊的區域,結果卻是 AWT 元件出現在 Swing 元件上面。這并未遵守第二條的規定,因為此例同時使用了 AWT 和 Swing,Swing 元件被當成和容器同一個層,AWT 元件則是容器上一層。

在六個标準的 layout(包括 BorderLayout、FlowLayout、GridLayout、GridBagLayout、CardLayout、以及 BoxLayout)管理之下,容器的元件之間不會重疊(甚至連 CardLayout 管理之下也是如此,因為 CardLayout 隻是邏輯上元件重疊,但實際上的做法是一次隻有一個元件被設為 visible),是以大多數情況下 Z-order 并不會影響外觀。但是當元件之間有重疊的情況發生時,你就要特别注意了,這些包括了:

˙ 将 layout 設為 null,由程式自行控制元件的 layout,而且允許元件重疊的話,你不可以同時使用 AWT 和 Swing 的元件。

˙ JInternalFrame、JScrollPane、JLayeredPane、JDesktopPane...... 等容器會以重疊的方式放置元件,你不可以同時使用 AWT 和 Swing 的元件。

˙ 當使用 pop-up menu 時,此 menu 的容器如果有 heavyweight 的元件,那麼你必須讓此 pop-up menu 為 heavyweight,否則此 pop-up menu 可能會被蓋住。呼叫 JPopupMenu.setLightWeightPopupEnabled(false) 即可讓以後産生的 pop-up menu 都是 heavyweight 形式的。

前面提到,越早加入某容器的元件,其 Z-order 越高。除了 remove 再 add 之外,想改變 Z-order,你别無它法。但是如果你真的需要改變某容器的元件之 Z-order,那麼你可以改用 javax.swing.JLayeredPane 當作容器,因為 JLayeredPane 提供了許多讓你可以改變元件 Z-order 的 method。

對于 JLayeredPane 來說,Z-order 由兩部分組成,分别是 layer 和 position,兩者都是整數值。某元件的 layer 數目越高,表示位于越上層。如果兩個元件位于同一個 layer,則 position 數目越低者位于越上層。關于 JLayeredPane,請看 O'Reilly 出版的「Java Swing」一書 223~231 頁。

這篇文章可能有點複雜,必要時請仔細地多閱讀幾次。