Java程序设计之第八章.GUI设计

时间:2021-11-16 05:08:56

一、GUI 程序概述

一个设计良好的图形用户界面(Graphical User Interface,GUI)能够使软件更好地与用户交互,从而帮助用户更好地理解和使用软件。

1.AWT 简介

抽象窗口工具包(Abstract Window Toolkit,AWT),该工具包提供了一套与本地图形界面进行交互的接口,由于不同操作系统图形库提供的功能有差异,为了实现“一次编译,到处运行”的概念,AWT 只保留了多种通用型操作系统所提供的图形功能,所以具有天然的局限性。
由于 AWT 是依靠本地方法实现其功能的,所以通常把 AWT 组件称为重量级组件。
AWT 主要由组件、容器、布局管理器、事件处理模型、图形图形工具和数据传送类等组成。
各个组件类位于 java.awt 包中,java.awt 包主要类的层次结构如下
Java程序设计之第八章.GUI设计

(网图侵删)

2.Swing 简介

为了解决 AWT 的局限性,出现了 Swing ,Swing 提供了 AWT 所能提供的所有功能,并且用纯 Java 代码对 AWT 的功能进行了大幅度的扩容。
由于 Swing 没有使用本地方法实现其图形功能的,所以通常把 Swing 组件称为轻量级组件。
Swing 组件一般位于扩展包 javax.swing 中
在进行图形界面设计时,一般都遵循下列步骤:
1. 选择容器
2. 确定布局
3. 向容器中添加组件
4. 进行事件处理

二、容器与布局

1.容器

在进行图形化界面设计的时候,GUI 的各个组件需要放置在容器中进行管理。
而容器本身也是一种组件,因此,容器里面也可以再放置其他的容器。
AWT 的容器分为两类:内部容器和外部容器。
其中,外部容器是可以独立存在的,这些容器来自 Window 分支。
而内部容器一般是不能独立存在的,通常嵌套在外部容器或其他内部容器中使用。

1-1.容器的功能

容器类的一般功能:
1.管理组件,容器类提供了一组用于管理组件的方法。例如:

  • add()
    • 向容器类中添加组件
  • remover()
    • 移除组件
  • paintComponents()
    • 绘制组件
  • getCompoent()
    • 获取组件
  • printConpoent()
    • 打印组件

2.设置布局:每个容器都和一个布局管理器相关联,用以确定组容器件在容器中的布局。

  • setLayout()
    • 为容器设置一种布局

1-2.常用的容器

常用的容器有:

  • 框架(Frame)
    • Frame 是Window 的子类,由 Frame 或其子类创建的对象是带有标题和边框的顶层窗口,其默认布局是 BorderLayout
  • 面板(Panel)
    • Panel 提供了放置其他组件的一个空间,也可以放置其他面板,面板默认布局是 FlowLayout

1-2-1.Frame 常用方法

  • Frame()
    • 创建一个新的默认为不可见的窗口
  • Frame(String title)
    • 创建一个标题栏为 title 的窗口,默认为不可见
  • setBounds(int x,int y,int width,int height)
    • 设置窗口位置和大小
  • setSize(int width,int height)
    • 设置窗口尺寸
  • setLocation(int x,int y)
    • 设置窗口位置
  • setBackground(Color bgColor)
    • 设置背景颜色
  • setVisible(boolean b)
    • 设置窗口是否可见
  • setTitle(String title)
    • 设置窗口标题
  • setResizable(boolean resizable)
    • 设置窗口是否可以调整大小

1-3.容器示例

import java.awt.*;
public class Test{
    public static void main(String args[]){
        Frame f = new Frame("My First Frame");
        f.setLocation(200,100);
        f.setSize(600,300);
        f.setBackground(Color.RED);
        f.setVisible(true);
    }
}

运行结果:
Java程序设计之第八章.GUI设计

(真的….好丑)而且由于没有为关闭按钮添加事件,点击关闭按钮并不能真正将窗口关闭。

2.布局管理

在设计图形用户界面时,必须决定界面中的各个组件如何摆放并展示给用户,Java 通过布局管理器来实现对布局的管理。

2-1.FlowLayout 布局管理器

FlowLayout 布局的原则是将各个组件按照添加的顺序,从左到右、从上到下进行放置,如果本行放不下所有组件,则放入下一行。

