现在的位置: 首页 > 综合 > 正文

简述 Java 图形用户界面设计 (Swing)

2013年09月15日 ⁄ 综合 ⁄ 共 7522字 ⁄ 字号 评论关闭
2003年5月发在CCW-I社区
2006年9月般至CSDN,略做修改
2006年11月般至Mblogger
 
  作为一个 Java 程序员,从论坛上感受到使用 Java 开发程序的人越来多,心中不免欣慰。但是,同样是从论坛中,看到多数人提到 Java 就以为是网络开发——不是这样的,Java 也可以开发应用程序,而且可以开发出漂亮的图形用户界面的应用程序,也就是 Windows/XWindow 应用程序。因此,我写下这篇文章,希望能带你进入 Java 图形用户界面设计之门。
 
  下面,让我们开始……
 
  说明:所有代码均在 Windows XP + Eclipse 环境下编写和测试,示例图片均在 Windows XP 下捕捉。
 
目  录
 
一. AWT 和 Swing
二. 框架、监听器和事件
三. 按钮、切换按钮、复选按钮和单选按钮
四. 文本输入框、密码输入框
五. 窗格、滚动窗格和布局管理
六. 后记——什么是 SWT (2006年补充)


 
 
一. AWT 和 Swing
 
 AWT 和 Swing 是 Java 设计 GUI 用户界面的基础。与 AWT 的重量级组件不同,Swing 中大部分是轻量级组件。正是这个原因,Swing 几乎无所不能,不但有各式各样先进的组件,而且更为美观易用。所以一开始使用 AWT 的程序员很快就转向使用 Swing 了。
 
  那为什么 AWT 组件没有消亡呢?因为 Swing 是架构在 AWT 之上的,没有 AWT 就没有 Swing。所以程序员可以根据自己的习惯选择使用 AWT 或者是 Swing。但是,最好不要二者混用——除开显示风格不同不说,还很可能造成层次 (Z-Order) 错乱,比如下例: 
 
/*
 * AwtSwing.java
 * @author Fancy
 
*/


import java.awt.BorderLayout;
import java.awt.Button;

import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;

public final class AwtSwing {

    
public static void main(String[] args) {
        AwtSwing as 
= new AwtSwing();
        as.show();
    }


    JFrame frame 
= new JFrame("Test AWT and SWING");

    JDesktopPane jdp 
= new JDesktopPane();

    JInternalFrame jif1 
= new JInternalFrame("controls");

    JInternalFrame jif2 
= new JInternalFrame("cover");

    
public AwtSwing() {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(jdp);

        jif1.setContentPane(
new JPanel());
        jif2.setContentPane(
new JPanel());

        jif1.getContentPane().setLayout(
new BorderLayout());
        jif1.getContentPane().add(
new Button("AWT Button"), BorderLayout.WEST);
        jif1.getContentPane().add(
new JButton("Swing Button"),
                BorderLayout.EAST);

        jif1.setSize(
200100);
        jif2.setSize(
200100);

        jdp.add(jif1);
        jdp.add(jif2);

        frame.setSize(
240140);
    }


    
public void show() {
        frame.setVisible(
true);
        jif1.setVisible(
true);
        jif2.setVisible(
true);
    }


}
 
  运行这个程序,并用鼠标拖动那个名为“cover”的子窗口,我们会发现一个非常有趣的现象,如图:
 
 
  显然 cover 子窗口是在 controls 子窗口之上的,但是它只罩盖住了 Swing Button,没有罩盖住 AWT Button。再看一会儿,你是不是有这样一种感觉:Swing Button 是“画”上去的,而 AWT Button 则是“贴”上去的。这就是二者混用造成层次错乱的一个例子。
 
  Swing 组件有美观、易用、组件量大等特点,也有缺点——使用 Swing 组件的程序通常会比使用 AWT 组件的程序运行更慢。但是大家都还是更喜欢用 Swing 组件,原因何在?因为随着计算机硬件的升级,一点点速度已经不是问题。相反的,用户更需要美观的用户界面,开发人员则更需要易用的开发组件。
 
 
