资源描述
单击此处编辑母版标题样式,*,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,第6章,GUI,编程,现代的用户倾向于使用可以由鼠标方便操作的图形用户界面(,GUI-Graphical User Interface),程序。本章的内容就是介绍如何使用,JFC(Java Foundation Class),中的,Swing,组件(,Component),来构建,GUI,应用程序。,6.1,Swing,起步-,Swing,概述,在,Java1.0,中,已经有一个用于,GUI,编程的类库,AWT(Abstract Window Toolkit),,称之为抽象窗口工具箱。遗憾的是,,AWT,中的组件(例如按钮,类名为,Button),在实现中使用了本地代码(,Native Code),,这些组件的创建和行为是由应用程序所在平台上的本地,GUI,工具来处理的。因此,,AWT,组件要在不同的平台上提供给用户一致的行为就受到了很大的限制。同时,,AWT,组件中还存在很多,bug,,这就使得使用,AWT,来开发跨平台的,GUI,应用程序困难重重。,1996年,,SUN,公司和,Netsacpe,公司在一个称为,Swing,的项目中合作完善了,Netsacpe,公司原来开发的一套,GUI,库,也就是现在所谓的,Swing,组件。,Swing,组件和原来的,AWT,组件完全不同,最大的区别就是,Swing,组件的实现中没有使用本地代码,这样对底层平台的依赖型就大为降低,并且可以给不同平台的用户一致的感觉。此外,和原来的,AWT,相比,,Swing,中提供了内容更多、使用更为方便的组件。,读者在阅读一些书籍时,常会遇到名词,JFC(Java Foundation Class),。,JFC,的概念是在,1997,年的,JavaOne,开发者大会上首次提出的,是指用于构建,GUI,的一组,API,。,实际上,,Swing,只是,JFC,的一部分,其它的还有二维图形,(,Java 2D)API,以及拖放,(,Drag and Drop)API,等等。,Swing,概述-续,在,GUI,编程中,使用什么样的,GUI,组件固然很重要,但是采用什么事件处理模型同样也很重要。,Java 1.0,中,,AWT,的事件处理模型是很不完善的。,Java 1.1,中使用新的,AWT,事件处理模型,在此之后,未作变动。在编写本书时,使用的仍旧是,1.1,的事件处理模型。,Swing,并不是完全取代了,AWT,,,Swing,只是使用更好的,GUI,组件,(,如,JButton,),代替,AWT,中相应的,GUI,组件,(,如,Button),并且增加了一些,AWT,中原来所没有的,GUI,组件。并且,,Swing,仍使用,AWT 1.1,的事件处理模型。,虽然现在,AWT,组件仍得到支持,但是建议在你的应用程序中尽量使用,Swing,组件和,1.1,的事件模型。,6.1.2,一个,GUI,实例,下面的这个应用程序很简单,只是在屏幕上显示一个框架组件,(,JFrame,),:,import,javax,.swing.*;,public class,FirstGUI,public static void main(String ,args,),JFrame,f=new,JFrame,(); /,创建一个框架对象,f,f.,setTitle,(,FirstFrame,); /,设定框架的标题,f.,setSize,(250,100); /,设定框架的大小,f.show(); /,显示框架,GUI,实例续,Swing,中的组件是,“,轻量级,”(,lightweight),组件,并且每个组件都可以是一个容器。可以向任何一个组件中添加其它的组件,但是顶层容器类型的组件不能添加到任何其它组件中。此外,任何一个,Swing,组件要想在屏幕上显示出来,最终都必须由一个顶层容器来容纳。,Swing,中组件的类名通常以,J,开头,(,如,JFrame,),,,以区别于,AWT,中相应的组件,(,如,Frame),。,Swing,位于包,javax,.swing,中,,javax,是,java,e,x,tension,的缩写形式,表示,Swing,包是,java,的一个扩展包。,6.1.3,面板,JPanel,是常用的,Swing,组件之一,。,JPanel,本身也是一个容器,可以向其中添加其它,GUI,组件,(,如按钮,JButton,),;,但是,JPanel,不是顶层容器,因此,要在屏幕上显示,JPanel,,,必须将它添加到一个顶层容器,(,如,JFrame,),中。,JPanel,还具备在自身表面绘制图形的功能,可以通过定制的方式在面板表面绘制各种图形。,Swing,中允许组件嵌套添加,例如:可以将一个,JButton,添加到一个,JPanel,中,再将,JPanel,添加到,JFrame,中。在构建复杂的用户界面时,常常需要使用这种嵌套添加的方式。,Swing,中还允许将一个组件添加到同类型的组件中,例如:可以将一个,JPanel,添加到另一个,JPanel,中去。,6.1.3.1,作为容器,import,javax,.swing.*;,import java.,awt,.*;,public class,FirstPanel,public static void main(String ,args,),JLabel labOne,=new,JLabel,(,这是标签,);,JTextField txtOne,=new,JTextField,(,这是文本框,);,JPanel,p=new,JPanel,(); /,生成面板对象,/,将标签和文本框添加到面板容器中,p.add(,labOne,);,p.add(,txtOne,);,/,给面板增加一个边框,/,Border border=,BorderFactory,.,createEtchedBorder,();,/p.,setBorder,(border);,JFrame,f=new,JFrame,(); /,创建一个框架对象,f,f.,setSize,(300,300); /,设定框架的大小,/,取得框架的内容窗格,Container,contentPane,=f.,getContentPane,();,/,将面板添加到框架的内容窗格中,contentPane,.add(p);,f.,setDefaultCloseOperation,(,JFrame,.EXIT_ON_CLOSE);,f.show(); /,显示框架,分为两步:,(1) 将一个标签和一个文本框添加到面板中。,(2) 再将面板添加到框架中,然后显示框架。,6.1.3.2,表面重绘,paintComponent,(Graphics g),方法,。,repaint(),方法,。,在覆盖组件的,paintComponent,(Graphics g),方法时,记得首先调用,super.,paintComponent,(g),。,6.1.4,改变应用程序的观感,UIManager,.,setLookAndFeel,(String,lnfName,),方法。,SwingUtilities,.,updateComponentTreeUI,(),方法。,例如,要设定,Windows,的观感:,try,String,lnfName,=com.sun.java.swing.,plaf,.windows.,WindowsLookAndFeel,;,UIManager,.,setLookAndFeel,(,lnfName,);,catch(Exception e),e.,printStackTrace,();,6.2,AWT,事件处理,6.2.1,事件处理简介,用户对应用程序进行操作时会产生事件(,Event) ,,事件源的事件发生后,可以被传递给任何对象,前提是该对象实现(,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,中,内部类,LookAndFeelListener,实现了动作事件侦听接口,ActionListener,。,接口,ActionListener,只有一个需要实现的方法,public void,actionPerformed,(,ActionEvent,e),。,在类,ActionFrame,中,使用语句:,ActionListener,al=new,LookAndFeelListener,();,创建了对象,al,,,由于内部类,LookAndFeelListener,实现了接口,ActionListener,,,因此对象,al,可以被注册到动作事件源,btnLookAndFeel,:,btnLookAndFeel,.,addActionListener,(al);,这样,当事件源,btnLookAndFeel,发生动作事件时,就会调用侦听器,al,对象中的,actionPerformed,(,ActionEvent,e),方法,并且所发生的动作事件以一个,ActionEvent,类型的对象传递进来。,事件处理实例续,不仅是,JButton,类型的事件源能产生动作事件,(,ActionEvent,),,,也有其它类型的事件源可以产生动作事件。例如点击菜单,(,JMenuItem,),选项、双击列表框中,(,JList,),的选项以及在文本输入框,(,JTextField,、,JPasswordField,),中按下回车键等,也会产生动作事件。,本小节介绍了三种(独立的类、内部类以及匿名内部类,具体内容参见教材,),方式来注册事件侦听器,希望读者都能够掌握。因为,这三种方式都可能在别的程序员编写的程序中出现,而你很可能需要阅读这样的程序。,6.2.3,使用事件适配器,前面我们已经知道,在缺省情形下,关闭框架,(,JFrame,),只是使之隐藏。可以通过,f.,setDefaultCloseOperation,(,JFrame,.EXIT_ON_CLOSE);,使得在关闭框架时应用程序退出。由于在关闭框架时,会发生一个窗口事件(,WindowEvent,),,因此也可以通过捕获该事件,然后让应用程序退出。任何实现了,WindowListener,接口的类所生成的对象均可以注册到窗口事件源。由于,WindowListener,接口中包含了如下的七个方法:,public interface,WindowListener,void,windowActivated,(,WindowEvent,e);,void,windowClosed,(,WindowEvent,e);,void,windowClosing,(,WindowEvent,e);,void,windowDeactivated,(,WindowEvent,e);,void,windowDeiconified,(,WindowEvent,e);,void,windowIconified,(,WindowEvent,e);,使用事件适配器续,因此,一个类要实现,WindowListener,接口,就必须实现该接口中的所有七个方法。例如,可以按如下方式定义一个,WindowExit,类,并且实现,WindowListener,接口:,class,WindowExit,implements,WindowListener,public void,windowActivated,(,WindowEvent,e),public void,windowClosed,(,WindowEvent,e),public void,windowClosing,(,WindowEvent,e),System.exit(0);/,强制应用程序退出,public void,windowDeactivated,(,WindowEvent,e),public void,windowDeiconified,(,WindowEvent,e),public void,windowIconified,(,WindowEvent,e);,public void,windowOpened,(,WindowEvent,e),然后就可以将,WindowExit,生成的对象注册为窗口事件侦听器:,JFrame,f=new,JFrame,();,WindowListener wl,= new,WindowExit,();,f.,addWindowListener,(,wl,);,使用事件适配器续,然而,在很多时候我们只对其中某个或是某几个方法感兴趣,例如在本例中,就只对其中的,windowClosing,(,WindowEvent,e),方法感兴趣,如果像上面一样直接定义一个类去实现,WindowListener,接口,对那些不感兴趣的方法也必须实现。尽管只是用空方法去实现那些不感兴趣的方法,这也是一件麻烦的事情。,AWT,事件模型中使用适配器,(,Adapter),类来帮助程序员做这件事情:对于那些具有不止一个方法的事件侦听接口,(,例如,WindowListener,),,,都提供了一个用空方法实现接口中全部方法的适配器类,(,例如,WindowAdapter,),。,这样当程序员只需要继承适配器类,然后覆盖自己感兴趣的方法即可,而不用再去实现接口中所有方法。例如,上面的,WindowExit,类可以按如下方式来定义:,class,WindowExit,extends,WindowAdapter,public void,windowClosing,(,WindowEvent,e),System.exit(0);/,强制应用程序退出,然后使用和前面类似的方式,将,WindowExit,生成的对象注册为窗口事件侦听器:,JFrame,f=new,JFrame,();,WindowListener wl,= new,WindowExit,();,f.,addWindowListener,(,wl,);,这样,就大大减少了程序员的编码工作量。,6.2.4,AWT,事件继承关系,AWT,事件模型中事件类的继承关系,AWT,事件模型中提供的侦听器接口及对应的适配器类,侦听器接口,适配器类,ActionListener,无,AdjustmentListener,无,ComponentListener,ComponentAdapter,ContainerListener,ContainerAdapter,FocusListener,FocusAdapter,ItemListener,无,KeyListener,KeyAdapter,MouseListener,MouseAdapter,MouseMotionListener,MouseMotionAdapter,MouseWheelListener,无,TextListener,无,WindowListener,WindowAdapter,WindowFocusListener,无,WindowStateListener,无,6. 3,布局管理,在,Java,中,,GUI,组件在容器中的布局是由容器的布局管理器(,Layout Manager),来决定的。每个容器都具有一个缺省的布局管理器。程序设计人员可以方便地改变容器的布局管理器。例如,面板的缺省布局管理器是流布局管理器,(,FlowLayout,),,,内容窗格的缺省布局管理器是边框布局管理器,(,BorderLayout,),。,如果面板或是内容窗格的缺省布局管理器不能满足要求,可以调用这两种容器的,setLayout,(,aNewLayout,),方法来改变其布局管理器,方法,setLayout,(),的参数是一个布局管理器对象。,6.3.1,流式布局,例,6.3.1,使用类似方法向一个面板中添加三个按钮,图,6.3.1,是程序的运行结果。可以发现,这三个按钮是按照被添加到面板中的顺序排列在一行上的。如果将框架的宽度缩小,可以发现三个按钮将排列成两行,如图,6.3.2,所示。由此可知:使用,FlowLayout,布局时,,GUI,组件将按照添加入容器的顺序自左而右排列在一行上,如果一行排不下,另起一行;也就是说组件是按照自左而右、自上而下的顺序进行排列的。,FlowLayout,类提供了三种构建器,public,FlowLayout,(),、,public,FlowLayout,(,int,alignment),和,public,FlowLayout,(,int,alignment,int horizontalGap,int verticalGap,),。,alignment,参数可以取值,FlowLayout,.LEFT,、,FlowLayout,.CENTER,或是,FlowLayout,.RIGHT,,,用于指定组件在一行上的对齐方式。,horizontalGap,和,verticalGap,分别表示组件在水平和垂直方向上的间距,(,以像素为单位,),。缺省情况下,,alignment,取值为,FlowLayout,.CENTER,,,horizontalGap,和,verticalGap,均取值为,5,。,6.3.2 边框布局,使用了,边框(,BorderLayout,),布局的容器均提供,5,个位置用于存放组件,分别是,North,、,South,、,East,、,West,以及,Center,,,如图所示:,下面的语句将一个按钮添加到内容窗格中:,contentPane,.add(new,JButton,(North),BorderLayout,.NORTH);,例子:,import,javax,.swing.*;,import java.,awt,.*;,public class,BorderLayoutExample,public static void main(String ,args,),JFrame,f=new,JFrame,();,Container,contentPane,=f.,getContentPane,();,contentPane,.add(new,JButton,(North),BorderLayout,.NORTH);,contentPane,.add(new,JButton,(South),BorderLayout,.SOUTH);,contentPane,.add(new,JButton,(West),BorderLayout,.WEST);,contentPane,.add(new,JButton,(East),BorderLayout,.EAST);,contentPane,.add(new,JButton,(Center),BorderLayout,.CENTER);,f.,setSize,(300,200);,f.show();,运行结果,观察运行结果,如果对框架进行缩放,可以发现,BorderLayout,的特点是:,North,和,South,位置的组件在水平方向上会扩张,以填满水平方向的空间,垂直方向不变;,West,和,East,位置的组件在垂直方向上会扩张,以填满垂直方向的空间,水平方向不变;而,Center,位置的组件会在水平和垂直方向均会尽量扩张,填满中部的空间。,此外,如果向同一个位置,(,比如,West),添加多个组件,那么只有最后一个被添加的才是有效的,也就是说,BorderLayout,布局中的每个位置只能放一个组件。,6.3.3,网格布局,网格布局(,GridLayout,),将容器划分为大小相同的网格,把,GUI,组件向使用了网格布局的容器中添加时,是按照自左向右,自上而下的位置存放的。,GridLayout,类提供了两个构建器:,public,GridLayout,(,int,rows,int,columns),public,GridLayout,(,int,rows,int,columns,int horizontalGap,int verticalGap,),rows,和,columns,分别指定划分网格的行数及列数。,horizontalGap,和,verticalGap,用于指定组件在水平和垂直方向上的间隔,缺省情况下均为零。,阅读例,6.3.3,GridWindow,.java,的源代码。,例,6.3.3运行结果,可以发现,添加到使用网格布局容器中的,GUI,组件,均具有相同的大小。对容器进行缩放后,其中的,GUI,组件也进行同步的缩放。也就是说,网格布局适合于规则的布局。,6.3.4,网格袋布局,在构建复杂的用户界面时,仅仅使用前面所讲的三种布局往往不能达到理想的效果。网格袋布局,GridBagLayout,是最灵活(也是最复杂的)一种布局管理器,该布局管理器具有强大的功能,非常适合复杂界面的布局。与网格布局类似,网格袋布局也是将用户界面划分为若干个网格(,Grid),,不同之处在于:,(1) 网格袋布局中的每个网格的宽度和高度都是可以不一样的。,(2) 每个组件可以占据一个或是多个网格。,(3) 可以指定组件在网格中的停靠位置。,当将一个,GUI,组件添加到使用了网格袋布局的容器中时,需要指定该组件的位置、大小以及缩放等一系列约束条件。可以使用一个,GridBagConstraints,类型的对象来存储这些约束条件,这样,向使用网格袋布局的容器中添加组件的代码框架如下:,代码框架:,JPanel,p=new,JPanel,();,JTextField txtField,=new,JTextField,();,p.,setLayout,(new,GridBagLayout,();/,容器,p,设置为网格袋布局,/创建约束条件对象,GridBagConstraints,constraints=new,GridBagConstraints,();,/,设置具体的约束条件,.,/按照约束条件,constraints,将,txtField,添加到,p,中,p.add(,txtField, constraints);,如何设置约束条件,是使用网格袋布局中最重要、也是最困难的步骤。下面,我们通过一个实例来仔细说明约束条件中有哪些参数需要设置以及如何设置。,实例:,目标:们想在一个面板上按照图,6.3.7,所示排列组件,此外,我们还希望面板上的标签在面板缩放时,大小不改变;文本框在面板缩放时,在水平方向上能够相应地缩放,而在垂直方向上大小不改变。,参数设置,对于要添加到该面板中的组件,需要有一个,GridBagConstraints,类型的约束条件对象,constraints。,那么,,constraints,中有如下字段需要设置:,constraints.,gridx,和,constraints.,gridy,这两个字段用于指定组件的起始网格坐标。例如,对于出生日期标签:,constraints.,gridx,=2; constraints.,gridy,=1;,对于备注文本框:,constraints.,gridx,=1;constraints.,gridy,=2;,constraints.,gridwidth,和,constraints.,gridheight,这两个字段用于指定组件所占网格的列数和行数。对于备注文本框:,constraints.,gridwidth,=2; constraints.,gridheight,=1;,对于出生日期标签:,constraints.,gridwidth,=1; constraints.,gridheight,=1;,constraints.fill,组件在网格中的填充方式。,GridBagConstraints,中定义了一些常量用于,确定组件在网格中的填充方式:,GridBagConstraints,. HORIZONTAL /,水平方向尽量扩张,GridBagConstraints,. VERTICAL /,垂直方向尽量扩张,GridBagConstraints,. BOTH /,水平、垂直方向均扩张,GridBagConstraints,. NONE /,水平、垂直方向均不扩张,参数设置,constraints.insets,该字段是一个,Insets,类型的对象。 该对象用作所添加组件的外部填塞,其大小由该对象中的,left、top、right,及,buttom,字段决定。例如:,constraints.insetsnew Insets(2,2,2,2);,constraints.,ipadx,和,constraints.,ipady,与,constraints.insets,字段相对应,这两个字段称为组件的内部填塞。这两个值被加到组件的最小宽度和最小高度上,从而保证组件不会收缩到它的最小尺寸之下。,constraints.anchor,当组件比所在网格小的时候,可以使用该字段来确定组件在网格中的停靠位置。,GridBagConstraints,中定义了如下的常量来表示组件的停靠位置:,GridBagConstraints,.CENTER,GridBagConstraints,.NORTH,GridBagConstraints,.NORTHEAST,GridBagConstraints,.EAST,GridBagConstraints,.SOUTHEAST,GridBagConstraints,.SOUTH,GridBagConstraints,.SOUTHWEST,GridBagConstraints,.WEST,GridBagConstraints,.NORTHWEST,参数设置,constraints.,weightx,和,constraints.weighty,这两个字段是组件在水平方向和垂直方向的扩张权重。如果在某个方向上不希望组件扩张,则将该方向上的权重置为0。例如对于出生日期标签,水平、垂直方向均不希望扩张:,constraints.,weightx,=0; constraints.weighty=0;,对于备注文本框,水平方向扩张,而垂直方向不扩张:,constraints.,weightx,=1; constraints.weighty=0;,理论上权重可以取任意非负值,用以表示扩张的程度。但是实践中我们发现,通过调整权值来设定各个组件扩张意义并不大。一个简单、适用的规律就是:将需要扩张方向上的权重设定为1;不需要扩张方向上的权重设定为0。,特别要小心,权重字段的取值要和,fill,字段相适应。,源代码见,例,6.3.4 。,6.4,常用,GUI,组件,标签 (,JLabel,),通常是用来标识另外一个组件的含义 。以在标签上显示文字、图象或是文字图象的组合。,JLabel labText,=new,JLabel,(“,文本标签,”);,labText,.,setText,(,文本标签);,ImageIcon,icon = new,ImageIcon,(image/greenflag20.gif);,JLabel labImage,=new,JLabel,(icon);,labImage,.,setIcon,(,aIcon,);,如果要在标签上同时显示文本和图象,可以使用,JLabel,提供的一个构建器,JLabel,(String text, Icon icon,int horizontalAlignment,),。,该构建器中,第一个参数是欲显示的文本,第二个参数是欲显示的图象,第三个参数为水平方向上的对齐方式,取值为:,SwingConstants,.LEFT,、,SwingConstants,.RIGHT,或,SwingConstants,. CENTER,。,如:,JLabel labTextImage,=new,JLabel,(,文本图象标签,icon,SwingConstants,.LEFT);,标签上同时显示文本和图象时,在缺省情况下,文字是显示在图象的右侧的。如果希望文字显示在图象的左侧,可以使用如下方法:,labTextImage,.,setHorizontalTextPosition,(,SwingConstants,.LEFT);,类似地,可以设置文本与图象在垂直方向上的相对位置。,标签还支持使用,HTML,类型的文本参数,使用,HTML,可以方便地在标签上显示丰富多彩的文本,例如:,String,htmlText,= a Red Label;,JLabel labHTML,=new,JLabel,(,htmlText,);,6.4.2.1,文本框,用户可以在文本框中输入单行文本并且进行编辑。下面的代码生成了一个文本框对象:,JTextField txtA,=new,JTextField,();,在生成一个文本框对象时初始化文本框中的文本内容:,JTextField txtA,=new,JTextField,(,abc,);,指定文本框的列宽度:,JTextField txtA,=new,JTextField,(20);,同时指定初始文本内容与列宽度:,JTextField txtA,=new,JTextField,(,abc,20);,不要依赖列宽度参数来设定文本框的宽度大小。列宽度参数只是影响到文本框初始大小的设定,最终还是由文本框所在容器的布局管理器来决定的。,使用文本框对象的,getText,(),方法,可以取得文本框中的文本内容。,setEditable,(,boolean aValue,),方法来设置文本框是否可编辑。,6.4.2.2,密码框,密码框实际上是一种特殊类型的文本框,用户可以向其中输入文本并加以编辑。和文本框不同的是,向密码框中输入文本时,显示的不是实际输入的文本,而是特殊的回显字符(通常是*)。可以使用,setEchoChar,(char c),方法来改变缺省的回显字符。,需要注意的是,取得文本框中的文本时,使用方法,getText,(),,该方法返回的是一个,String,类型的对象;而要取得密码框中的文本,使用方法,getPassword,(),,该方法返回的是一个,char,数组。,例如,创建了一个密码框:,JPasswordField txtPwd,=new,JPasswrodField,(20);,设定该密码框的回显字符为#:,txtPwd,.,setEchoChar,(#);,取得密码框中的内容:,char ,pwd,=,txtPwd,.,getPassword,();,也可以方便地将,char,数组转化为,String,类型的对象:,String,pwdStr,=new String(,txtP,.,getPassword,();,6.4.2.3,文本域,文本域允许用户在其中输入多行文本并进行编辑。创建一个文本域对象:,JTextArea txtArea,=new,JTextArea,();,还可以在创建时指定文本域的行数和列数:,JTextArea txtArea,=new,JTextArea,(10,30);,需要注意的是,和文本框一样,不要依赖列这两个参数来设定文本域的大小。,文本域对象的,setLineWrap,(true),方法来将文本域设置为自动换行 。,给文本域加上滚动条非常简单,只需要将文本域作为参数创建一个滚动窗格(,JScrollPane,),即可(,不仅仅是文本域,很多其它组件需要增加滚动条时,也是将组件添加到滚动窗格中,):,JFrame,f=new,JFrame,();,JTextArea,t=new,JTextArea,();,JScrollPane,scroll=new,JScrollPane,(t);,f.,getContentPane,().add(scroll);,滚动窗格提供了方法用来设定水平或是垂直滚动条的显示策略:,setHorizontalScrollBarPolicy,(,int,policy),setVerticalScrollBarPolicy,(,int,policy),参数,policy,可以取值为,(,三种策略,),:,JScrollPane,.VERTICAL_SCROLLBAR_AS_NEEDED /,根据需要显示,JScrollPane,.VERTICAL_SCROLLBAR_NEVER /,从不显示,JScrollPane,.VERTICAL_SCROLLBAR_ALWAYS /,一直显示,6.4.2.4,组合框,使用组合框,JComboBox,。这样,一方面可以减少用户的输入工作量,另一方面还可以减少用户输入出错的机会。,组合框可以以两种模式工作:可编辑模式与不可编辑模式。在缺省情形下,组合框处于不可编辑的模式。,在不可编辑编辑模式下,用户点击组合框后,组合框会提供一个选项列表供用户选择,并且用户只能从该选项列表中选择一项作为组合框的输入。在可编辑模式下,一方面用户可以从选项列表中选择,另一方面还可以直接在组合框中输入并进行编辑。,使用,addItem,(Object,anObject,),方法向组合框中添加选项,例如:,JComboBox comFont,=new,JComboBox,();,comFont,.,addItem,(“,宋体,”);,如果想在选项列表中插入选项,使用,insertItemAt,(Object,anObject,int,index),方法,第一个参数是待插入的选项,第二个参数是插入的位置索引,例如:,comFont,.,insertItemAt,(“,隶书,”,2);,组合框还提供了两种方法用以删除选项列表中的选项:,removeItem,(Object,anObject,),和,removeItemAt,(,int anIndex,),。,例如:,comFont,.,removeItem,(,宋体,);,comFont,.,removeItemAt,(0),组合框事件,当用户选中了组合框选项列表中的一项,组合框会生成一个动作事件(,ActionEvent,)。,监听该事件,就可以取得当前被选中的选项。在事件侦听器中使用,getSelectedItem,(),方法:,Object,selectedItem,=,comFont,.,getSelectedItem,();,也可以先使用,getSelectedIndex,(),得到所选中选项的位置索引,然后使用,getItemAt,(,int,index),方法来取得选项值:,int,index=,comFont,.,getSelectedIndex,();,Object,selectedItem,=,comFont,.,getItemAt,(index);,在可编辑的组合框中,键入回车后,组合框同样发生动作事件。这里需要注意的是:如果用户在组合框中输入的值不是选项列表中的任何一项(以图6.4.8示例),在监听器中使用,getSelectedItem,(),方法,得到的结果是方正姚体:,Object,selectedItem,=,comFont,.,getSelectedItem,();,/,selectedItem,为 方正姚体,而如果使用如下方式,则不能正确得到用户的实际输入:,int,index=,comFont,.,getSelectedIndex,(); /index,的值为-1,Object,selectedItem,=,comFont,.,getItemAt,(index);,/,selectedItem,为,null,6.4.3.1,单选按钮,单选按钮(,JRadioButton,),通常成组(,Group),使用,即若干个单选按钮构成一组,并且每次只能有一个按钮被选中,适用于从多个备选选项中选择一项的场合(如图6.4.9所示)。从完成的功能来看,类似于不可编辑的组合框。,当备选选项内容较少时,既可以使用单选按钮,也可以使用组合框。但是当备选选项内容较多时,使用单选按钮就不合适了,因为要占据太多的画面显示空间。,在实际使用时,先生成一组单选按钮,例如:,JRadioButton radMSSQL,=new,JRadioButton,(MS SQL Server);,JRadioButton radOracle,=new,JRadioButton,(ORACLE Server);,JRadioButton radMysql,=new,JRadioButton,(,MySQL,Server);,然后生成一个按钮组(,ButtonGroup,),对象,并将这些单选按钮添加到其中:,ButtonGroup,group=new,ButtonGroup,();,group.add(,radMSSQL,);,group.add(,radOracle,);,group.add(,radMysql,);,单选按钮续,当对一组单选按钮进行布局时,是对该组中的每个按钮进行布局,而不是对按钮组对象布局。按钮组对象只是用来控制这一组按钮的行为的,即每次仅有一个按钮能被选中。,和其它类型的按钮一样,点击一个单选按钮时,同样生成动作事件(,ActionEvent,)。,例6.4.4中,使用了三个单选按钮构成一个按钮组。这三个单选按钮分别指示不同的数据库服务器类型。当用户点击单选按钮时,会在一个标签上显示出当前所选定的数据库服务器类型。,阅读,例,6.4.4 源代码。,6.4.3.2,复选框,前面所讲的组合框和单选按钮均只能从备选选项中选择一项,即各个选项之间是互斥的。当需要从备选选项中选择不止一项时,可以使用复选框,(,JCheckBox,),。,复选框是一种二状态的,GUI,组件:重复点击同一个复选框,会在选中和未选中这两种状态之间进行切换。一组复选框中可以同时有多个复选框被选中,如图,6.4.10,所示。,下面的这条语句生成一个复选框对象,其中的字符串参数用以表示该复选框的含义:,JCheckBox chkOperation,=new,JCheckBox,(,清空操作记录);,要判断一个复选框是否被选中,使用方法,isSelected,()。,如果返回值是,true,,表示选中;若返回值为,false,,表示未选中。还可以使用,setSelected,(,boolean aValue,),方法设定复选框是否被选中,参数,aValue,为,true(false),时设定为选中(未选中)。,同样,点击一个复选框,会生成一个动作事件。,点击复选框也会生成一个,ItemEvent,事件,任何实现了,ItemListener,接口的类所生成的对象均可以作为,ItemEvent,事件的监听器,,ItemListener,接口中唯一的方法是,public void,itemStateChanged,(,ItemEvent,e),。,6.4.3.4,列表框,列表框(,JList,),将一组选项以列表的方式提供给用户选择(参见表6.4.1)。根据列表框所设定的性质不同,用户可以同时选择一个或是多个选项。下面的两行代码生成了一个列表框对象:,Object employee= Tom Hanks,Bob,Jack London,Sindy, Mike,Lizz,Jerrey,;,JList lstEmployee,=new,JList,(employee);,数组,employee,中的元素将作为列表框,lstEmployee,的选项供用户选择。需要注意的是,使用这种方法创建的列表框,不能再向其中添加、插入或是删除选项。例如,要向其中添加一个名叫“,Jerry”,的雇员是不行的,也即这种列表框中的选项是不可改变(,immutable),的。,如果希望创建一个可改变选项的列表框,则需要使用一个可改变的数据模型,例如使用一个,DefaultListModel,类型的模型:,DefaultListModel lstModel,=new,DefaultListModel,();,JList lstEmployee,=new,JList,(,lstModel,);,DefaultListModel,类型的模型中存储的数据是可变的。使用,addElement,(Object,aObject,),向模型中添加数据:,lstModel,.,addElement,(Tom Hanks);,列表框续,如果需要在指定位置插入一个选项,使用,insertElementAt,(Object,aObject,int,index),,第一个参数为欲插入的数据,第二个参数为指定的插入位置索引:,lstModel,.,insertElementAt,(Bob,0);/,将,Bob,置为列表框的第一个选项值,有三种方法可以删除模型中的数据:,void,removeAllElements,(); /,删除模型中所有数据,boolean removeElement,(Object,aObject,); /,删除模型中指定的数据,void,removeElementAt,(,int,index); /,删除模型中指定位置索引的数据,当列表框中的选项较多时,就需要给列表框加上滚动条。与给文本域添加滚动条类似,也是将列表框作为参数创建一个滚动窗格,(,JScrollPane,),对象:,JScrollPane,scroll=new,JScrollPane,(,lstEmployee,);,列表框续,使用,setSelectionMode,(,int,mode),方法可设置列表框的选中模式,列表框共有三种选中模式:,ListSelectionModel,.SINGLE_SELECTION,ListSelectionModel,.SINGLE_INTERVAL_SELECTION),ListSelectionModel,.MULTIPLE_INTERVAL_SELECTION,不管列表框使用了何种选中模式,只要改变了选中的内容,列表框就会生成一个列表选择事件,(,ListSelectionEvent,),。,实现了,ListSelectionListener,接口的类所生成的对象可以作为列表选择事件的侦听器。,ListSelectionListener,接口中唯一的方法是:,public void,valueChanged,(,ListSelectionEvent,e),。,6.4.4.1,菜单,菜单也是一种常用的,GUI,组件,菜单采用的是一种层次结构,最顶层是菜单栏,(,JMenuBar,),;,在菜单栏中可以添加若干个菜单,(,JMenu,),,,每个菜单中又可以添加若干个菜单选项,(,JMenuItem,),、,分隔线,(,Separator),或是菜单,(,称之为子菜单,),。,菜单续,构建应用程序的菜单时,先创建一个菜单栏:,JMemuBar menuBar,=new,JMenuBar,();,通常使用框架的,setJMenuBar,(,JMenuBar aMenuBar,),方法将菜单栏置于框架中:,frame.,setJMenuBar,(,menuBar,);,随后,创建所需要的各菜单并逐个添加到菜单栏中,例如:,JMenu menuDBAccount,=new,JMenu,(,电表出帐(,C);,.,JMenu menuSysConfig,=new,JMenu,(,系统设置(,X);,menuBar,.add(,menuDBAccount,);,.,menuBar,.add(,menuSysConfig,);,菜单续,最后,向各个菜单中添加菜单选项、分隔线或是子菜单,这里以图,6.4.11,中所示的系统设置菜单为例:,/,创建菜单选项或是子菜单,JMenuItem sysConfigItem,=new,JMenuItem,(,参数设置,(,S).);,JMenu viewMenu,=new,JMenu,(,外观设置,);,JRadioButtonMenuItem metalItem,=new,JRadioButtonMenuItem,(,金属外观,);,JRadioButtonMenuItem classicItem,=new,JRadioButtonMenuItem,(,传统外观,);,JRadioButtonMenuItem modernItem,=new,JRadioButtonMenuItem,(,现代外观,);,JMenuItem cascadeItem,=new,JMenuItem,(,层叠窗口,(,C);,JMenuItem iconifyItem,=new,JMenuItem,(,排列图标,(,I);,/,将三个单选按钮添加到一个按钮组,ButtonGroup,group=ne
展开阅读全文