第8章GUI(下)
8.5 常用AWT组件
在上一章开始的第一个程序中,我们就用到了按钮(Button),这是最简单的组件之一了,在AWT里还有很多用于GUI设计的组件,我们现在就来了解更多的GUI组件。如图8.9所示描述了AWT中的组件及类层次关系图。
图8.9
8.5.1 Component类
Java的图形用户界面的最基本组成部分是组件,组件是一个可以以图形化的方式显示在屏幕上并能与用户进行交互的对象,例如一个按钮,一个标签等。抽象类Component是所有Java GUI组件的共同父类。Component类规定了所有GUI组件的基本特性,该类中定义的方法实现了作为一个GUI组件所应具备的基本功能。Java程序要显示的GUI组件必须是抽象类Component或MenuComponent的子类。
8.5.2 Canvas
Canvas代表屏幕上的一块空白的矩形区域,程序能够在这个部件表面绘图,也能够捕
获用户的操作,产生相应的事件,Canvas可以说是具有最基本的和最简单的GUI功能的部件。当我们要设计一种自己定制的具有GUI功能的部件类,我们的这个类就可以继承Canvas,这样,我们的部件类就已经完成了GUI的基本功能,我们只需要在这个基础上增加子类部件所专有的外观和功能的相关代码就行了,我们要想绘制子类部件的外观,我们必须覆盖Canvas的paint方法。
我们现在设计一个计时器部件,鼠标在部件上按下时,计时器开始计时,并在部件上显示计时时间,鼠标释放时,计时器停止计时。下面是这个计时器的程序代码,其中涉及到的技巧和知识点,都在前面有过讲解,这里就不作详细解释了。
程序清单:TestStopWatch.java
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.text.SimpleDateFormat;
class StopWatch extends Canvas implements Runnable
{
private long startTime = 0;
private long endTime = 0;
private boolean bStart = false;
public StopWatch()
{
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
setSize(80,30);
}
protected void processMouseEvent(MouseEvent e)
{
if(e.getID() == MouseEvent.MOUSE_PRESSED)
{
bStart = true;
startTime = endTime = System.currentTimeMillis();
repaint();
new Thread(this).start();
}
else if(e.getID() == MouseEvent.MOUSE_RELEASED)
{
bStart = false;
repaint();
}
super.processMouseEvent(e);
}
public void paint(Graphics g)
{
SimpleDateFormat sdf= new SimpleDateFormat("HH:mm:ss");
Date elapsedTime =null;
try
{
elapsedTime= sdf.parse("00:00:00");
}catch(Exception e){}
elapsedTime.setTime(endTime - startTime +
elapsedTime.getTime());
String display = sdf.format(elapsedTime);
g.drawRect(0,0,78,28);
g.fill3DRect(2,2,75,25,true);
g.setColor(Color.RED);
g.drawString(display,10,20);
}
public void run()
{
while(bStart)
{
try
{
Thread.sleep(500);
}catch(Exception e){e.printStackTrace();}
endTime = System.currentTimeMillis();
repaint();
}
}
}
public class TestStopWatch
{
public static void main(String [] args)
{
Frame f =new Frame("StopWatch");
f.add(new StopWatch());
f.setSize(200,200);
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}
编译并运行这个程序,你一定要用鼠标按住计时器部件几秒钟,你就能看到我们期望的效果了,如图8.10所示。
图8.10
如果你想让这个计时器部件尽可能满足更多的应用需求,你还需要增加一些功能,如允许用户设置文本的颜色,文本的大小随部件大小的改变而改变等。
8.5.3 Checkbox
如果你熟悉Windows的应用,对单选按钮和多选按钮一定不会陌生,这两种按钮都有选中和不选两种状态,如图8.3所示。对多选按钮来说,如果有多个这样的按钮,每个按钮之间没有制约关系,可以同时选中其中的多个。而单选按钮则要求有一组按钮,这一组按钮中同时只能有一个为选中状态。
Java里提供的这个Checkbox类来建立单选按钮和多选按钮,Checkbox的使用很容易,如果要创建多选按钮,我们只要使用public Checkbox(String label, boolean state)这个构造函数来创建Checkbox对象就行了,创建多选按钮要用到两个参数,前一个是选框旁边的说明文字,后一个参数决定选框是否默认被选定。因为创建单选按单钮需要一组按钮,所以在创建单选按钮时,我们还需要指定这个按钮所属于的组,使用
public Checkbox(String label,boolean state,CheckboxGroup group)
这个构造函数创建的就是单选按钮。其中,CheckboxGroup类对象指定了这个单选按钮所属于的组。
对一般的程序来说,需要处理单选按钮和多选按钮的ItemEvent事件,从而获得用户选择的结果。处理ItemEvent事件的监听器接口为ItemListener,其中只有一个itemStateChanged方法,显然,ItemEvent是一种语义事件。
下面是一段创建多选按钮和单选按钮以及相关事件处理的程序代码:
程序清单:TestCheckbox.java
import java.awt.*;
import java.awt.event.*;
public class TestCheckbox
{
Checkbox cb1=new Checkbox("你喜欢我吗?",true);
CheckboxGroup cbg=new CheckboxGroup();
Checkbox cb2=new Checkbox("喜欢",cbg,true);
Checkbox cb3=new Checkbox("不喜欢",cbg,false);
public void init()
{
Frame f=new Frame("TestCheckBox");
//创建FlowLayout布局管理器,关于布局管理器,本章后面有专门的讲解,看不明白//的读者暂时可以不去下面两句代码的作用。
FlowLayout fl=new FlowLayout();
f.setLayout(fl);
f.add(cb1);
f.add(cb2);
f.add(cb3);
cb1.addItemListener(new CbItemListener());
cb2.addItemListener(new CbItemListener());
cb3.addItemListener(new CbItemListener());
f.setBounds(0,0,300,100);
f.setVisible(true);
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
class CbItemListener implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
Checkbox cb = (Checkbox)e.getItemSelectable();
if(cb.getLabel().equals("你喜欢我吗?"))
{
if(cb.getState() == true)
System.out.println("我很高兴");
else
System.out.println("我很伤心");
}
/*else if(cb.getLabel().equals("喜欢"))
{
if(e.getStateChange() == ItemEvent.SELECTED)
System.out.println("我也喜欢你");
else
System.out.println("我也不喜欢你");
}*/
else
{
Checkbox cbx =cbg.getSelectedCheckbox();
if(cbx != null)
System.out.println(cbx.getLabel());
}
}
}
public static void main(String[] args)
{
new TestCheckbox().init();
}
}
程序运行效果如图8.11所示:
图8.11
如果我们用注释的程序代码来处理单选按钮,需要为每一个单选按钮都编写一段这样的代码,如果按钮的个数较多,程序就比较臃肿,但从这段注释代码中,读者可以了解到对同一问题的多种处理方式,也可以在正好有这方面的需要时参考。如果我们不想直接处理这些单选按钮的事件,而是在别的部件的事件处理代码中收集这些单选按钮的选择结果。譬如,我们经常在最后单击ok按钮的事件中再去收集设置对话框上的所有部件的设置结果,我们使用类似程序中else部分的代码,就是一个不错的方法。
8.5.4 Choice
Choice类用来制作用于单选的下拉列表,用起来也比较容易,来看这段程序:
程序清单:TestChoice.java
import java.awt.*;
import java.awt.event.*;
public class TestChoice
{
Choice ch=new Choice(); //创建Choice对象
TestChoice()
{
ch.add("choice1"); //用add方法向列表里加入选项
ch.add("choice2"); //用add方法向列表里加入选项
ch.add("choice3"); //用add方法向列表里加入选项
FlowLayout fl=new FlowLayout();
Frame f=new Frame("TestChoice");
f.setLayout(fl);
f.add(ch); //把列表加入到窗口
f.setBounds(0,0,200,100);
f.setVisible(true);
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public static void main(String[] args)
{
new TestChoice();
}
}
如图8.12所示是程序执行后展开列表的情况:
图8.12
8.5.5 菜单
一个完整的菜单系统由菜单条、菜单和菜单项组成。他们与菜单的对应关系如图8.13所示:
图8.13
在图中,File、Edit、Tools、Help各项叫做菜单,这些顶层菜单共同组合成菜单条,在File项的下拉菜单中New、Open等各项叫做菜单项。
Java中与菜单相关的类主要有三个MenuBar(菜单条)、Menu(菜单)、MenuItem(菜单项)。
我们来看看完成图8.13的菜单系统的程序代码。
程序清单:TestMenuBar.java
import java.awt.*;
import java.awt.event.*;
public class TestMenuBar
{
MenuBar menubar=new MenuBar(); //创建菜单条对象
Menu fileM=new Menu("File"); //创建各菜单
Menu editM=new Menu("Edit"); //创建各菜单
Menu toolsM=new Menu("Tools"); //创建各菜单
Menu helpM=new Menu("Help"); //创建各菜单
MenuItem fileMI1=new MenuItem("New"); //创建各菜单项
MenuItem fileMI2=new MenuItem("Open"); //创建各菜单项
MenuItem fileMI3=new MenuItem("Save"); //创建各菜单项
CheckboxMenuItem fileMI5=new CheckboxMenuItem("Quit",true);
//创建各菜单项
Menu filePrint = new Menu("print");//创建子菜单
MenuItem printM1 = new MenuItem("preview");
MenuItem printM2 = new MenuItem("setting");
TestMenuBar()
{
FlowLayout fl=new FlowLayout();
Frame f=new Frame("TestMenuBar");
f.setLayout(fl);
menubar.add(fileM); //将菜单加入菜单条
menubar.add(editM);
menubar.add(toolsM);
menubar.add(helpM);
fileM.add(fileMI1); //将菜单项加入file菜单中
fileM.add(fileMI2);
fileM.add(fileMI3);
filePrint.add(printM1);//将菜单项加入print菜单中
filePrint.add(printM2);
fileM.add(filePrint);
//将print菜单作为一个菜单项加入file菜单中
fileM.addSeparator(); //将一条分割线加入菜单中
fileM.add(fileMI5); //将菜单项加入菜单中
f.setMenuBar(menubar); //把整个菜单系统显示在窗口中
f.setBounds(0,0,250,200);
f.setVisible(true);
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public static void main(String[] args)
{
new TestMenuBar();
}
}
在窗口上产生菜单的过程非常简单,我们首先要产生MenuBar对象,然后产生Menu对象,最后产生MenuItem对象。将MenuItem增加到Menu上后,再将Menu增加到MenuBar 上,最后将MenuBar 挂到Frame窗口上。要注意的一点是,Menu类本身又是MenuItem的子类,一个Menu对象也可以作为一个菜单项增加到另外一个Menu对象上,这就是我们在上面看到的print子菜单。
关于程序中用到的复选菜单以及菜单分割条,读者不用刻意去记,用到的时候查一下JDK文档就可以找到,另外,在诸如Jbuilder的集成开发环境里,其可视化的生成工具就刻意帮我们产生这样程序代码,我们只要看得懂就行了。
程序不用处理菜单条和菜单的事件,但需要对菜单项的动作进行响应,单击一个菜单项,会发出ActionEvent事件。我们来看看如何对上面程序中的print子菜单的两个菜单项的动作进行处理,为了直观,下面只给出了新增部分的程序代码。
Menu filePrint = new Menu("print");//创建子菜单
MenuItem printM1 = new MenuItem("preview");
MenuItem printM2 = new MenuItem("setting");
MenuListener ml = new MenuListener();
printM1.addActionListener(ml);
printM2.addActionListener(ml);
class MenuListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(e.getActionCommand().equals("preview"))
System.out.println("doing preview");
else if(e.getActionCommand().equals("setting "))
System.out.println("doing setting");
}
}
对于发出ActionEvent事件的组件,我们可以调用setActionCommand方法为其关联一个字符串,用于指示这个动作想执行的命令。如果程序没有使用setActionCommand方法为组件关联一个命令字符串,则其命令字符串为组件的标题文本。ActionEvent的getActionCommand方法就是用于返回这个命令字符串的。
使用命令字符串,我们可以用同一菜单来发出连接和断开的命令,在要发出的命令为连接前,我们用MenuItem. setActionCommand指定命令字符串为“connect”,在要发出的命令为断开前,我们指定命令字符串为“disconnect”,事件处理程序通过判断这个命令字符串,就知道该采取哪种动作了。如果我们程序中的菜单要针对不同的国家,用不同语言文字显示,我们不管菜单项标题上显示的是什么文字,只要用setActionCommand方法为这个菜单项指定一个命令字符串,我们就可以用同样的事件处理程序去处理这个用不同语言文字显示的菜单项的事件。
其实,其他一些组件也可以使用getActionCommand方法,到底有哪些,读者只要在JDK 文档中查找getActionCommand就知道了,如图8.14所示:
图8.14
8.5.6 Container类
组件不能独立地显示出来,必须将组件放在一定的容器中才可以显示出来。象我们上面见到的窗口就是一个容器,类Container是所有容器的父类,容器(Container)实际上是Component的子类,因此容器类对象本身也是一个组件,具有组件的所有性质,另外还具有容纳其它组件和容器的功能。容器类对象可以使用方法add()添加组件。容器类的类层次图如图8.15所示
图8.15
Container有几个主要的子类:Window类,Panel类,ScrollPane类。
8.5.7 Window类
Window类是可自由停泊的顶级窗口,它没有边框和菜单条,我们很少直接使用Window 类,而是使用它的两个子类:Frame类和Dialog类。Frame对象显示效果是一个“窗口”,带有标题和尺寸重置角标,默认初始化为不可见的,可以使用setVisible(true)方法使之变为可见,在前面我们已经多次用到了Frame类。
8.5.8 Dialog
Dialog(对话框)一般是一个临时的窗口,用于显示提示信息或接收用户输入。在对话框中一般不需要菜单条,也不需要改变窗口大小。有两种模式的对话框,模态对话框和非模态对话框。模态对话框显示时,用户不能操作其它窗口,直到这个对话框被关闭。非模态对话框显示时,用户还可以操作其它窗口。Dialog类用来创建用户对话框,对话框和框架(Frame)比较相似,同样可以在对话框上添加其它的组件。
Dialog类有两种常用的构造方法:
public Dialog(Frame owner,String title);
public Dialog(Frame owner,String title,boolean modal)
对话框不能独立存在,它必须有一个上级窗口,这个上级窗口就是对话框的拥有者。这两个构造方法的第一个参数代表对话框的拥有者,第二个参数是对话框标题,第三个参数设置对话框的模式,如果是true,则为模态对话框,如果是false,则为非模态对话框。
下面是一个关于对话框的例子程序。
程序清单:TestDialog.java
import java.awt.*;
import java.awt.event.*;
public class TestDialog
{
TextField tf = new TextField(10);
Button b1=new Button("模态显示");
Button b2=new Button("非模态显示");
Frame f=new Frame("TestDialog");
Button b3=new Button("确定");
Dialog dlg = new Dialog(f, "Dialog Title", true);
FlowLayout fl=new FlowLayout();
TestDialog()
{
f.setLayout(fl);
f.add(tf);
f.add(b1);
f.add(b2);
b1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
dlg.setModal(true);
dlg.setVisible(true);
tf.setText("https://www.wendangku.net/doc/6017744684.html,");
}
});
b2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
dlg.setModal(false);
dlg.setVisible(true);
tf.setText("https://www.wendangku.net/doc/6017744684.html,");
}
});
f.setBounds(0,0,400,200);
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
dlg.setLayout(fl);
dlg.add(b3);
b3.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dlg.dispose();
}
});
dlg.setBounds(0,0,200,150);
}
public static void main(String[] args)
{
new TestDialog();
}
}
程序运行效果如图8.16所示:
图8.16
细心的读者能够发现,当我们用模态方式显示对话框后,我们不能再操作主框架窗口,程序中的tf.setText("https://www.wendangku.net/doc/6017744684.html,")语句没有被执行,这条语句在当对话框关闭后才被执行,这说明dlg.setVisible(true)语句直到对话框关闭后才能返回。当我们用非模态方式显示对话框后,程序中的tf.setText("https://www.wendangku.net/doc/6017744684.html,")语句立即被执行,我们也能操作主框架窗口(譬如对文本框进行输入),这说明dlg.setVisible(true)语句没有等到对话框关闭就立即返回了。
Dialog类下面有一种用于文件存取对话框的子类,这就是FileDialog类,FileDialog 类能够产生标准的文件于文件存取对话框,如图8.17所示:
图8.17
它具有Dialog的一切特征。由于安全性限制,它不能在Applet中使用,只有在Application中才能使用。它典型的构造方法如下:
public FileDialog(Frame parent, String title, int mode);
这种方法中,前两个参数的作用和Dialog一样,第三个参数有两个值,分别是FileDialog.LOAD或FileDialog.SAVE,这个参数用来确定产生的是读文件对话框还是写文件对话框。
8.5.9 Panel类
Panel可作为容器容纳其它组件,但不能独立存在,必须被添加到其它容器中(如Window 或Applet)。Panel是一个空白容器类,提供容纳组件的空间,通常用于集成其他的若干组件,使这些组件形成一个有机的整体,再增加到别的容器上。
8.5.10 ScrollPane类
我们有时候需要在一个较小的容器窗口中,显示较大的子部件,这时就需要用到ScrollPane类。ScrollPane也是一种容器,不能单独使用,通过滚动窗口可以利用滚动条查看大面积区域。ScrollPane中只能放置一个组件,无布局管理器。我们要将多个组件添加到ScrollPane上,只能先将多个组件嵌套在一个Panel容器中,然后将这个Panel作为一个组件放置到ScrollPane上。下面是使用ScrollPane的一个例子程序。
程序清单:TestPane.java
import java.awt.*;
import java.awt.event.*;
public class TestPane
{
TestPane()
{
Frame f=new Frame("TestDialog");
ScrollPane sp = new ScrollPane();
TextArea ta = new TextArea("",10,50,TextArea.SCROLLBARS_NONE);
sp.add(ta);
f.add(sp);
f.setSize(200,200);
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public static void main(String[] args)
{
new TestPane();
}
}
由于有各种各样的组件,我们很难在这里一一介绍,其实读者只要掌握了其中几个具有典型意义的组件,其他的组件都可以在需要时临时查阅文档资料,很多东西是你自己一看都能够明白的。
8.6 布局管理器
8.6.1 了解布局管理
为了理解布局管理器,我们先来看一个程序的运行结果。
程序清单:TestLayout.java
import java.awt.*;
public class TestLayout
{
public static void main(String [] args)
{
Frame f=new Frame("布局管理器");
f.add(new Button("第一个按钮"));
f.add(new Button("第二个按钮"));
f.setSize(300,300);
f.setVisible(true);
}
}
程序运行后,显示效果如图8.18所示:
图8.18
我们只看到了第二个按钮,为什么无法显示第一个按钮呢?在AWT中,每个组件在容器中都应该有一个具体的位置和大小,我们想在容器中排列若干组件时,会很难确定它们的大小和位置。为了简化编程者对容器上的组件的布局控制,一个容器内的所有组件的显示位置可以由一个“布局管理器”自动管理,我们可以为容器指定不同的布局管理器。在不同的布局管理器下,同一个组件将会有不同的显示效果,并且我们不能完全按自己的意愿设置组件的大小和位置了。
为了使我们生成的图形用户界面具有良好的平台无关性,Java语言中,提供了布局管理器这个工具来管理组件在容器中的布局,而不使用直接设置组件位置和大小的方式。每个容器都有一个布局管理器,当容器需要对某个组件进行定位或判断其大小尺寸时,就会调用其对应的布局管理器。
8.6.2 BorderLayout
BorderLayout将容器划分为东、南、西、北、中五个区域,如图8.19所示。
图8.19
我们将组件添加到容器时,需要指定组件放置的区域。当改变容器大小时,北方和南方的组件只改变宽度,东方和西方的组件只改变高度,而中间组件宽度和高度都会改变。在BorderLayout布局管理下,这个管理器允许最多放置五个组件,如果我们想在窗口上放置更多的组件,可以将若干组件添加到一个Panel上,然后将这个Panel作为一个组件放置到窗口上。当容器上放置的组件少于五个,没有放置组件的区域将被相邻的区域占用。对Frame 和Dialog来说,默认的布局管理器就是BorderLayout布局管理器。在上面的程序里,没有指定布局管理器,默认的BorderLayout布局管理器就把两个组件都放在中间了,所以实际上我们看到的是第二个按钮覆盖在第一个按钮上的效果。我们要想将上面例子中的第一个按钮放置在窗口上的北面,而第二个按钮放置在窗口的中间,程序修改如下:程序清单:TestBorderLayout.java
import java.awt.*;
public class TestBorderLayout
{
public static void main(String [] args)
{
Frame f=new Frame("布局管理器");
f.add(new Button("第一个按钮"),"North");
f.add(new Button("第二个按钮"));
f.setSize(300,300);
f.setVisible(true);
}
}
程序运行的效果如图8.20所示:
图8.20
可见,如果我们在使用Container.add方法,没有指定位置参数时,AWT会用“Center”作为这个组件的放置位置。注意,位置参数的字符串的书写是非常严格的,不能有任何大小写问题,必须是大写。对于这个问题,作者又要发一些牢骚了,真不明白Java的设计人员当初怎么想的,在这些鸡毛蒜皮的小问题上都不让我们轻松,非要我们浪费一些时间去处理这些死板的细节。我们要是将“North”随手写成了“north”,也完全能够表达我们的意图啊,这对SUN公司的Java设计人员并未增加什么处理难度!读者以后在编写软件时,应多向Microsoft公司学习,在不增加自己多大的编程难度的情况下,尽量为用户提供方便和宽容,这反过来也为自己增加了用户的好感和信赖。
8.6.3 FlowLayout
FlowLayout是一个简单的布局风格,组件从左到右,从上到下依此排列。如果一个组件在本行放不下,就自动换到下一行的开始。FlowLayout是Panel和applet的默认布局管理器。调用Container.setLayout方法,就可以改变容器的布局管理器,下面是使用FlowLayout的例子程序。
程序清单: TestFlowLayout.java
import java.awt.*;
public class TestFlowLayout
{
public static void main(String [] args)
{
Frame f=new Frame(“布局管理器”);
f.setLayout(new FlowLayout());
f.add(new Button(“第一个按钮”),”North”);
f.add(new Button(“第二个按钮”));
f.add(new Button(“第三个按钮”),”South”);
f.add(n ew Button(“第四个按钮”));
f.setSize(300,300);
f.setVisible(true);
}
}
在FlowLayout布局中,Java将忽略我们在Container.add方法中指定的位置参数,如上面用黑体显示的程序行。
当容器窗口大小改变时,组件的位置可能会发生变化,但组件的尺寸不变,如图8.21所示,当窗口变大后,第二行的组件排列到了第一行上。
图8.21
8.6.4 GridLayout
GridLayout将容器划分成若干行列的网格。在容器上添加组件时,它们会按从左到右、从上到下的顺序在网格中排列。在GridLayout的构造方法里,我们需要指定希望将容器划分成的网格的行、列数。GridLayout布局管理器总是忽略组件的最佳大小,所有单元的宽度是相同的,是根据单元数对可用宽度进行平分而定的。同样地,所有单元的高度是相同的,是根据行数对可用高度进行平分而定的。其效果如图8.22所示。
图8.22
8.6.5 CardLayout
C ardLayout布局管理器能够实现将多个组件放在同一容器区域内的交替显示,相当于多张卡片摞在一起,在任何时候都只有最上面的一个可见。CardLayout提供了几个方法,可以显示特定的卡片,也可以按先后顺序依此显示,还可以直接定位到第一张或最后一张。如图8.23所示是我们要讲的例子程序界面。
图8.23
只有一个布局管理器来实现上面组件布局是相当的困难的,所以下面的例子联合了更多的布局类型。如果我们创建两个Panel对象,每个Panel上都能拥有一个布局管理器,在左边的Panel上使用GridLayout放置三个按钮,在右边的Panel上使用CardLayout来放置卡片,最后在窗口上使用BorderLayout放置这两个面板。CardLayout容器中带有5张卡片(用5个按钮模拟),按下prev按钮,依次向前显示,按下next按钮,依次向后显示,按下three 按钮,显示第三张卡片。下面是程序代码:
程序清单:TestCardLayout.java
import java.awt.*;
import java.awt.event.*;
public class TestCardLayout
{
CardLayout cl = new CardLayout();
Panel plCenter = new Panel();
public static void main(String [] args)
{
new TestCardLayout().init();
}
public void init()
{
Frame f=new Frame("布局管理器");
Panel plWest = new Panel();
f.add(plWest,"West");
f.add(plCenter);
plWest.setLayout(new GridLayout(3,1));
Button btnPrev = new Button("prev");
plWest.add(btnPrev);
Button btnNext = new Button("next");
plWest.add(btnNext);
Button btnThree = new Button("three");
plWest.add(btnThree);
plCenter.setLayout(cl);
plCenter.add(new Button("One"),"1");
plCenter.add(new Button("two"),"2");
plCenter.add(new Button("three"),"3");
plCenter.add(new Button("four"),"4");
plCenter.add(new Button("five"),"5");
class MyActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(e.getActionCommand().equals("prev"))
cl.previous(plCenter);
else if(e.getActionCommand().equals("next"))
cl.next(plCenter);
else if(e.getActionCommand().equals("three"))
cl.show(plCenter,"3");
}
}
MyActionListener ma = new MyActionListener();
btnPrev.addActionListener(ma);
btnNext.addActionListener(ma);
btnThree.addActionListener(ma);
f.setSize(300,300);
f.setVisible(true);
}
}
8.6.6 GridBagLayout
前面讲到的布局管理器的功能还是相当有限的,只能满足我们一些简单的需求。在复杂的布局要求下,我们需要使用GridBagLayout布局管理器。GridBagLayout有布局管理器之王的说法,其功能非常强大,使用时也比较复杂,读者可以在JDK文档中了解到其详细说明及例子程序,考虑到一般的读者很少会使用到这种布局管理器,并且在Swing中我们可以有