11.AWT编程

本章要点

  • 图形用户界面编程的概念
  • AWT的概念
  • AWT容器和常见布局管理器
  • 使用AWT基本组件
  • 使用对话框
  • 使用文件对话框
  • java的事件机制
  • 事件源,事件,事件监听器的关系
  • 使用菜单条,菜单,菜单项创建菜单
  • 创建并使用右键菜单
  • 重写paint()方法实现绘图
  • 使用Graphics类
  • 使用BufferedImage和ImageIO处理位图
  • 使用剪贴板
  • 剪贴板数据风格

java使用AWT和Swing类完成图形用户界面编程,其中AWT的全称是抽象窗口工具集(Abstract Window Toolkit),它是Sun最早提供的GUI库,这个GUI库提供了一些基本功能,单证GUI库的功能比较有限,所以后来又提供了Swing库。通过使用AWT和Swing提供的图形界面组件库,java的图形用户界面编程非常简单,程序只要一次创建所需的图形组件,并以合适的方式将这些组件组织在一起,就可以开发出非常美观的用户界面。

程序以一种“搭积木”的方式将这些图形用户组件组织在一起,就是实际可用的图形用户及没按,但这些图形用户界面还不能与用户交互,为了实现图形用户界面的用户交互操作,程序还应为程序提供事件处理,事件处理负责让程序可以响应用户动作。

11.1 GUI(图形用户界面)和AWT

作为一个程序设计者,必须优先考虑用户的感受,一定要让用户感到“爽”,我们的程序才会被需要,被使用,这样的程序才有价值。

Swing并没有完全替代AWT,而是建立在AWT基础之上。

11.2 AWT 容器

如果从程序员的角度来看一个窗口时,这个窗口不是一个整体,而是由多个部分组合而成的。

任何窗口都可被分解成一个空的容器,容器里盛装了大量基本组件,通过设置这些基本组件的大小,位置等属性,就可以将该空容器和基本组件组成一个整体的窗口。实际上,在笔者看来,图形界面编程非常简单,它非常类似于我们儿时玩的拼图游戏,容器类似于拼图的“母板”,而普通组件(如Button,list之类)则类似于拼图的图块。创建图形用户界面的过程就是完成拼图的过程。

容器是Component的子类,因此容器对象本身也是一个组件,具有组件的所有性质,可以调用钻进的所有方法。

Frame代表常见的窗口。

Panel是AWT中另一个典型的容器,它代表不能独立存在,必须放在其他容器中的容器。Panel外在表现为一个矩形区域,该区域内科盛装其他组件。Panel容器存在的意义在于为其他组件提供空间,Panel容器具有如下几个特点:

  • 可作为容器来盛装其他组件,为放置组件提供空间。
  • 不能单独存在,必须放置到其他容器中。
  • 默认使用FlowLayout作为其布局管理器。

SrollPane是一个带滚动条的容器,它也不能独立存在,必须被添加到其他容器中。ScrollPane容器具有如下几个特点:

  • 可作为容器来盛装其他组件,当组件占用空间过大时,ScrollPane自动产生滚动条。当然也可以通过制定特定的构造器参数来指定默认具有滚动条。
  • 不能单独存在,必须放置到其他容器中。
  • 默认使用BorderLayout作为其布局管理器。ScrollPane通常用于盛装其他容器,所以通常不允许改变ScrollPane的布局管理器。

11.3 布局管理器

为了使生成的图形用户界面具有良好的平台无关性,java语言中,提供了布局管理器这个工具来管理组件在容器中的布局,而不使用直接设置组件位置和大小的方式。

对于不同的组件而言,他们都有一个最佳大小(既没有冗余空间,也没有内容被遮挡),这个最佳大小通常是平台相关的,程序在不同平台上运行时,相同内容的大小可能不一样。如果让程序手动控制每个组件的大小,位置,这将给编程带来巨大的困难,为了解决这个问题,java提供了LayoutManager来解决这个问题,LayoutManager可以根据运行平台来调整组件的大小,程序员要做的,只是为容器选择合适的布局管理器。

所有的AWT容器都有默认的布局管理器,如果没有为容器指定布局管理器,该容器使用默认的布局管理器。为容器指定布局管理器通过调用容器对象的setLayoutManger(LayoutManager Im)方法来完成。

AWT提供了FlowLayout,BorderLayout,GriLayout,GridBigLayout,CardLayout五个常用的布局管理器,Swing还提供了一个BoxLayout布局管理器。

11.3.1 FlowLayout 布局管理器