二. 框架、监听器和事件
 
  框架 (Frame, JFrame) 是 Java 图形用户界面的基础,它就是我们通常所说的窗口,是 Windows/XWindow 应用程序的典型特征。说到 Windows/XWindow,大家很容易联想到“事件 (Event) 驱动”。Java 的图形用户界面正是事件驱动的,并且由各种各样的监听器 (Listener) 负责捕捉各种事件。
 
  如果我们需要对某一个组件的某种事件进行捕捉和处理时,就需要为其添加监听器。比如,我们要在一个窗口 (JFrame) 激活时改变它的标题,我们就需要为这个窗口 (JFrame 对象) 添加一个可以监听到“激活窗口”这一事件的监听器——WindowListener。
 
  怎么添加监听器呢?这通常由组件类提供的一个 addXxxxxListener 的方法来完成。比如 JFrame 就提供有 addWindowListener 方法添加窗口监听器 (WindowListener)。
 
  一个监听器常常不只监听一个事件,而是可以监听相关的多个事件。比如 WindowListener 除了监听窗口激活事件 (windowActivate) 之外,还可以监听窗口关闭事件 (windowClosing) 等。那么这些事件怎么区分呢?就靠重载监听器类 (Class) 的多个方法 (Method) 了。监听器监听到某个事件后,会自动调用相关的方法。因此我们只要重载这个方法,就可以处理相应的事件了。
 
  不妨先看一个例子: 
/**
 * @(#) TestFrame.java
 * 
@author James
 
*/


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