FlowLayout 类中,有5个静态常量,用来控制组件的对齐方式

  • CENTER:指示每一行组件都应该是居中的
  • LEADING:指示每一行组件都应该与容器方向的开始边对齐。例如从左到右的方向,应该左对齐
  • LEFT:指示每一行组件都应该是左对齐的
  • RIGHT:指示每一行组件都应该是右对齐的
  • TRAILING:指示每一行组件都应该与容器方向的结束边对齐。例如从左到右的方向,应该右对齐

2-1-1.FlowLayout 布局程序

import java.awt.*;
import java.awt.event.*;

public class Test{
    private Frame f;
    private Button button1,button2,button3;

    public static void main(String args[]){
        Test mflow = new Test();
        mflow.go();
    }

    public void go(){
        f = new Frame("FlowLayout 效果");
        f.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
                f.setVisible(true);
                f.dispose();
                System.exit(0);
            }
        });
        f.setLayout(new FlowLayout(FlowLayout.LEADING,20,20));
        button1 = new Button("第一个按钮");
        button2 = new Button("第二个按钮");
        button3 = new Button("第三个按钮");
        f.add(button1);
        f.add(button2);
        f.add(button3);
        f.setSize(200,200);
        f.pack();
        f.setVisible(true);
    }
}

运行结果:
Java程序设计之第八章.GUI设计

2-2.BorderLayout 布局管理器

borderLayout布局类似于地图上的方向,用东、南、西、北、中来安排组件的布局,分别用EAST、WEST、SOUTH、NORTH 和 CENTER 来代表各个方向,以上北、下南、左西、右东占据界面的四边, CENTER 占据剩余中间部分。

2-2-1. BorderLayout 布局程序

import java.awt.*;
import java.awt.event.*;

public class Test{
    Frame f;
    Button east,south,west,north,center;

    public static void main(String args[]){
        Test mb = new Test();
        mb.go();
    }

    public void go(){
        f = new Frame("BorderLayout 演示");
        f.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
                f.setVisible(true);
                f.dispose();
                System.exit(0);
            }
        });
        f.setBounds(0,0,300,300);
        f.setLayout(new BorderLayout());

        north = new Button("上");
        south = new Button("下");
        east = new Button("右");
        west = new Button("左");
        center = new Button("中");

        f.add(BorderLayout.NORTH,north);
        f.add(BorderLayout.SOUTH,south);
        f.add(BorderLayout.EAST,east);
        f.add(BorderLayout.WEST,west);
        f.add(BorderLayout.CENTER,center);

        f.setVisible(true);
    }
}

运行结果:
Java程序设计之第八章.GUI设计

2-3.GridLayout 布局管理器

GridLayout 布局是一种网格布局,将容器划分成若干行和列的结构,在各个网格中放置组件。在网格组件中的各个组件具有相同的宽和高,其放置顺序也是从左向右开始填充,一行占满后开始填充下一行,仍然是从左到右的顺序。

在 GridLayout 类中,提供了以下三个布局方法

  • GridLayout()
    • 创建具有默认值的网格布局,即每个组件占据一行一列。
  • GridLayout(int rows,int cols)
    • 创建具有 rows 行和 cols 列的网格布局
  • GridLayout(int rows,int cols,int hgap,int vgap)
    • 创建具有 rows 行、cols 列、水平间距为 hgap 和垂直间距为 vgap 的网格布局

2-3-1.GridLayout 布局程序

import java.awt.*;
import java.awt.event.*;

public class Test{
    private Frame f;
    private Button[] btn;

    public static void main(String args[]){
        Test grid = new Test();
        grid.go();
    }

    public void go(){
        f = new Frame("GridLayout 演示");
        f.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
                f.setVisible(true);
                f.dispose();
                System.exit(0);
            }
        });

        f.setLayout(new GridLayout(3,3,10,10));
        btn = new Button[9];
        for(int i=0;i<9;i++){
            int j = i+1;
            btn[i] = new Button(""+j);   //将int型的j转换为string型
            f.add(btn[i]);
        }

        f.setSize(500,300);
        f.setVisible(true);
    }
}

运行结果:
Java程序设计之第八章.GUI设计

2-4.CardLayout 布局管理器

CardLayout 是一种卡片式的布局,这些卡片层叠在一起,每层放置一个组件,容器则充当卡片的堆栈,当容器第一次显示时,第一个添加到 CardLayout 对象的组件为可见组件,每层只有最外层的组件露出来。卡片的顺序由组件对象本身在容器内部的顺序决定。

