吉行天下, 力挽狂澜

----------吉豕皮, 力一女共筑----------
随笔 - 35, 文章 - 2, 评论 - 3, 引用 - 0
数据加载中……

关于轻量级和重量级的问题(AWT和SWING)

使用 AWT 时,屏幕上所看得到的组件都是继承自 java.awt.Component 而来。窗口、对话盒、 按钮、条列盒、下拉式条列盒 … 等,无一不是 Component。Swing 中亦是如此,所有的 Swing 组件都继承自 javax.swing.JComponent,而 JComponent 又继承自 Component。一个组件是轻量级或重量级的关键 就在于,它和底层的操作系统之间有没有关系。
请注意:虽然 Java 虚拟机器给人的感觉好象是一个操作系统,但它不是。Java 平台是架构在真正的一套操作系统之上的,为了取得操作系统的资源(比方说:磁盘驱动器、网络卡、屏幕), Java 必须使用到原生码。所以,Java 的使用者接口必须和操作系统沟通,才能在画面上显示出窗口和组件。

粗糙、老旧的 AWT

AWT 透过「同侪系统( peer system )」来和操作系统沟通。每个使用者接口的对象都有一个对应的「同侪对象( peer object)」,用来管理操作系统所提供的真正 使用者接口对象。比方说:如果你建立一个按钮( Button)对象,就会有一个按钮同侪( ButtonPeer )对象一请被建立, 此按钮同侪对象会请底层的操作系统建立一个真正的按钮。如果此程序是在 Windows 98 上执行,所建立的按钮自然是 Windows 98 的按钮。

AWT 组件的外观就会受到底层操作系统的影响。如果此程序是在 Solaris上执行,AWT 组件 看起来就是 X Windows 风味;如果此程序是在 Windows 上执行, AWT 组件看起来就是 Windows 风味。 这样的差异不晓得该说是特色还是 bug,有些人认为跨平台的程序就该随着底层的操作系统变化, 有些人认为跨平台的程序不应该随着操作系统改变而有不同的外观。

先不管这些分歧的意见,AWT 所使用的方式的确是带来一些严重的缺点。第一,这使得移植 AWT 的部分到不同平台相当困难。虽然大多数的图形化操作系统都有类似的基本组件,但外观和行为互异。 要让 AWT 在各个不同的平台上都能透过不同的同侪组件来可靠地运作,这是个难题。

另外,性能也是一大问题。你可能认为使用操作系统的原生组件应该比较快才是,其实是有这样的 可能,但不保证。执行纯 Java 或纯原生码都相当快,但是如果 Java 和底层的操作系统之间沟通太频仍, 系统的性能会下降。一个包含许多组件的 AWT 窗口中,也隐含着许多同侪对象,每个同侪对象都扮演着 AWT 组件和操作系统之间的沟通桥梁。对于复杂的接口来说, AWT 就很蹩脚。犹有甚者,原生组件在内存 管理上可能未臻理想。

有同侪对象的组件就称为「重量级组件」,因为他们很可能将你的程序陷入泥淖之中。 AWT的组件都是重量级的。

Swing 快又好用

Swing 使用有别于 AWT 的技巧,每个 Swing 组件都要负责画出自己的外观、负责处理 鼠标和键盘的事件,不需要同侪对象的帮忙。当然,某些方面还是要底层的操作系统沟通,但这也仅只在 窗口对象才需如此,所以顶层窗口和仍须要同侪对象。如果你在窗口上头放一些 Swing 组件,那么这些 组件是不需要同侪对象的,他们只需要在容器组件(这里指的是窗口)中画出自己的外观,并负责接收 处理来自容器对象的事件。比起 AWT,Swing 的方式大大地减少 Java 程序和底层操作系统之间的沟通频率。 这也意味着:使用 Swing 的程序一般都更能跨平台。只需要实做顶层容器的同侪接口, Swing 就可以 跨平台。其实, Swing 完全都是用 Java 撰写的,顶层容器的同侪接口是借自于 AWT 。除了这部分得依靠 AWT 之外,Swing 完全不用 AWT 的同侪系统。又因为 Swing 完全是用 Java 撰写的,不需移植就可以跨平台。

