如何混排Swing和AWT组件

我们都清楚Swing是模拟的,所谓轻量级(lightweight)组件。而AWT使用本地的,所谓重量级(heavyweight)组件。什么是重量级组件?重量级组件是指每个组件对象对应于一个代表本地组件的native peer。这一点Swing组件和AWT完全不同。Swing中组件除顶级容器类组件如Window、Frame和Applet有自己的native peer外其他都没有对应的native peer,用来模拟这些Swing组件的图形对象资源都来自于它所在的顶层容器。
    由于实现机制不同,Swing和AWT组件有着完全不同的Z-order机制。这使得它们在混合排版时出现各种各样的互相遮盖问题。Swing的设计者Amy Fowler在SDN(Sun Developer Network)有一篇权威的文章介绍了混排Swing和AWT组件存在的问题,以及解决混排问题的原则:
[url=http://java.sun.com/products/jfc/tsc/articles/mixing/]
Mixing heavy and light components
[/url]
    该文不推荐使用AWT组件,更不推荐将AWT组件和Swing组件进行混排,而推荐使用纯粹的Swing组件完成所需的任务。这样做有三点好处:
    1.高效的利用资源。Swing是轻量级组件,AWT是重量级组件,需要操作系统的额外开销。
    2.维护平台一致性。重量级组件在不同平台具有不同的缺省外观、尺寸、布局、字体以及行为等。这需要编写额外代码维护各个平台程序的一致性。比如Windows和Linux的缺省字体、大小和样式都不一样,需要显示的设置字体样式和大小等;在Windows很好看的布局有可能在Linux变得乱七八糟;组件间的间隔因为平台的不同而有很多变化。
    3.良好的皮肤支持。AWT同SWT一样是不支持皮肤更换机制的。
    同重量级AWT组件相比,轻量级Swing组件有如下不同特征:
    1.Swing组件可以是透明的,而AWT是不透明的。
    2.Swing的透明机制可以实现不规则形状的组件,而AWT组件只能是矩形组件。
    3.鼠标事件总是通过Swing组件所在的顶层容器一层层传递给它,因此可被中间组件截获进行特殊处理。最大应用就是GlassPane和LayerPane,可用来实现跨组件的协作,比如拖动以及弹出式菜单和下拉框等。而AWT组件的鼠标事件是直接传递给该组件的,应用程序无法截获而实现特殊效果,控制权都在本地图形系统哪儿。
    4.Swing组件的实际Z-order是同其顶层容器一样的。Swing的模拟行为最终要靠顶层容器提供图形设备资源完成,实际上所有同一顶层容器的Swing组件都共享该顶层容器的拥有的本地资源。因此这些Swing组件的实际Z-order值都是一样的,都要小于该顶层容器内所包含的其他AWT组件。所以当Swing组件和AWT组件重合时,Swing组件总是被AWT组件所覆盖。
    Z-order问题使Swing组件和AWT组件混排的界面产生了很多问题。但许多时候却往往不得不混排。一种典型混排场景是Swing组件没有提供相对应的组件,而这些组件目前只有AWT组件才提供。如为了显示网页内容,需要将JDIC的WebBrowser嵌入到Swing程序中。WebBrowser是使用AWT机制实现的重量级组件,它将平台的缺省浏览器嵌入到一个AWT的Canvas组件中。如果此时WebBrowser附近恰好有一个Swing组件需要动态变化,如WebBrowser的顶部有一些JMenu菜单或者JComboBox,那么这时弹出的窗口就会被WebBrowser所覆盖。
    Amy列举除了四条混排原则帮助你解决这些问题:
    1.Do not mix lightweight (Swing) and heavyweight (AWT) components within  a container where the lightweight component is expected to overlap the  heavyweight one.不要在同容器内混排互相覆盖的轻量级组件和重量级组件。比如下图两个标签一个是Swing的,一个是AWT的,它们互相覆盖,结果造成AWT总是覆盖助Swing的,不管你添加的顺序如何:

    2.Do not place heavyweight (AWT) components inside a JScrollPane. If  you need to scroll areas containing heavyweight components, use AWT's  ScrollPane instead.不要将AWT组件放在JScrollPane内。如需要滚动AWT组件,请使用java.awt.ScrollPane。下图是将java.awt.Button放在JScrollPane的后果:

    3.Do not place heavyweight (AWT) components inside a JInternalFrame.不要将AWT组件放在JInternalFrame中。因为JInternalFrame是Swing组件,而且不同的JInternalFrame需要经常改变显示顺序,加在上面的AWT组件会总是在顶部,不管它所在的JInternalFrame是不是在前面:


    4.If you place Swing popup components in a window containing heavyweight  components and it's possible that the popup windows will intersect a  heavyweight, then invoke JPopupMenu.setDefaultLightWeightPopupEnabled(false)before the popup components are instantiated.如果窗口中同时有Swing弹出式组件(比如菜单、下拉框)和重量级AWT组件,那么弹出窗口有可能会被重量级组件所遮挡。这是因为Swing弹出式窗口的实现为了尽可能轻量级化,使用了轻量级模拟窗口,而不是java.awt.Window组件。比如菜单,如果弹出菜单的窗口恰好在顶层容器内部,那么Swing采用模拟窗口的办法画出此窗口,只有当弹出窗口的边界超出了顶层容器窗口的边界时,这时才使用Window实现。这就会带来同上面各种Z-order问题相同的现象,菜单或者下拉框被AWT组件所遮住了:

    如何解决这种问题?如果是菜单或者下拉框,可调用JPopupMenu的静态方法setDefaultLightWeightPopupEnabled(false)将弹出的窗口强制为Canvas或者Window等重量级的AWT组件,这样就可以避免弹出窗口被别的AWT组件所覆盖。其他的弹出式组件都有对应的方法强制弹出重量级窗口。
    总之,不推荐使用Swing和AWT混排的方法编写界面。如果非要这样编写界面,那么首先保证它们之间不互相覆盖,其次AWT组件要放到ScrollPane而不是JScrollPane中,再次JInternalFrame中不能放置AWT组件,最后对于需要弹出窗口的组件要使用相应方法强制弹出重量级窗口。

你可能感兴趣的:(如何混排Swing和AWT组件)