2-4-1.CardLayout 布局程序

import java.awt.*;
import java.awt.event.*;

public class Test{

    public static void main(String args[]){
        Test card = new Test();
        card.go();
    }

    public void go(){
        final Frame f = new Frame("CardLayout 演示");
        f.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
                f.setVisible(false);
                f.dispose();
                System.exit(0);
            }
        });

        f.setSize(300,100);
        f.setLayout(new CardLayout());

        final Frame f1 = f;
        for(int i=0;i<5;i++){
            Button b = new Button(""+i);
            b.setSize(100,25);
            b.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                CardLayout c1 =(CardLayout)f1.getLayout();
                c1.next(f1);
                }
            });
            f.add(b,"button"+i);
        }
        f.setVisible(true);
    }
}

运行结果:
Java程序设计之第八章.GUI设计
Java程序设计之第八章.GUI设计
Java程序设计之第八章.GUI设计
Java程序设计之第八章.GUI设计
Java程序设计之第八章.GUI设计

点一下变一张,当第四张时,又跳回到0

2-5.GridBagLayout 布局管理器

GridBagLayout 布局是 GridLayout 布局的一种改进,但与 GridLayout 不同的是,在这种布局中,一个组件可以横跨多个网络。
在使用这种布局管理器时,需要使用 GridBagConstraints 类来指定 GridBagLayout 布局管理器所布置组件的约束。
GridBagConstraints 类中定义了很多常量用来设置 GridBagLayout 的布局约束。

  • setConstraints()
    • 设置此布局管理器中指定组件的约束条件
  • weightx
    • 指定如何分布额外的水平空间
  • weighty
    • 指定如何分布额外的垂直空间
  • gridwidth
    • 指定组件显示区域的某一行中的单元格数
  • gridheight
    • 指定组件显示区域的某一列中的单元格数
  • BOTH
    • 在水平方向和垂直方向上同时调整组件大小
  • REMAINDER
    • 指定此组件是其行或列中的最后一个组件
  • RELATIVE
    • 指定此组件为其行或列中的倒数第二个组件,或者让此组件紧跟在以前添加的组件之后

在使用这种布局方式后,首先用 GridBagConstraints 类创建一个实例对象,然后设置该实例中各个属性的约束条件,再将该约束与某个具体的组件联系起来,最后将组件加入到容器中。

2-5-1.GridBagLayout 布局程序

import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Test extends Panel{
    protected void makeButton(String name,GridBagLayout gb,GridBagConstraints c){
        Button button = new Button(name);
        gb.setConstraints(button,c);
        add(button); 
    }
    public static void main(String args[]){
        final Frame f = new Frame("GridbagLayout 演示");
        f.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
                f.setVisible(false);
                f.dispose();
                System.exit(0);
            }
        });
        Test gb = new Test();
        gb.go();
        f.add("Center",gb);
        f.pack();
        f.setVisible(true);
    }
    public void go(){
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        setFont(new Font("Helvetica",Font.PLAIN,14));
        setLayout(gridbag);
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        makeButton("Button1",gridbag,c);
        makeButton("Button2",gridbag,c);
        makeButton("Button3",gridbag,c);
        c.gridwidth = GridBagConstraints.REMAINDER;
        makeButton("Button4",gridbag,c);
        c.weightx = 0.0;
        makeButton("Button5",gridbag,c);
        c.gridwidth = 2;
        makeButton("Button6",gridbag,c);
        c.gridwidth = GridBagConstraints.REMAINDER;
        makeButton("Button7",gridbag,c);
        c.gridwidth = 1;
        c.gridheight = 2;
        c.weighty = 1.0;
        makeButton("Button8",gridbag,c);
        c.weighty = 1.0;
        c.gridwidth = GridBagConstraints.REMAINDER;
        c.gridheight = 1;
        makeButton("Button9",gridbag,c);
        makeButton("Button10",gridbag,c);
        setSize(500,300);
    }
}

运行结果:
Java程序设计之第八章.GUI设计

三、常用组件

1.AWT 组件

1-1.标签

标签(Label)是一种放在面板上的常用组件,用来表示静态文本。
一个标签仅显示一行只读文本,文本可由应用程序更改,但是用户不能直接对其进行编辑
效果图:
Java程序设计之第八章.GUI设计

1-2.按钮