在FlowLayout布局管理器中,组件像水流一样向某方向流动(排列),遇到障碍(边界)就折回,重头开始排列。默认情况下,FlowLayout布局管理器从左向右排列所有组件,遇到边界就折回下一行重新开始。

11.3.2 BorderLayout 布局管理器

BorderLayoout将容器分为EAST,SOUTH,WEST,NORTH,CENTER五个区域,普通组件可以被放置在这五个区域中任意一个。BorderLayout的布局示意如图所示。

当改变使用BorderLayout的容器大小时,NORTH,SOUTH和CENTER区域水平调整,而EAST,WEST和CENTER垂直调整。使用BorderLayout有如下两个注意点:

  • 当向使用BorderLayout布局管理器的容器中添加组件时,需要指定要添加到哪个区域里。如果没有指定添加到哪个区域里,则默认添加到中间区域里。
  • 如果向同一个区域中添加多个组件时,后放入的组件会覆盖前面的组件。

11.3.3 GridLayout 布局管理器

GridLayout布局管理器将容器分割成纵横线分割的网络,每个网格所占的区域大小相同。当向使用gridlayout的容器中添加组件时,默认从左向右,从上向下依次添加到每个网格中。与FlowLayout不同的是,放在gridlayout布局管理器中的各组件的大小由组件所处的区域来决定(每个组件将自动涨大到占满整个区域)。

11.3.4 GridBagLayout 布局管理器

GridBagLayout布局管理器是功能最强大,但也是最复杂的布局管理器,与GridLayout布局管理器不同的是:在GridBagLayout布局管理器中,一个组件可以跨越一个或多个网格,并可以设置各网格的大小互不相同,从而增加了布局的灵活性。当窗口的大小发生变化时,GridBagLayout布局管理器也可以准确地控制窗口各部分的反应。

11.3.5 CardLayout 布局管理器

CardLayout 布局管理器以时间而非空间来管理它里面的组件,它将加入容器的所有组件看成一叠卡片,每次只有最上面的那个Componet才可见。就好像一副扑克牌,它们叠在一起,每次只有最上面的一张扑克牌才可见。

11.3.6 绝对定位

11.3.7 BoxLayout布局管理器

GridBagLayout布局管理器虽然功能强大,但它实在太复杂了,所以Swing引入了一个新的布局管理器:BoxLayout。它保留了GridBagLayout的很多优点,但是却没那么复杂。BoxLayout可以在垂直和水平两个方向上摆放GUI组件。

11.4 AWT 常用组件

AWT组件需要调用运行平台的图形界面来创建和平台一致的对等体,所以AWT只能使用所有平台都支持的公共组件,所以AWT只提供了一些常用的GUI组件。

11.4.1 基本组件

AWT提供了如下基本组件:

  • Button:按钮,可接受单击操作。
  • Canvas:用于绘图的画布。
  • Checkbox:复选框组件(也可变成单选框组件)。
  • CheckboxGroup:用于将多个Checkbox组件组合成一组,一组Checkbox组件将只有一个可以被选中,即全部变成单选框组件。
  • Choice:下拉式选择框组件。
  • Frame:窗口,在GUI程序里通过该类创建窗口。
  • Label:标签类,用于放置提示性文本。
  • List:列表框组件,可以添加多项条目。
  • Panel:不能单独存在基本容器类,必须放到其他容器中。
  • Scrollbar:滑动条组件。如果需要用户输入位于某个范围的值,就可以使用滑动条组件。如调色板中设置RGB的三个值所用的滑动条。当创建一个滑动条时,必须指定它的方向,初始值,滑块的大小,最小值和最大值。
  • ScrollPane:带水平及垂直滚动条的容器组件。
  • TextArea:多行文本域。
  • TextField:单行文本框。

11.4.2 对话框

对话框是window类的子类,是一个容器类,属于特殊组件。对话框也是可以独立存在的顶级窗口,因此用法与普通窗口用法几乎完全一样。但对话框有两点需要注意:

  • 对话框通常依赖于其他窗口,就是通常有一个parent窗口。
  • 对话框有非模式(non-modal)和模式(modal)两种,当某个模式对话框被关闭之前,它依赖的窗口无法获得焦点。

对话框有多个重载的构造器,它的构造器可能有如下三个参数:

  • owner:指定该对话框所依赖的窗口,既可以是窗口,也可以是对话框。
  • title:指定该对话框的窗口标题。
  • modal:指定该对话框是否是模式的,可以是true或false。

Dialog类还有一个子类:FileDialog,它代表一个文件对话框,用于打开或者保存文件,FileDialog也提供了几个构造器,分别可支持parent,title和model三个构造参数,其中parent,title指定文件对话框的所属父窗口和标题,而model用于指定该窗口用于打开文件或保存文件,该参数支持如下两个参数值:FileDialog.LOAD,FileDialog.SAVE.

