第6章 GUI编程.docx
- 文档编号:26845763
- 上传时间:2023-06-23
- 格式:DOCX
- 页数:95
- 大小:454.21KB
第6章 GUI编程.docx
《第6章 GUI编程.docx》由会员分享,可在线阅读,更多相关《第6章 GUI编程.docx(95页珍藏版)》请在冰豆网上搜索。
第6章GUI编程
第6章GUI编程
6.1Swing起步
6.1.1Swing概述
前面几章中,编写的都是控制台应用程序,控制台应用程序和用户之间的交互效率是不能令人满意的。
现代的用户倾向于使用可以由鼠标方便操作的图形用户界面(GUI-GraphicalUserInterface)程序。
本章的内容就是介绍如何使用JFC(JavaFoundationClass)中的Swing组件(Component)来构建GUI应用程序。
先来回顾一下Java在GUI编程上的发展历程。
在Java1.0中,已经有一个用于GUI编程的类库AWT(AbstractWindowToolkit),称之为抽象窗口工具箱。
遗憾的是,AWT中的组件(例如按钮,类名为Button)在实现中使用了本地代码(NativeCode),这些组件的创建和行为是由应用程序所在平台上的本地GUI工具来处理的。
因此,AWT组件要在不同的平台上提供给用户一致的行为就受到了很大的限制。
同时,AWT组件中还存在很多bug,这就使得使用AWT来开发跨平台的GUI应用程序困难重重。
1996年,SUN公司和Netsacpe公司在一个称为Swing的项目中合作完善了Netsacpe公司原来开发的一套GUI库,也就是现在所谓的Swing组件。
Swing组件和原来的AWT组件完全不同,最大的区别就是Swing组件的实现中没有使用本地代码,这样对底层平台的依赖型就大为降低,并且可以给不同平台的用户一致的感觉。
此外,和原来的AWT相比,Swing中提供了内容更多、使用更为方便的组件。
√在GUI编程中,使用什么样的GUI组件固然很重要,但是采用什么事件处理模型同样也很重要。
Java1.0中,AWT的事件处理模型是很不完善的。
Java1.1中使用新的AWT事件处理模型,在此之后,未作变动。
在编写本书时,使用的仍旧是1.1的事件处理模型。
√Swing并不是完全取代了AWT,Swing只是使用更好的GUI组件(如JButton)代替AWT中相应的GUI组件(如Button),并且增加了一些AWT中原来所没有的GUI组件。
并且,Swing仍使用AWT1.1的事件处理模型。
√虽然现在AWT组件仍得到支持,但是建议在你的应用程序中尽量使用Swing组件和1.1的事件模型。
读者在阅读一些书籍时,常会遇到名词JFC(JavaFoundationClass)。
JFC的概念是在1997年的JavaOne开发者大会上首次提出的,是指用于构建GUI的一组API。
实际上,Swing只是JFC的一部分,其它的还有二维图形(Java2D)API以及拖放(DragandDrop)API等等。
6.1.2一个GUI实例
本小节编写我们的第一个GUI应用程序FirstGUI.java。
这个应用程序很简单,只是在屏幕上显示一个框架组件(JFrame),源代码见例6.1.1。
这种框架组件是一种顶层(Top-Level)容器,Swing组件中还有其它三种顶层容器:
JWindow、JDialog和JApplet。
√Swing中的组件是"轻量级"(lightweight)组件,并且每个组件都可以是一个容器。
可以向任何一个组件中添加其它的组件,但是顶层容器类型的组件不能添加到任何其它组件中。
此外,任何一个Swing组件要想在屏幕上显示出来,最终都必须由一个顶层容器来容纳。
图6.1.1在屏幕上显示的一个框架
√Swing中组件的类名通常以'J'开头(如JFrame),以区别于AWT中相应的组件(如Frame)。
Swing位于包javax.swing中,javax是javaextension的缩写形式,表示Swing包是java的一个扩展包。
运行程序FirstGUI.java,可以发现在屏幕的左上角会显示如图6.1.1所示的一个框架。
下面来分析一下这个程序的执行过程:
在命令行输入javaFirstGUI并回车后,应用程序从类FirstGUI的main方法开始执行。
首先创建一个JFrame类型的对象f,随后使用setTitle()和setSize()方法来分别设定框架的标题及大小。
框架对象f创建完毕后,是处于不可见状态的,因此使用了show()方法来使得框架显示在屏幕上。
随后main方法执行完毕退出。
和之前的控制台程序不同的是,该应用程序在main方法退出后并没有终止。
原因是show()方法启动了另外一个GUI线程,使得应用程序仍然处于活动状态。
例6.1.1FirstGUI.java
importjavax.swing.*;
publicclassFirstGUI{
publicstaticvoidmain(String[]args){
JFramef=newJFrame();//创建一个框架对象f
f.setTitle("FirstFrame");//设定框架的标题
f.setSize(250,100);//设定框架的大小
f.show();//显示框架
}
}
运行程序会发现:
点击框架的关闭按钮'
',框架虽然不见了,但是程序仍然没有退出。
这是因为,在默认情况下,关闭框架只是将框架置为不可见,即框架仍旧是'活'的。
使用setDefaultCloseOperation()方法可以改变框架关闭时的默认动作。
例如,如果希望上面的程序在点击框架的关闭按钮后,应用程序退出,可以添加如下语句:
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JFrame类中提供了大量的方法供用户使用,例如setIconImage()用于设定框架的图标,setLocation()用于设定框架在屏幕上的位置,等等。
√Swing中的每个组件都提供了大量的方法供用户使用。
本书作为一本入门教材(而非API手册),所介绍的均是最基本、最常用的方法。
我们不想在本书中罗列每个组件的所有方法。
如果您需要的方法没有在本书中出现,最好的方式是查阅JavaAPI文档。
6.1.3面板
上一节中,我们已经知道组件JFrame是一个顶层容器,本节介绍另一个常用的Swing组件JPanel。
JPanel本身也是一个容器,可以向其中添加其它GUI组件(如按钮JButton);但是JPanel不是顶层容器,因此,要在屏幕上显示JPanel,必须将它添加到一个顶层容器(如JFrame)中。
JPanel还具备在自身表面绘制图形的功能,可以通过定制的方式在面板表面绘制各种图形。
√Swing中允许组件嵌套添加,例如:
可以将一个JButton添加到一个JPanel中,再将JPanel添加到JFrame中。
在构建复杂的用户界面时,常常需要使用这种嵌套添加的方式。
√Swing中还允许将一个组件添加到同类型的组件中,例如:
可以将一个JPanel添加到另一个JPanel中去。
6.1.3.1作为容器
作为容纳其它Swing组件的容器是JPanel最常使用的功能之一。
在制作复杂的用户界面时,常常需要使用多个JPanel将复杂的界面分解为相对较简单的子界面,然后再对每个JPanel进行布局。
下面来看一个将面板作为容器使用的例子。
这个例子中分为两步:
(1)将一个标签和一个文本框添加到面板中。
(2)再将面板添加到框架中,然后显示框架。
步骤
(1)比较简单,先创建一个标签对象、一个文本框对象和一个面板对象,然后调用面板对象中的add()方法将标签和文本框添加到面板中,如:
JLabellabOne=newJLabel("这是标签");
JTextFieldtxtOne=newJTextField("这是文本框");
JPanelp=newJPanel();//生成面板对象
//将标签和文本框添加到面板容器中
p.add(labOne);
p.add(txtOne);
步骤
(2)中将面板添加到框架中有点复杂。
实际上,框架作为一种特殊的顶层容器,其内部结构是很复杂的,如图6.1.2所示。
框架内部按层排列了4种窗格(Pane)并预留了一个存放菜单的位置,其中根窗格(JRootPane)、层叠窗格(JLayeredPane)以及透明窗格(GlassPane)我们可以不必关注,这些窗格是系统用来实现观感时用到的。
内容窗格(ContentPane)和菜单才是我们需要直接用到的。
内容窗格也是一个容器,当需要把组件添加到框架中时,通常是将组件添加到框架的内容窗格中。
因此,当将一个面板添加到框架中时,首先取得框架的内容窗格,然后将面板添加到该内容窗格即可:
//取得框架的内容窗格
ContainercontentPane=f.getContentPane();
//将面板添加到框架的内容窗格中
contentPane.add(p);
完整的程序见例6.1.2,该程序的运行结果见图6.1.3。
图6.1.2框架内部的层次结构
图6.1.3FirstPanel的运行结果
例6.1.2FirstPanel.java
importjavax.swing.*;
importjava.awt.*;
publicclassFirstPanel{
publicstaticvoidmain(String[]args){
JLabellabOne=newJLabel("这是标签");
JTextFieldtxtOne=newJTextField("这是文本框");
JPanelp=newJPanel();//生成面板对象
//将标签和文本框添加到面板容器中
p.add(labOne);
p.add(txtOne);
//给面板增加一个边框
//Borderborder=BorderFactory.createEtchedBorder();
//p.setBorder(border);
JFramef=newJFrame();//创建一个框架对象f
f.setSize(300,300);//设定框架的大小
//取得框架的内容窗格
ContainercontentPane=f.getContentPane();
//将面板添加到框架的内容窗格中
contentPane.add(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.show();//显示框架
}
}
观察FirstPanel.java的运行结果,我们甚至不能觉察到面板的存在。
可以通过给面板增加一个边框(Border)来观察面板的存在。
请读者取消FirstPanel.java中被注释掉的给面板增加边框的语句,重新编译后,观察运行结果。
6.1.3.2表面重绘
在很多情况下(例如组件的首次显示、窗口缩放等等),Swing中的组件需要对其自身的表面进行重绘(repainting),以保证组件的正确显示。
当一个组件需要进行重绘时,事件处理器会通知该组件,从而引起组件paintComponent(Graphicsg)方法的自动调用。
用户永远不直接调用该方法。
如果用户要主动发起组件的重绘,可以调用repaint()方法通知组件需要重绘,从而实现paintComponent(Graphicsg)方法的自动调用。
paintComponent(Graphicsg)方法需要的一个图形参数Graphics也是由系统自动传递进来的。
Graphics类型的对象中存储了用于绘制图形和文本的设置集合(如字体、颜色)以及绘制图形和文本的工具。
由此可知,可以通过覆盖(Override)组件的paintComponent(Graphicsg)方法,在组件表面绘制出我们所希望的内容。
在例6.1.3中,我们自定义了一个类CustomPanel,该类继承了JPanel,并且重新定义了paintComponent(Graphicsg)方法。
需要注意的是,在该方法中第一个语句为:
super.paintComponent(g);
该语句表示调用超类的重绘方法。
调用超类的重绘方法是为了确保超类完成属于自己的那部分重绘工作。
随后,追加我们定制的绘制内容,包括在面板表面绘制一个字符串以及一幅图片。
√在覆盖组件的paintComponent(Graphicsg)方法时,记得首先调用super.paintComponent(g)。
例6.1.3PaintingPanel.java
importjavax.swing.*;
importjava.awt.*;
importjavax.imageio.*;
importjava.io.*;
classCustomPanelextendsJPanel{
publicvoidpaintComponent(Graphicsg){
super.paintComponent(g);//调用超类的重绘方法
//追加的绘制内容如下
g.drawString("画一副图片",20,20);
try{
Imageimage=ImageIO.read(newFile("image/ant.jpg"));
g.drawImage(image,50,50,null);
}catch(Exceptione){
e.printStackTrace();
}
}
}
publicclassPaintingPanel{
publicstaticvoidmain(String[]args){
JFramef=newJFrame();
f.getContentPane().add(newCustomPanel());
f.setSize(500,400);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.show();
}
}
图6.1.4PaintingPanel的运行结果
Graphics对象中提供了在组件表面进行绘制的工具:
例6.1.3中使用drawString()方法在指定的位置绘制一个字符串,drawImage()方法在指定的位置绘制一幅图象。
更多绘制方法可以参考Java帮助文档。
在例6.1.3中用到了类ImageIO,使用其中的静态方法read()可以从指定的文件输入流读入图象。
6.1.4改变应用程序的观感
Java中允许为应用程序指定观感(LookandFeel)。
上面的几个例子中,应用程序使用的是一种称之为Metal的观感(名字为"javax.swing.plaf.metal.MetalLookAndFeel")。
也许有的读者更喜欢Window的观感,可以用下面的程序片断来将应用程序设定为Window观感:
try{
StringlnfName="com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
UIManager.setLookAndFeel(lnfName);
}catch(Exceptione){
e.printStackTrace();
}
其中,"com.sun.java.swing.plaf.windows.WindowsLookAndFeel"是Window观感的名字。
UIManager是一个用户界面管理器,其中的静态方法setLookAndFeel()将应用程序设定为指定名称的观感。
上述代码中使用了Window观感,程序运行后界面上的组件将会以Window的风格显示。
读者可以在前面的几个例子的main方法中加入上述代码片断,然后观察运行结果。
通常,我们会在程序一开始运行时(Swing组件还未显示出来)就设定好观感。
但是有的时候,可能会在程序的运行过程中(Swing组件已经显示出来)动态改变观感。
这时候,在使用UIManager.setLookAndFeel(lnfName)语句设定完观感后,还必须使用SwingUtilities.updateComponentTreeUI()语句来更新所有的已显示组件的观感。
注意,SwingUtilities.updateComponentTreeUI()语句中的方法参数是一个容器,例如:
SwingUtilities.updateComponentTreeUI(frame);//frame为一个JFrame对象
那么容器frame内所有Swing组件的观感将被更新为当前设定的观感。
因此动态改变应用程序观感的一般代码框架可以是:
UIManager.setLookAndFeel(lnfName);//设定观感
//更新容器frame内的所有组件的观感
SwingUtilities.updateComponentTreeUI(frame);
6.2AWT事件处理
6.2.1事件处理简介
用户对应用程序进行操作时会产生事件(Event),例如:
点击按钮会产生一个动作(Action)事件;缩放或是关闭框架会产生一个窗口(Window)事件;移动鼠标会产生鼠标移动(MouseMotion)事件。
在Java中,事件被封装成一个对象,该对象中包含了和事件相关的信息,如事件源、事件类型等等。
同样拿点击按钮来说,按钮就是动作(Action)事件的事件源。
事件源的事件发生后,可以被传递给任何对象,前提是该对象实现(implements)了适当的接口并且注册到该事件源。
例如,可以给按钮的动作事件(ActionEvent)注册(Register)一个动作事件侦听器(ActionListener);任何一个实现了ActionListener接口的类所生成的对象都可以注册成为按钮的动作事件侦听器。
这样,当按钮的动作事件发生时,动作事件就会传递给已注册的所有侦听器。
√一个事件源可以注册多个侦听器,一个侦听器也可以被注册到多个事件源。
给事件源注册事件侦听器,使用该事件源中的addXXXListener(aListener)方法。
依据事件类型的不同,注册的方法名也不同。
例如给按钮注册一个动作事件侦听器:
aButton.addActionListener(aActionListener);
而给框架注册一个窗口事件侦听器:
aFrame.addWindowListener(aWindowListener);
6.2.2事件处理实例
本小节通过一个按钮动作事件实例来进一步理解事件处理的机制。
该程序提供了一个按钮,点击该按钮可以使得应用程序在Windows和Metal观感之间进行切换,源代码见例6.2.1,图6.2.1显示了运行过程中两种不同的观感。
图6.2.1两种不同的观感
在例6.2.1中,内部类LookAndFeelListener实现了动作事件侦听接口ActionListener。
接口ActionListener只有一个需要实现的方法publicvoidactionPerformed(ActionEvente)。
在类ActionFrame中,使用语句:
ActionListeneral=newLookAndFeelListener();
创建了对象al,由于内部类LookAndFeelListener实现了接口ActionListener,因此对象al可以被注册到动作事件源btnLookAndFeel:
btnLookAndFeel.addActionListener(al);
这样,当事件源btnLookAndFeel发生动作事件时,就会调用侦听器al对象中的actionPerformed(ActionEvente)方法,并且所发生的动作事件以一个ActionEvent类型的对象传递进来。
√不仅是JButton类型的事件源能产生动作事件(ActionEvent),也有其它类型的事件源可以产生动作事件。
例如点击菜单(JMenuItem)选项、双击列表框中(JList)的选项以及在文本输入框(JTextField、JPasswordField)中按下回车键等,也会产生动作事件。
例6.2.1ActionEventExample_1.java
importjavax.swing.*;
importjava.awt.event.*;
publicclassActionEventExample_1{
publicstaticvoidmain(String[]args){
newActionFrame().show();
}
}
classActionFrameextendsJFrame{
privateJButtonbtnLookAndFeel=newJButton("Windows");
publicActionFrame(){
ActionListeneral=newLookAndFeelListener();
btnLookAndFeel.addActionListener(al);
JPanelp=newJPanel();
p.add(btnLookAndFeel);
getContentPane().add(p);
this.setSize(300,200);
}
classLookAndFeelListenerimplementsActionListener{
publicvoidactionPerformed(ActionEvente){
Stringtext=btnLookAndFeel.getText().trim();
StringlnfName="javax.swing.plaf.metal.MetalLookAndFeel";
if(text.equals("Windows")){
lnfName="com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
btnLookAndFeel.setText("Metal");
}elseif(text.equals("Metal")){
lnfName="javax.swing.plaf.metal.MetalLookAndFeel";
btnLookAndFeel.setText("Windows");
}
try{
UIManager.setLookAndFeel(lnfName);//设定观感
//更新容器内的所有组件的观感
SwingUtilities.updateComponentTreeUI(ActionFrame.this);
}catch(Exceptionexcp){
excp.printStackTrace();
}
}
}
}
前面我们已经提到,任何实现了ActionListener接口的类所生成的对象,均可作为动作事件的侦听器。
因此,在例6.2.1中,也可以让类ActionFrame实现ActionListener接口,然后使用ActionFrame类型的对象来作为侦听器,这样就可以不使用内部类。
为此,可以将例6.2.1改写为例6.2.2的形式:
例6.2.2ActionEventExample_2.java
importjavax.swing.*;
importjava.awt.event.*;
publicclassActionEventExample_2
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第6章 GUI编程 GUI 编程