单击按钮(Button)时,ActionEvent 事件会发生,该事件产生时,由 ActionListener 接口进行监听和处理,应用程序能执行某项动作。
构造方法如下:

Button btn = new Button(name);

1-3.下拉式菜单

当有大量选项时,下拉式菜单(Choice)能节约界面显示空间,每次可以选择其中的一项。
其使用方法如下:

Choice ColorChooser = new Choice();
ColorChooser.add(“红”);
ColorChooser.add(“绿”);
ColorChooser.add(“蓝”);

效果图:
Java程序设计之第八章.GUI设计

1-4.文本框

文本框(TextField)用于单行文本的输入,可以接受来自用户的键盘输入。
在构建一个文本框的时候有多种选择:

  • TextField tf1 = new TextField()
    • 空的文本框
  • TextField tf1 = new TextField(20)
    • 空的长度为20的文本框
  • TextField tf1 = new TextField(“请输入…”)
    • 带有初始值的文本框
  • TextField tf1 = new TextField(“请输入…”,20)
    • 带有初始值并指定长度为20的文本框

效果图:
Java程序设计之第八章.GUI设计

1-5.文本区

当需要输入多行文本时,可以使用文本区(TestArea)。
和文本框类似,文本区有多个构造方法:

  • TestArea()
    • 构造一个将空字符串作为文本的新文本区
  • TestArea(int rows,int columns)
    • 构造一个指定行数和列数的空文本区,并将空字符串作为文本
  • TestArea(String text)
    • 构建具有指定文本的新文本区
  • TestArea(String text,int rows,int columns)
    • 构建一个指定行数和列数及指定文本的新文本区
  • TestArea(String text,int rows,int columns,int scorllbars)
    • 构建一个指定行数和列数及指定文本的新文本区且具有滚动条可见性

效果图:
Java程序设计之第八章.GUI设计

1-6.列表

列表(List)含有多个选项且所有选项可见,如果由于选项过多而超出了列表的可见范围,则会在列表框旁边产生一个滚动条。
可以设置 list 属性,使其允许用户进行单选或多选。
当用户选中或取消选中某项时,AWT 将向列表发生一个 ItemEvent 实例。
当用户双击列表的某一项时,AWT 会在紧随项事件后向列表发送一个 ActionEvent 实例。
当用户选中列表中的某项并按下 Enter 键时,AWT 也会生成一个动作事件。

        List list = new List(2,false);  //显示行数为2行,不允许多选
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");

效果图:
Java程序设计之第八章.GUI设计

1-7.复选框

复选框(Checkbox)是一个开/关选项(on/off),用于对某一项进行选取,单击复选框可以将其状态进行转换。
当复选框被选择时,会产生 ItemEvent 事件,使用 ItemListener 可对其进行监听。
另外,可以使用 getState() 方法来获取复选框当前状态。
效果图:
Java程序设计之第八章.GUI设计

1-8.复选框组

复选框组(CheckboxGroup)的功能类似于单选框,即在某一时刻当选择其中某一项时,强制其他项状态必须处于相反。
效果图:
T_T有bug改不来

1-9.画布

画布(Canvas)组件表示屏幕上一个空白矩形区域,应用程序可以在该区域内绘图,或者可以从该区域捕获用户的输入事件。
效果图:
….略

1-10.菜单

开发菜单(Menu) 时,一般需要使用到三个类:MenuBar 类、Menu 类和MenuItem 类。

1-10-1.MenuBar 类

菜单(Menu) 不能直接添加到容器的某一位置,只能添加到 MenuBar 中,然后将 MenuBar 对象与框架(Frame) 相关联,调用框架的 setMenuBar() 方法实现关联。

1-10-2.Menu 类

菜单(Menu) 可以被添加到 MenuBar 对象中或其他 Menu 对象中

1-10-3.MenuItem 类

MenuItem 是菜单项,处于菜单树的最底层,菜单中的所有项都必须是 MenuItem 对象或其子类的对象。
单击某个菜单项时,会发出 ActionEvent 动作对象,因此可以为 MenuItem 对象注册 ActionListener 监听器,以实现对应的操作。

1-10-4.菜单实例

import java.awt.*;
import java.awt.event.*;