提示:FileDialog不能指定是模式对话框或是非模式对话框,因为FileDialog依赖于运行平台的实现,如果运行平台的文件对话框是模式的,那么FileDialog‘也是模式的;否则就是非模式的。

FileDialog提供了如下两个方法来获取被打开/保存文件的路径:

  • getDirectory():获取FileDialog被打开/保存文件的绝对路径。
  • getFile():获取FileDialog被打开/保存文件的文件名。

11.5 事件处理

前面介绍了如何放置各种组件,使得图形界面能呈现出丰富多彩的图形界面,但这些界面还不能响应用户的任何操作。就像我们单击前面所有窗口右上角的“X”按钮,但窗口依然不会关闭。因为再AWT编程中,所有事件的处理必须由特定对象(事件监听器)来处理,而Frame和组件本身并没有事件处理能力。

11.5.1 java事件模型的流程

为了使图形界面能够接收用户的操作,必须给各个组件加上事件处理机制。

在事件处理的过程中,主要涉及3类对象:

  • Event Source(事件源): 事件发生的场所,通常就是各个组件,例如按钮,窗口,菜单等。
  • Event(事件):事件封装了GUI组件上发生的特定事情(通常就是一次用户操作)。如果程序需要获得GUI组件上所发生事件的相关信息,都通过Event对象来取得。
  • Event Listener(事件监听器):负责监听事件源所发生的事件,并对各种事件做出相应处理。

提示:

有过JavaScript,VB等编程经验的读者都知道,事件响应的动作实际上就是一系列的程序语句,通常以方法的形式组织起来,但java是面向对象的编程语言,方法不能独立存在,所以必须以类的形式来组织这些方法,所以事件监听器的核心就是它所包含的方法---这些方法也被称为事件处理器。当事件源上的事件发生时,事件对象会作为参数传给事件处理器。

当用户按下一个按钮,或者单击某个菜单项,或单击窗口右上角的状态按钮时,这动作就会激发一个相应的事件,该事件会由AWT封装成一个相应的Event对象,该事件就会触发事件源上注册的事件监听器(特殊的java对象),事件监听器调用相应的事件处理器(事件监听器里的实例方法)来作出响应的响应。

AWT的事件处理机制是一种委派式(Delegation)事件处理方式:普通组件(事件源)将整个事件处理委托给特定的对象(事件监听器);当该事件源发生指定的事件时,就通知所委托的事件监听器,由事件监听器来处理这个事件。

每个组件均可以针对特定的事件指定一个或多个事件监听对象,每个事件监听器也可监听一个或多个事件源。因为同一个事件源上可能发生多种事件,委派式事件处理方式可以把事件源上所有可能发生的事件分别授权给不同的事件监听器来处理;同时也可以让一类事件都使用同一个事件监听器来处理。

提示:

委派式事件处理方式明显“抄袭”了人类社会的分工协作,例如某个单位发生了火灾,该单位通常不会自己处理该事件,而是将该事件委派给消防局(事件监听器)处理;如果发生了打架斗殴事件,则委派给公安局(事件监听器)处理;而消防局,公安局也会同时监听多个单位的火灾,大家斗殴事件。这种委派式的处理方式将事件源和时间监听器分离,从而提供更好的程序模型,有利于提高程序的可维护性。

实现AWT事件处理机制的步骤如下:

  • 实现事件监听器类,该监听器类是一个特殊的java类,必须实现一个XxxListener接口。
  • 创建普通组件(事件源),创建事件监听器对象。
  • 调用addXxxListener方法将事件监听器对象注册给普通组件(事件源)。这样当事件源上发生指定事件时,AWT会触发事件监听器,由事件监听器调用相应的方法(事件处理器)来处理事件,事件源上所发生的事件会作为参数传入事件处理器。

11.5.2 事件和事件监听器

EventObject类代表更广义的事件对象,包括Swing组件上所引发事件,数据库连接所引发的事情等。

AWT事件分为两大类:低级事件和高级事件。

低级事件