Swing 组件必须自行绘出外观,所以即使在不同的平台下,也能拥有一致的外观。而且,想要改变外观 很容易,只要设定一下即可。 Swing 甚至将此概念一般化成为可拆卸式的 Look & Feel,程序的整体外观风格可以随时转变。

就像水和油不相溶一样

有一个重要的问题:轻量级组件和重量级组件可否和平共存?答案是:不一定,要看情形。你不应该混用 AWT 和 Swing 组件,否则情况可能会不是你所预期的。

如果轻量级和重量级组件之间有重叠,不管何者在上面,重量级组件一定会「看起来」在轻量级组件 的上面。比方说:重量级的按钮一定会「出现在」任何轻量级组件的上面,包括轻量级的选单 ( menu )。

反正请你记住一点,要么就全都用轻量级组件,要么就全都用重量级组件,但两者不要混用。如果 你正在改写旧有的程序代码,你可能常遇到这样的问题,当然你也可以花些心力让两者可以和平共存,但你 会遭遇到不少麻烦,不值得这么做。

小心地绘图

Swing 轻量级组件的巧妙之处在于绘图。你以前可能写过程序设计出继承自 AWT 组件 (比方说 Canvas )的类别,你必须 override 原有的 paint() 来提供自己的绘图方式。在 Swing 中,你 也可以这么做。但是,如果此组件是容器组件,且包含其它组件的话,你不能单单只是 override 此容器组件的 paint() 。请牢记: Swing 组件必须在父容器中绘出自己的外观,换句话说, Swing 容器必须负责绘出自身所包含的所有组件之外观。

overrided 的 paint() 应该要呼叫 super.paint() 来照料这些子组件。或者,你也可以不要 override paint() 而去 override paintComponent() 。 paint() 会呼叫到 paintComponent() 。 paintComponent() 的责任很单纯,就只是负责绘出组件。

Show Me the Money

接下来,我会用两个简洁的范例来说明重量级和轻量级的组件。第一个范例是 HosedMenu,说明窗口中的一个重量级组件如何遮盖住一个轻量级的下拉式选单。

import  java.awt. * ;
import  java.awt.event. * ;
import  javax.swing. * ;

public   class  HosedMenu
extends  JFrame  {
     
public   static   void  main(String[] args) {
        JFrame f 
=   new  HosedMenu();
        f.addWindowListener(
new  WindowAdapter() {
           
public   void  windowClosing(WindowEvent we)  { System.exit( 0 ); }  }
);
        f.setVisible(
true );
    }

    
public  HosedMenu()  {
        
super ( " HosedMenu v1.0 " );
        setSize(
200 200 );
        setLocation(
200 200 );
            
//  Add lightweight and heavyweight buttons to the content pane.
        getContentPane().setLayout( new  GridLayout( 2 1 ));
        getContentPane().add(
new  JButton( " Lightweight JButton " ));
        getContentPane().add(
new  Button( " Big Bad Button " ));
           
//  Add a menu bar.
        JMenuBar mb  =   new  JMenuBar();
        JMenu file 
=   new  JMenu( " File " true );
        file.add(
" This " );
        file.add(
" menu " );
        file.add(
" is " );
        file.add(
" hosed. " );
        mb.add(file);
        setJMenuBar(mb);
    }

}
 


执行此范例,你会看到一个小窗口中有两个按钮和一个选单轴。上面的按钮是轻量级的 ;下面的按钮是重量级的。请点按 File 选单,此轻量级选单的下摆会被重量级的按钮遮住。(有趣的是, 如果你将此窗口缩到很小,建立出来的选单将是重量级组件,而非轻量级组件。因为 Swing 发现窗口太小,无法在窗口范围内绘出轻量级选单,所以只好另外建立一个重量级组件来容纳此选单。)