public class Test extends Frame{
    PopupMenu pop;
    Test(){
        super("Golf Caddy");
        addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
                setVisible(false);
                dispose();
                System.exit(0);
            }
        });
        this.setSize(300,300);
        this.add(new Label("2333"),BorderLayout.NORTH);

        Menu woods = new Menu("Woods");
        woods.add("1 W");
        woods.add("3 W");
        woods.add("5 W");

        Menu irons = new Menu("Irons");
        irons.add("3 iron"); 
        irons.add("4 iron"); 
        irons.add("5 iron"); 
        irons.add("6 iron"); 
        irons.add("7 iron"); 
        irons.add("8 iron"); 
        irons.add("9 iron"); 
        irons.addSeparator();
        irons.add("PW");
        irons.insert("6 iron",3);

        MenuBar mb = new MenuBar();
        mb.add(woods);
        mb.add(irons);
        this.setMenuBar(mb);

        pop = new PopupMenu("Woods");
        pop.add("2 W");
        pop.add("4 W");
        pop.add("6 W");

        final TextArea p = new TextArea(100,100);

        p.setBounds(0,0,100,200);
        p.setBackground(Color.black);
        p.add(pop);
        p.addMouseListener(new MouseAdapter(){
            public void mouseReleased(java.awt.event.MouseEvent evt){
                if(evt.isPopupTrigger()){
                    System.out.println("popup trigger");
                    System.out.println(evt.getComponent());
                    System.out.println(""+evt.getX()+""+evt.getY());
                    pop.show(p,evt.getX(),evt.getY());
                }
            }
        });
        this.add(p,BorderLayout.CENTER);
    }

    public static void main(String args[]){
        new Test().setVisible(true);
    }

}

运行结果:
Java程序设计之第八章.GUI设计

1-11.对话框

对话框(Dialog) 继承于窗口(Window)类,是一个带标题和边界的顶层窗口,属于容器类。
与其他组件有所区别,其边界一般用于从用户处获得某种形式的输入。
默认布局为 BorderLayout
对话框分为:

  • 模式对话框
    • 启动时将原应用程序阻塞,只有关闭后才能回到原应用程序。
  • 无模式对话框
    • 不会影响到原应用程序的执行

1-12.文件对话框

在编写程序时,经常遇到对文件的保存或打开,可以使用文本对话框(FileDialog)实现
文件对话框是模式对话框

2.Swing 组件

Swing 组件类位于 Java 扩展包 javax.swing 中,一般以 J 开头,同类组件的使用方法和 AWT组件用法类似

2-1.面板

面板(JPanel) 使用方法类似于 Panel ,JPanel 的默认布局是 FlowLayout

2-2.滚动窗口

滚动窗口(JScrollPane)是带滚动条的面板

2-3.选项板

选项板(JTabbedPane)允许用户通过单击具有给定标题或图标的选项卡,在一组组件之间进行切换。
可通过 addTab( ) 和 insertTab( ) 方法将选项卡/组件添加到 JTabbedPane 对象中。

2-4.工具栏

工具栏(JToolBar)按钮一般对应着菜单中的某一项,便于用户进行常用的操作。

2-5.按钮

按钮(JButton)是最常用的组件之一,按钮上可以带图标或标签。
其构造方法如下:

JButton( )      //创建不带有设置文本或图标的按钮
JButton(Action a)    //创建一个按钮,其属性从所提供的 Action 中获取
JButton(Icon icon)    //创建一个带图标的按钮
JButton(String text)  //创建一个带文本的按钮
JButton(String text,Icon icon)  //创建一个带初始文本和图标的按钮

2-6.复选框

复选框(JCheckBox)提供一个“开/关”(on/off)量,边上显示一个文本标签

2-7.单选框

单选框(JRadioButton)实现一个单选按钮,该按钮项可以被选择或被取消选择,并且可以显示其状态。
与 ButtonGroup 对象配合使用可以创建一组按钮,一次只能选择其中的一个。

2-8.选择框

选择框(JComboBox)类似于文本框和列表框的组合,既可以选择其中的一项,也可以进行编辑。

2-9.标签

标签(JLabel)对象可以显示文本、图像或二者的组合。
可以设置其垂直和水平对齐方式。
默认情况下,标签在其显示区内垂直居中对齐;只显示文本的标签是开始边对齐;只显示图形的标签是水平居中对齐。
还可以指定文本相对于图像的位置。默认情况下,文本位于图像的结尾边上,文本和图像都垂直对齐。

2-10.菜单