低级事件是指基特定动作的事件。如鼠标的进入,点击,拖放等动作的鼠标事件,当组件得到焦点,失去焦点时触发焦点事件。

  • ComponentEvent:组件事件,当组件尺寸发生变化,位置发生移动,显示/隐藏状态发生改变触发该事件。
  • ContainerEvent:容器事件,当容器里发生成添加组件,删除组件时触发该事件。
  • WindowEvent:窗口事件,当窗口状态发生改变(如打开,关闭,最大化,最小化)时触发。
  • FocusEvent:焦点事件,当组件得到焦点或失去焦点时触发该事件。
  • KeyEvent:键盘事件,当键进行按下,松开,单击时触发该事件。
  • MouseEvent:鼠标事件,当鼠标进行单击,按下,松开,移动等动作时触发该事件。
  • PaintEvent:组件绘制事件,该事件是一个特殊事件类型,当GUI组件调用update/paint方法来呈现自身时将触发该事件,该事件并非专用于事件处理模型。

高级事件(语义事件)

高级事件是基于语义的事件,它可以不和特定的动作相关联,而依赖于触发此事件的类。比如,在TextField中按Enter键会触发ActionEvent事件,滑动滚动条会触发AdjustmentEvent事件,选中项目列表的某一条就会触发ItemEvent事件。

  • ActionEvent:动作事件,当按钮,菜单项被单击,TextField中按Enter键时触发。
  • AdjustmentEvent:调节事件,在滑动条上移动滑块以调节数值时触发。
  • ItemEvent:选项事件,当用户选中某项,或取消选中某项时触发。
  • TextEvent:文本事件,当文本框,文本域里的文本发生改变时触发。

11.5.3 事件适配器

事件适配器是监听器接口的空实现:事件适配器实现了监听器接口,并为该接口里每个方法都提供了实现,这种实现是一种空实现(方法体内没有任何代码的实现)。当需要创建监听器时,可以通过集成事件适配器,而不是实现监听器接口。因为事件适配器已经为监听器接口的每个方法提供了空实现所以程序自己的监听器无须实现监听器接口里的每个方法,只需要重写自己感兴趣的方法,从而可以简化事件监听器的实现类代码。

注意:

如果某个监听器接口只有一个方法,则该监听器接口就无须提供适配器,因为该接口对应的监听器别无选择,只能重写该方法!如果不重写该方法,就没有必要实现该监听器。

11.5.4 事件监听器的实现形式

事件监听器是一个特殊的java对象,实现事件监听器对象有如下几种形式:

  • 内部类形式:将事件监听器类定义成当前雷达额内部类。
  • 顶级类形式:将事件监听器类定义成一个顶级类。
  • 类本身作为事件监听器类:让当前类本身实现监听器接口或继承事件适配器。
  • 匿名内部类形式:使用匿名内部类创建事件监听器对象。

内部类形式

使用内部类可以在当前类中复用该监听器类,因为监听器类是外部类的内部类,所以可自由访问外部类的所有GUI组件,这也是内部类的两个优势。

顶级类形式

使用顶级类定义事件监听器的形式比较少见,主要因为如下两个原因:

  • 事件监听器通常属于特定的GUI界面,定义成顶级类不利于提高程序的内聚性。
  • 顶级类形式的事件监听器不能自由访问创建GUI界面的类中的组件,编程不够简洁。

但如果某个事件监听器确实需要被多个GUI界面所共享,而且主要是完成某种业务逻辑的实现,则可以考虑使用顶级类的形式来定义事件监听器类。

注意;

实际上我们不推荐将业务逻辑实现写在事件监听器中,包含业务逻辑的事件监听器将导致程序的显示逻辑和业务逻辑耦合,从而增加程序后期的维护难度,如果确实有多个实现监听器需要实现相同的业务逻辑功能,可以考虑使用业务逻辑组件来定义业务逻辑功能,再rag事件监听器来调用业务逻辑组件的业务逻辑方法。

类本身作为事件监听器类

这种形式使用GUI界面类直接作为监听器类,可以直接在GUI界面类中定义事件处理器方法,这种形式非常简洁,也是早期AWT事件编程里比较喜欢采用的形式。但这种做法有两个缺点:

  • 这种形式可能造成混乱的程序结构,GUI界面的职责应该主要完成界面初始化工作,但此时还需包含事件处理器方法,从而引起混乱。
  • 如果GUI界面类需要继承事件适配器,将会导致该GUI界面类不能继承其他父类。

匿名内部类形式

大部分时候,事件处理器都没有什么复用价值(可复用代码通常都被抽象成了业务逻辑方法),因此大部分事件监听器只是临时使用一次,所以使用匿名内部类形式的事件监听器更合适。实际上,这种形式是目前使用最广泛的事件监听器形式。

11.6 AWT 的菜单

前面介绍了创建GUI界面的方式:将AWT组件按某种布局摆放在容器内即可。创建AWT菜单的方式与此完全类似:将菜单条,菜单,菜单项组合在一起即可。

11.6.1 菜单条,菜单和菜单项