第二个范例是 TouchyFeely ,用来说明如何在执行时动态地改变 Swing 组件的外观。此程序中有两个 面版( panel ),此二面版中都各有一些组件。左半部的是 Swing 组件,右半部是 AWT 组件。 上面有一个选单可以让你选择不同的外观,包括了 Meta.、 Motif、 Windows 。在你选择不同的外观之后, Swing 组件会马上对应着改变,而右边的 AWT 组件毫无反应,因为他们是重量级组件,由底层的操作系统 来决定外观。

import  java.awt. * ;
import  java.awt.event. * ;
import  javax.swing. * ;
public   class  TouchyFeely
extends  JFrame  {
  
public   static   void  main(String[] args)  {
    JFrame f 
=   new  TouchyFeely();
    f.addWindowListener(
new  WindowAdapter()  {
          
public   void  windowClosing(WindowEvent we)  { System.exit( 0 ); }  }
);
    f.setVisible(
true );
  }

  
public  TouchyFeely()  {
  
super ( " TouchyFeely v1.0 " );
  setSize(
400 200 );
  setLocation(
200 200 );
        
//  Create two panels of controls.
  Container content  =  getContentPane();
  content.setLayout(
new  GridLayout( 1 2 ));
        
//  Create a Swing panel.
  JPanel jp  =   new  JPanel();
  jp.add(
new  JCheckBox( " JCheckBox " ));
  String[] names 
=   new  String[]  " Tosca " " Scarpia " " Cavaradossi "  } ;
  jp.add(
new  JComboBox(names));
  jp.add(
new  JList(names));
  jp.add(
new  JButton( " JButton " ));
  jp.add(
new  JLabel( " JLabel " ));
  jp.add(
new  JTextField( " JTextField " ));
  content.add(jp);
        
//  Create an AWT panel.
  Panel p  =   new  Panel();
  p.add(
new  Checkbox( " Checkbox " ));
  Choice choice 
=   new  Choice();
  choice.add(
" Tosca " );
  choice.add(
" Scarpia " );
  choice.add(
" Cavaradossi " );
  p.add(choice);
  List list 
=   new  List();
  list.add(
" Tosca " );
  list.add(
" Scarpia " );
  list.add(
" Cavaradossi " );
  p.add(list);
  p.add(
new  Button( " Button " ));
  p.add(
new  Label( " Label " ));
  p.add(
new  TextField( " TextField " ));
  content.add(p);
       
//  Add a menu bar.
  JMenuBar mb  =   new  JMenuBar();
  JMenu file 
=   new  JMenu( " Look & Feel " true );
  ButtonGroup buttonGroup 
=   new  ButtonGroup();

  
final  UIManager.LookAndFeelInfo[] info  =  UIManager.getInstalledLookAndFeels();
  
for  ( int  i  =   0 ; i  <  info.length; i ++ {
     JRadioButtonMenuItem item 
=   new
     JRadioButtonMenuItem(info[i].getName(), i 
==   0 );
     
final  String className  =  info[i].getClassName();
     item.addActionListener(
new  ActionListener()  {
       
public   void  actionPerformed(ActionEvent ae)  {
          
try   { UIManager.setLookAndFeel(className); }
       
catch  (Exception e)  { System.out.println(e); }
       SwingUtilities.updateComponentTreeUI(TouchyFeely.
this ); }

     }
);
     buttonGroup.add(item);
     file.add(item);
  }

  mb.add(file);
  setJMenuBar(mb);
  }

}
 

这个范例内容不多,大部分都只是在建立两个面版组件和其内部组件罢了。技巧的部分在选单上, 透过呼叫 UIManager.getInstalledLookAndFeels() ,我们可以取得目前有支持的 Look & Feel,然后 每个 Look & Feel 建立一个选项,并透过 ActionListener 来设定新的 Look & Feel。

结论

Swing 是个有待好好消化的题材,本文告诉你 AWT 和 Swing 之间的基本差异,希望你能 因此加速直冲 Swing 之路。依照惯例,本文中附上了范例程序,希望这能成为你学习 Swing 的弹跳板。

posted on 2006-11-26 19:28 吉豕皮 阅读(1067) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。