菜单(JMenu)的使用于 AWT 中的 Menu 类似,与之不同的是,它可以通过 setJMenuBar(MenuBar) 方法将菜单放置在容器中的任何位置。

2-11.进度条

进度条(JProgressBar)是以可视化形式显示某些任务进度的组件。还可以显示此百分比的文本表示形式。

2-12.滑动条

滑动条(JSlider)可以让用户以图形方式在有界区间内通过移动滑块来选择值。
刻度标记之间值的个数由 setMajorTickSpacing() 方法和 setMinorTickSpacing() 方法来控制,而刻度标记的绘制则由 setPaintTicks() 方法控制。

2-13.表格

表格(JTable)用来显示和编辑常规二维单位表。

2-13-1.表格实例

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

class Test extends JFrame {
    private boolean DEBUG = true;

    Test(){
        super("RecorderOfWorkers");
        MyTableModel myModel = new MyTableModel();
        JTable table = new JTable(myModel);
        table.setPreferredScrollableViewportSize(new Dimension(500,80));

        JScrollPane scrollPane = new JScrollPane(table);
        getContentPane().add(scrollPane,BorderLayout.CENTER);
        addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
                System.exit(0);
            }
        });
    }

    class MyTableModel extends AbstractTableModel{
        final String[] columNames = {"First name","Position","Telephone","MonthlyPay","Married"};
        final Object[][] data={{"张三","Executive","123456789",8000,false},
                                {"李四","Secretary","555555555",6500,true},
                                {"王五","Manager","333333333",7500,false},
                                {"大熊","Safeguard","111111111",4000,true},
                                {"小康","Salesman","999999999",7000,false}};

        public int getColumnCount(){
            return columNames.length;
        }

        public int getRowCount(){
            return data.length;
        }

        public String getColumnName(int col){
            return columNames[col];
        }

        public Object getValueAt(int row,int col){
            return data[row][col];
        }

        public Class getColumnClass(int c){
            return getValueAt(0,c).getClass();
        }

        public boolean isCellEditable(int row,int col){
            if(col<2)
                return false;
            else
                return true;
        }

        public void setValueAt(Object value,int row,int col){
            if(DEBUG)
                System.out.println("Setting value at "+ row +","+ col +" to "+ value +"(an instance of "+ value.getClass() +")");
            if(data[0][col] instanceof Integer && !(value instanceof Integer)){  
                try{
                    data[row][col] = value.toString();
                    fireTableCellUpdated(row,col);
                }
                catch(NumberFormatException e){
                    JOptionPane.showMessageDialog(Test.this,"The \""+ getColumnName(col) + "\" column accepts only interger values.");
                }
            }
            else{
                data[row][col] = value;
                fireTableCellUpdated(row,col);
            }
            if(DEBUG){
                System.out.println("New value of data:");
                printDebugData();
            }
        }

        private void printDebugData(){
            int numRows = getRowCount();
            int numCols = getColumnCount();
            for(int i=0;i<numRows;i++){
                System.out.print(" row "+ i +":");
                for(int j=0;j<numCols;j++)
                    System.out.print(""+data[i][j]);
                System.out.println();
            }
        }
    }

    public static void main(String args[]){
        Test frame = new Test();
        frame.pack();
        frame.setVisible(true);
    }
}

运行结果:
Java程序设计之第八章.GUI设计

2-14.树

树(JTree)可以将分层数据集以树的形式表现出来,使用户操作方便、直观易用。

2-14-1.树实例

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

class Branch{
    DefaultMutableTreeNode r;

    Branch(String []data){
        r = new DefaultMutableTreeNode(data[0]);
        for (int i=1;i<data.length ;i++ ) {
            r.add(new DefaultMutableTreeNode(data[i]));
        }
    }

    public DefaultMutableTreeNode node(){
        return r;
    }
}

public class Test extends JPanel{
    String [][] data={
        {"Color","Red","Blue","Green"},
        {"Flavors","Tart","Sweet","Bland"},
        {"Length","Short","Medium","Long"},
        {"Volume","High","Medium","Low"},
        {"Temperature","High","Medium","Low"},
        {"Intensity","High","Medium","Low"}};
    static int i=0;
    DefaultMutableTreeNode root,child,chosen;
    JTree tree;
    DefaultTreeModel model;