AWT中的菜单由如下几个类组合而成:

  • MenuBar:菜单条,菜单的容器。
  • Menu:菜单组件,菜单项的容器。它也是MenuItem的子类,所以可作为菜单项使用。
  • PopupMenu:上下文菜单组件(右键菜单组件)。
  • MenuItem:菜单项组件。
  • CheckboxMenuItem:复选框菜单项组件。
  • MenuShortcut:菜单快捷键组件。

11.6.2 右键菜单

右键菜单使用PopupMenu对象表示,创建右键菜单的步骤如下:

  1. 创建PopupMenu的实例。
  2. 创建多个MenuItem的多个实例,依次将这些实例加入PopupMenu中。
  3. 将PopupMenu加入到目标组件之中。
  4. 为需要出现上下文菜单的组件编写鼠标监听器,当用户释放鼠标右键时弹出右键菜单。

注意:

记住AWT的实现机制!AWT并没有为GUI组件提供实现,它仅仅是调用运行平台的GUI组件来创建和平台一致的对等体。因此程序中TextArea实际上是Windows(假设在Windows平台上运行)的多行文本域组件的对等体,具有和它相同的行为,所以该TextArea默认就具有右键菜单。

11.7 在AWT中绘图

很多程序如各种小游戏都需要在窗口中绘制各种图形,还有java的早期应用:Applet,更是可以做出丰富多彩的动画效果,这些都需要在AWT组件上画图。

11.7.1 画图的实现原理

在Component类里提供了三个和绘图有关的方法:

  • paint(Graphics g):绘制组件的外观。
  • update(Graphics g):调用paint方法,刷新组件外观。
  • repaint():调用update方法,刷新组件外观。

上面三个方法的调用关系为:repait方法调用update方法;update方法调用paint方法。

Container类中的update方法先以组件的背景色填充整个组件区域,然后调用paint方法重画组件。

程序不应该主动调用组件的paint和update方法,这两个方法都由AWT系统负责调用。如果程序希望AWT系统重新绘制该组件,调用该组件的repait方法即可。而paint方法和update方法通常用于被重写。通常情况下,程序通过重写paint方法实现在AWT组件上绘图。

11.7.2 使用Graphics类

Graphics是一个抽象的画笔对象,Graphics可以在组件上绘制丰富多彩的几何图形和位图,Graphics类提供了如下几个方法用于绘制几何图形和位图:

  • drawLine:绘制直线。
  • drawString:绘制字符串。
  • drawRect:绘制矩形。
  • drawRoundRect:绘制圆角矩形。
  • drawOval:绘制椭圆形状。
  • drawPolygon:绘制多边形边框。
  • drawArc:绘制一段圆弧(可能是椭圆的圆弧)。
  • drawPolyline:绘制折线。
  • fillRect:填充一个矩形区域。
  • fillRoundRect:填充一个圆角矩形区域。
  • fillOval:填充椭圆区域。
  • fillPolygon:填充一个多边形区域。
  • fillArc:填充圆弧和圆弧两个端点到中心连线所包围的区域。
  • drawImage:绘制位图。

除此之外,Graphics还提供了setColor和setFont两个方法设置画笔的颜色和字体(仅当绘制字符串时有效),其中setColor方法需要传入一个Color参数,它可以使用RGB,CMYK等方式设置一个颜色;而setFont方法需要传入一个Font参数,Font参数需要指定字体名,字体样式,字体大小三个属性。

提示:

实际上不仅Graphics对象可使用setColor和setFont 来设置画笔的颜色和字体,AWT普通组件也可以通过Color和Font来改变它的前景色和字体。除此之外,所有组件都有一个setBackground方法用于设置组件的背景色。

AWT专门提供一个Canvas类作为绘图的画布,程序可以通过创建Canvas的子类,并重写它的paint方法来实现绘图。

java也可用于开发一些动画。所谓动画,就是隔一定的时间间隔(通常小于0.1秒)重新绘制新的图像,两次绘制的图像之间差异较小,肉眼看起来就成了所谓的动画。为了实现隔一定的时间间隔就重新调用组件的repaint方法,可以借助于Swing提供的Timer类,Timer类是一个定时器。

11.8 处理位图

如果仅仅绘制一些简单的几何图形,程序的图形效果依然比较单调。AWT也允许在组件上绘制位图,Graphics提供了drawImage方法用于绘制位图,该方法需要一个Image参数--它就代表了位图,通过该方法就可以绘制出指定位图。

11.8.1 Image抽象类和BufferedImage实现类

11.8.2 使用ImageIO输入/输出位图

11.9 剪贴板

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值