public class TestFrame extends JFrame {

    
private int counter = 0;

    
public TestFrame() {
        
/* 使用匿名类添加一个窗口监听器 */
        addWindowListener(
new WindowAdapter() {
            
public void windowClosing(WindowEvent e) {
                System.out.println(
                    
"Exit when Closed event");
                
//退出应用程序
                System.exit(0);
            }


            
public void windowActivated(WindowEvent e) {
                
// 改变窗口标题
                setTitle("Test Frame " + counter++);
            }

        }
);

        
// 设置窗口为固定大小
        setResizable(false);
        setSize(
200150);
    }


    
public static void main(String[] args) {
        TestFrame tf 
= new TestFrame();
        tf.show();
    }


}
  这个例子中,我们设计了一个窗口类(public class TestFrame extends JFrame { ... }),并且为这个窗口添加了一个窗口监听器 (addWindowListener(new WindowAdapter() ...)。而我们添加的这个窗口监听器主要监听了两个事件:窗口关闭 (public void windowClosing(WindowEvent e) ...) 和窗口激活 (public void windowActivated(WindowEvent e) ...)。在窗口关闭事件中我们退出了整个应用程序(System.exit(0);),而在窗口激活事件中,我们改变了窗口的标题 (setTitle("Test Frame " + counter++);)。最后,我们在 main 方法中显示了这窗口类的一个实例,运行得到下图所示的结果:
 
 
  这个程序的运行结果就是一个什么东西都没有加的框架,也就是一个空窗口。那么,你知道显示一个窗口最主要的几句代码吗?不知道没关系,我来告诉你,显示一个窗口只需要做三件事:生成实例(对象)→设置大小→显示,相应的,就是下面的三句代码:
 
        JFrame frame = new JFrame("Frame's Title");
        frame.setSize(
400300);
        frame.setVisible(
true);
 
  也许你会说:第一句的意思我清楚,第三句的意思我也明白,为什么一定要第二句呢?其实想想也就明白了,叫你画一个没法有大小的矩形你能画出来吗?不能。同样,没有大小的窗口,怎么显示?所以我们需要用 setSize(int width, int height) 方法为其设置大小。我们还有另一种方法:用 JFrame 的 pack() 方法让它自己适配一个大小。pack() 在多数时候是令人满意的,但有时,它也会让你哭笑不得——多试试就知道了。
 
  在 JFrame 中,我们使用 addWindowListener 方法加入一个监听器 WindowListener (addWindowListener(new WindowAdapter() ...) 去监听发生在 JFrame 上的窗口事件。WindowListener 是一个接口,在 java.awt.event 这个包中,但是上例中好象并没有使用 WindowListener,而是使用的 WindowsAdapter 吧,这是怎么回事?
 
  WindowAdapter 是 WindowsListener 接口的一个最简单的实现,也在 java.awt.event 包中。如果我们直接使用 WindowListener 产生一个类,需要实现它的每一个方法 (一共 7 个)。但 WindowAdapter 作为 WindowListener 最简单的实现,已经实现了它的每一个方法为空方法 (即只包含空语句,或者说没有语句的方法)。用 WindowAdapter 就只需要重载可能用到的方法 (上例中只有 2 个) 就行了,而不需要再去实现每一个方法。优点显而易见——减少编码量。
 
  在 JFrame 上发生的窗口事件 (WindowEvent) 包括:
 
windowActivated(WindowEvent e)
窗口得到焦点时触发
windowClosed(WindowEvent e)
窗口关闭之后触发
windowClosing(WindowEvent e)
窗口关闭时触发
windowDeactivated(WindowEvent e)
窗口失去焦点时触发
windowDeiconified(WindowEvent e)
 
windowIconified(WindowEvent e)
 
windowOpened(WindowEvent e)
窗口打开之后触发
 
  上例重载了其中两个方法。如果在上例运行产生的窗口和另外一个应用程序窗口之间来回切换 (在 Windows 操作系统中你可以使用 Alt+Tab 进行切换)……试试看,你发现了什么?有没有现我们的示例窗口标题上的数字一直在增加,这便是在 windowActivated 事件中 setTitle("Test Frame " + counter++) 的功劳。
 
  而另一个事件处理函数 windowClosing 中的 System.exit(0) 则保证了当窗口被关闭时退出当前的 Java 应用程序。如果不作这样的处理会怎样呢?试验之后你会发现,窗口虽然关闭了,但程序并没有结束,但此时,除了使用 Ctrl+C 强行结束之外,恐怕也没有其它办法了。所以,这一点非常重要:你想在关闭窗口的时候退出应用程序,那就需要处理 windowClosing 事件。……也不尽然,其实还有另外一个更简单的办法,让 JFrame 自己处理这件事——你只需要如下调用 JFrame 的 setDefaultCloseOperation 即可:
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
  在产生 JFrame 对象之后执行上述语句,就可以不用处理 windowsClosing 事件来退出程序了。
 
  我们可以在 JFrame 对象中添加 AWT 或者 Swing 组件。但是,虽然它有 add 方法,却不能直接用于添加组件,否则崤壮鲆斐!恍啪褪允浴T斐烧飧鱿窒蟮脑蛑挥幸桓鼋馐停篔Frame 不是一个容器,它只是一个框架。那么,应该怎么添加组件呢?
 
  JFrame 有一个 Content Pane,窗口是显示的所有组件都是添加在这个 Content Pane 中。JFrame 提供了两个方法:getContentPane 和 setContentPane 就是用于获取和设置其 Content Pane 的。通常我们不需要重新设置 JFrame 的 Content Pane,只需要直接获取默认的 Content Pane 来添加组件等。如:(new JFrame()).getContentPane().add(new Button("test button"))。
 
 
三. 按钮、切换按钮、复选按钮和单选按钮
 
  按钮……就是按钮,不会连按钮都不知道吧?
  切换按钮,有两种状态的按钮,即按下状态和弹起状态,若称为选择状态或未选择状态。
  复选按钮,又叫复选框,用一个小方框中是否打勾来表示两种状态。
  单选按钮,又叫收音机按钮,以小圆框打点表示被选中。常成组出现,一组单选按钮中只有一个能被选中。
 
  发现什么了吗?——对了,这一部分是在讲各种各样的按钮,而且后三种按钮都有两种状态。先看看这些按钮都长成什么样:
 
 
  上图中,从上到下,依次就是按钮、切换按钮、复选按钮和单选按钮。图示的窗口,就是下面这个例子的运行结果:
 
/*
 * TestButtons.java
 * @author Fancy
 
*/


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JToggleButton;

public final class TestButtons {

    
public static void main(String[] args) {
        TestButtons tb 
= new TestButtons();
        tb.show();
    }


    JFrame frame 
= new JFrame("Test Buttons");

    JButton jButton 
= new JButton("JButton"); // 按钮

    JToggleButton toggle 
= new JToggleButton("Toggle Button"); // 切换按钮

    JCheckBox checkBox 
= new JCheckBox("Check Box"); // 复选按钮

    JRadioButton radio1 
= new JRadioButton("Radio Button 1"); // 单选按钮

    JRadioButton radio2 
= new JRadioButton("Radio Button 2");

    JRadioButton radio3 
= new JRadioButton("Radio Button 3");

    JLabel label 
= new JLabel("Here is Status, look here."); // 不是按钮,是静态文本

抱歉!评论已关闭.