    Test(){
        setLayout(new BorderLayout());
        root = new DefaultMutableTreeNode("root");
        tree = new JTree(root);
        add(new JScrollPane(tree));
        model = (DefaultTreeModel)tree.getModel();
        JButton test = new JButton("Press me");
        test.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                if(i<data.length){
                    child = new Branch(data[i++]).node();
                    chosen = (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
                    if(chosen==null)
                        chosen = root;
                    model.insertNodeInto(child,chosen,0);
                }
            }
        });
        test.setBackground(Color.blue);
        test.setForeground(Color.white);
        JPanel p = new JPanel();
        p.add(test);
        add(p,BorderLayout.SOUTH);
    }

    public static void main(String args[]){
        JFrame jf = new JFrame("JFrame demo");
        jf.getContentPane().add(new Test(),BorderLayout.CENTER);
        jf.setSize(200,500);
        jf.setVisible(true);
    }
}

运行结果:
Java程序设计之第八章.GUI设计

四、事件处理

1.事件处理的概念

应用程序在运行时,应该对用户的操作给予响应,用户的一个操作可以看成是一个”事件(Event)”,发出事件的组件成为”事件源(Event Source)”,同时,对于事件源来说,需要有”事件监听器(Event Listener)”对其进行监听,以便产生事件时,能够及时对事件做出响应。
Java 采用事件委托模型(Event Delegation Model)来处理事件过程。向事件源注册一些监听器,当事件源产生事件,产生事件对象,并向所有该事件注册的监听器发生通知,再由事件监听器将事件委托给不同的事件处理器处理。
Java程序设计之第八章.GUI设计
(网图侵删)
事件源不同,产生的事件类型也不同。
AWT中的事件可以分为:
1.低级(low-level)事件,常用的有:

  • KeyEvent:组件中发生键击的事件
  • MouseEvent:组件中发生鼠标动作的事件
  • MouseWheelEvent:鼠标滚轮在组件中滚动的事件
  • FocusEvent:组件获得或失去输入焦点的事件
  • WindowEvent:窗口改变的事件

2.语义(semantic)事件,常用的有:

  • ActionEvent:发生组件定义动作的事件
  • ItemEvent:指定项被选定或取消选定的事件
  • AdjustmentEvent:由 Adujustable 对象所发出的调整事件
  • TextEvent:对象文本已改变的事件

AWT事件的继承关系图:
Java程序设计之第八章.GUI设计
(网图侵删)

2.监听器和适配器

根据事件类型的不同,处理每类事件的监听器也有所差别。一个事件监听器可以处理一类事件,要使监听器具有处理某一类事件的能力,就需要让监听器实现相应的事件监听器接口,即一个监听器对象是一个实现了特定监听器接口的类的实例。
不同的监听器接口有不同的事件处理方法,要实现某种监听器接口,就要实现接口中的抽象方法来对事件进行处理。

2-1.监听器实例

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

public class Test{
    public static void main(String[] args) {
        ButtonFrame frame = new ButtonFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

class ButtonFrame extends JFrame{
    public static final int DEFAULT_WIDTH = 300;
    public static final int DEFAULT_HEIGHT = 200;
    ButtonFrame(){
        setTitle("Test");
        setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
        ButtonPanel panel = new ButtonPanel();
        add(panel);
    }
}

class ButtonPanel extends JPanel{
    ButtonPanel(){
        JButton yellowButton = new JButton("change to yellow");
        JButton blueButton = new JButton("change to blue");
        JButton redButton = new JButton("change to red");

        add(yellowButton);
        add(blueButton);
        add(redButton);

        ColorAction yellowAction = new ColorAction(Color.YELLOW);
        ColorAction blueAction = new ColorAction(Color.BLUE);
        ColorAction redAction = new ColorAction(Color.RED);

        yellowButton.addActionListener(yellowAction);
        blueButton.addActionListener(blueAction);
        redButton.addActionListener(redAction);
    }

    private class ColorAction implements ActionListener{
        private Color backgroundColor;
        public ColorAction(Color c){
            backgroundColor = c;
        }

        public void actionPerformed(ActionEvent event){
            setBackground(backgroundColor);
        }
    }
}

运行结果:
Java程序设计之第八章.GUI设计
Java程序设计之第八章.GUI设计
Java程序设计之第八章.GUI设计

2-2.部分监听器接口、方法及事件源

事件源 接口 方法
Action ActionListener actionPerformed(ActionEvent)
Item ItemListener itemStateChanged(ItemEvent)
Container ContainerListener componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
Mouse button MouseListener mouseClicked(MouseEvet)
mouseClicked(MouseEvet)
mouseEntered(MouseEvet)
mouseExited(MouseEvet)
mousePressed(MouseEvet)
mouseReleased(MouseEvet)
key KeyListener keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)
Focus FocusListener focusGained(FocusEvent)
focusLost(FocusEvent)
Adjustment AdjustmentListener adjustmentValueChanged(AdjustmentEvent)
Component ComponentListener componentHidden(ComponentEvent)
componentMoved(ComponentEvent)
componentResized(ComponentEvent)
componentShown(ComponentEvent)
Window WindowListener windowActivated(WindowEvent)
windowClosed(WindowEvent)
windowClosing(WindowEvent)
windowDeactivated(WindowEvent)
windowDeiconified(WindowEvent)
windowIconified(WindowEvent)
windowOpened(WindowEvent)
Mouse motion MouseListener mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
Text TextListener textValueChanged(TextEvent)

通过实现监听类接口来编写一个监听器类的时候,接口所定义的所有抽象方法都需要被实现。即使对处理某个事件的方法不感兴趣,仍然要编写一个空方法体。
为了解决此问题,AWT 提供了与监听器接口配套的适配器类(Adapter),好处是可以直接继承监听器接口所对应的适配器类,覆盖感兴趣的方法,而对于不感兴趣的方法则无须实现。
java.awt.event 中的适配器如下:

  • ComponentAdapter:接受组件事件的抽象适配器类
  • ContainerAdapter:接受容器事件的抽象适配器类
  • FocusAdapter:接受键盘焦点事件的抽象适配器类
  • KeyAdapter:接受键盘事件的抽象适配器类
  • MouseAdapter:接受鼠标事件的抽象适配器类
  • WindowAdapter:接受窗口事件的抽象适配器类

3.事件处理的编程方法

简单总结一下:
利用委托模型进行事件处理编程,需要完成两方面的工作:一方面是编写监听器类,完成事件处理方法的代码;另一方面是在组件上注册监听器。
在编写监听器类时,可以采用实现监听器接口的方式;也可以采用继承适配器类的方式,还可以使用匿名内部类来完成。

五、项目案例

项目描述:
编写一个用户注册的窗口,可以在这个窗口实现用户账号、用户密码、重复密码的输入操作,单击注册按钮实现注册功能。

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

class RegistFrame extends JFrame{
    private JTextField userText;
    private JPasswordField password;
    private JPasswordField repassword;
    private JLabel tip;

    RegistFrame(){
        this.setTitle("user regist");
        Container container = this.getContentPane();
        container.setLayout(new BorderLayout());
        JPanel registPanel = new JPanel();
        JLabel userLabel = new JLabel("userText:");
        JLabel passwordLabel = new JLabel("password:");
        JLabel repasswordLabel = new JLabel("repassword:");
        userText = new JTextField(15);
        password = new JPasswordField(15);
        repassword = new JPasswordField(15);
        JButton regist = new JButton("regist");
        JButton exitButton = new JButton("exit");
        registPanel.add(userLabel);
        registPanel.add(new JScrollPane(userText));
        registPanel.add(passwordLabel);
        registPanel.add(new JScrollPane(password));
        registPanel.add(repasswordLabel);
        registPanel.add(new JScrollPane(repassword));
        registPanel.add(regist);
        registPanel.add(exitButton);
        setResizable(false);
        setSize(280,180);
        setLocation(300,100);
        JPanel tipPanel = new JPanel();
        tip = new JLabel();
        tipPanel.add(tip);
        container.add(BorderLayout.CENTER,registPanel);
        container.add(BorderLayout.NORTH,tip);
        exitButton.addActionListener(new ExitActionListener());
        regist.addActionListener(new RegistActionListener());

    }

    class ExitActionListener implements ActionListener{
    public void actionPerformed(ActionEvent event){
        setVisible(false);
        dispose();
    }
}

class RegistActionListener implements ActionListener{
    public void actionPerformed(ActionEvent event){
        boolean bo = false;
        if(bo){
            tip.setText("regist success");
        }
        else
            tip.setText("user exist!");
    }
}

}


public class Test{
    public static void main(String[] args) {
        RegistFrame rf = new RegistFrame();
        rf.setVisible(true);
    }
}

运行结果:
Java程序设计之第八章.GUI设计