Java AWT开发教程学习2
4.卡片布局
卡片布局(Card Layout)把每个组件看作一张卡片,好像一副扑克牌,它们叠在一起,每次只有最外面的一个组件可以被看到。
package sample;
imp
imp
public class MyCardLayout {
public static void main(String args[]) {
new MyCardLayout().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 = 1; i <= 5; ++i) {
Button b = new Button(“Button ” + i);
b.setSize(100, 25);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
CardLayout cl = (CardLayout)f1.getLayout();
cl.next(f1);
}
} );
f.add(b, “button” + i);
}
f.setVisible(true);
}
}
程序运行结果见图10-6。
图10-6 卡片布局(Card Layout)
单击按钮Button1后,显示下一个按钮Button2,依此类推。
5.网格包布局
网格包(GridBag)布局是基于网格布局之上的一种改进。和基本的网格布局不同的是,一个组件可以跨越一个或多个网格,这样一来增加了布局的灵活性。为了处理网格的跨越性,我们可以使用GridBagConstraints类。有兴趣的读者可以参考Java API来了解它。
package sample;
imp
imp
imp
public class MyGridBagLayout extends Panel {
protected void makebutton(String name,
GridBagLayout gridbag,
GridBagConstraints c) {
Button button = new Button(name);
gridbag.setConstraints(button, c);
add(button);
}
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(“Button001″, gridbag, c);
makebutton(“Button2″, gridbag, c);
makebutton(“Button3″, gridbag, c);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
makebutton(“Button4″, gridbag, c);
c.weightx = 0.0; //reset to the default
makebutton(“Button5″, gridbag, c); //another row
c.gridwidth = 2; //GridBagConstraints.RELATIVE; //next-to-last in row
makebutton(“Button6″, gridbag, c);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
makebutton(“Button007″, gridbag, c);
c.gridwidth = 1; //reset to the default
c.gridheight = 2;
c.weighty = 1.0;
makebutton(“Button8″, gridbag, c);
c.weighty = 1.0; //reset to the default
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.gridheight = 1; //reset to the default
makebutton(“Button9″, gridbag, c);
makebutton(“Button10″, gridbag, c);
setSize(300, 100);
}
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);
}
});
MyGridBagLayout gb = new MyGridBagLayout();
gb.go();
f.add(“Center”, gb);
f.pack();
f.setVisible(true);
}
}
程序运行结果见图10-7。
图10-7 网格包(GridBag)布局
2.3 AWT组件库
本节从应用的角度进一步介绍AWT的一些组件,目的是使大家加深对AWT的理解,掌握如何用各种组件构造图形化用户界面,学会控制组件的颜色和字体等属性。在介绍组件过程中,我们提前引入了事件处理的一些相关内容(之后会详细展开)。
下面是一些常用组件的介绍。
1.按钮(Button)
按钮是最常用的一个组件,其构造方法如下:
Button b = new Button(“Quit”);
当按钮被点击后,会产生ActionEvent事件,由ActionListener接口进行监听和处理事件。
ActionEvent的对象调用getActionCommand()方法可以得到按钮的标识名,默认按钮名为label。用setActionCommand()可以为按钮设置组件标识符。
2.标签
标签是一种放到面板上的静止的正文。其构造方法如下:
Label label1 = new Label(“你好!”)
下面是一个标签的例子。
imp
imp
public class LabelTest extends Applet {
public void init() {
setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
Label label1 = new Label(“你好!”);
Label label2 = new Label(“欢迎!”);
add(label1);
add(label2);
}
……
}
3.复选框(Checkbox)
复选框提供简单的“on/off”开关,旁边显示文本标签。
其主要方法如下:
setLayout(new GridLayout(3,1));
add(new Checkbox(“on
add(new Checkbox(“two”));
add(new Checkbox(“three”));
复选框用ItemListener来监听ItemEvent事件,当复选框状态改变时,用getStateChange()获取当前状态,使用getItem()获得被修改复选框的字符串对象。
4.复选框组(CheckboxGroup)
使用复选框组,可以实现单选框的功能,也就是说,只能选择其中的一项。方法如下:
setLayout(new GridLayout(3, 1));
CheckboxGroup cbg = new CheckboxGroup();
add(new Checkbox(“on
add(new Checkbox(“two”, cbg, false));
add(new Checkbox(“three”, cbg, false));
5.下拉式菜单(Choice)
下拉式菜单每次只能选择其中的一项,它能够节省显示空间,适用于大量选项。
Choice colorChooser=new Choice();
colorChooser.add(“Green”);
colorChooser.add(“Red”);
colorChooser.add(“Blue”);
Choice用ItemListener接口来进行监听。
6.画布(Canvas)
一个应用程序必须继承Canvas类才能获得有用的功能,比如创建一个自定义组件。如果想在画布上完成一些图形处理,则Canvas类中的paint()方法必须被重写。
Canvas组件监听各种鼠标、键盘事件。当在Canvas组件中输入字符时,必须先调用requestFocus()方法。
创建画布的实例如下:
imp
imp
public class CanvasGUI extends Applet {
. . .
MyCanvas doodle;
. . .
public void init() {
. . .
//建立我们的画布
doodle = new MyCanvas();
doodle.reshape(0,0,100,100);
leftPanel.add(“Center”,doodle);
. . .
}
}
class MyCanvas extends Canvas {
public void paint(Graphics g) {
g.drawRect(0, 0, 99, 99);
g.drawString(“Canvas”,15,40);
}
}
7.单行文本输入区(TextField)
单行文本输入区也叫做文本域,一般用来让用户输入像姓名、信用卡号这样的信息,它是一个能够接收用户的键盘输入的小块区域。
单行文本输入区构造方法有4种类型供选择:空的、空的并且具有指定长度、带有初始文本内容的和带有初始文本内容并具有指定长度的。下面是生成这4种文本域的代码。
TextField tf1, tf2, tf3, tf4;
//空的文本域
tf1 = new TextField();
//长度为20的空的文本域
tf2 = new TextField(20);
//带有初始文本内容的文本域
tf3 = new TextField(“你好”);
//带有初始文本内容并具有指定长度的文本域
tf4 = new TextField(“你好”, 30);
单行文本输入区只能显示一行,当按下回车键时,会发生ActionEvent事件,可以通过ActionListener中的actionPerformed()方法对事件进行相应处理。可以使用setEditable(boolean)方法设置为只读属性。
8.文本输入区(TextArea)
TextArea可以显示多行多列的文本。与文本域类似,创建文本区时也有4种类型供选择,但如果指定文本区的大小,必须同时指定行数和列数。
TextArea ta1, ta2;
//一个空的文本区
ta1 = new TextArea();
//一个带有初始内容、大小为5×40的文本区
ta2 = new TextArea(“你好!”, 5, 40);
可以用成员方法setEditable()来决定用户是否可以对文本区的内容进行编辑。
//使文本区为只读的
ta2.setEditable(false)
我们可以用成员方法getText()来获得文本区的当前内容。在TextArea中可以显示水平或垂直的滚动条。要判断文本是否输入完毕,可以在TextArea旁边设置一个按钮,通过按钮点击产生的ActionEvent事件对输入的文本进行处理。
9.列表(List)
列表框使用户易于操作大量的选项。创建列表框的方法和下拉式菜单有些相似。列表框的所有条目都是可见的,如果选项很多,超出了列表框可见区的范围,则列表框的旁边将会有一个滚动条。列表框中提供了多个文本选项,可以浏览多项。
List lst=new List(4,false); //两个参数分别表示显示的行数,是否允许多选
lst.add(“Venus”);
lst.add(“Earth”);
lst.add(“Moon”);
lst.add(“Mars”);
cnt.add(lst);
10.滚动条
在某些程序中,需要调整线性的值,这时就需要滚动条。滚动条提供了易于操作的值的范围或区的范围。
(1)创建滚动条
当创建一个滚动条时,必须指定它的方向、初始值、滑块的大小、最小值和最大值。
public Scrollbar(int orientation, int initialValue, int sizeOfSlider, int minValue, int maxValue);
下面是一个例子。
Scrollbar redSlider;
public void init() {
redSlider = new Scrollbar(Scrollbar.VERTICAL,0,1,0,255);
add(redSlider);
}
(2)滚动条事件
和其他接口元件一样,滚动条产生一个可以控制的事件;但和其他事件不同,你必须直接使用成员方法handleEvent(),而不能使用成员方法act
(3)滚动条的值的显示
如果想显示滑块所在位置的值,则需要添加一个自己的文本域。下面是一个例子。
imp
imp
public class RedSliderTest extends Applet {
Scrollbar redslider;
TextField redvalue;
Label redlabel;
public void init( ) {
setLayout(new GridLayout(1, 3));
redslider = new Scrollbar(Scrollbar.HORIZONTAL,0,1,0,255);
redvalue = new TextField(“0″,5);
redvalue.setEditable(false);
redlable = new Label(“Red (0-255)”);
add(redlabel);
add(redslider);
add(redvalue);
}
public boolean handleEvent(Event e) {
if (e.target instanceof Scrollbar) {
redvalue.setText(Integer.toString(((Scrollbar)e.target).getValue()));
return true;
}
return super.handleEvent(e);
}
public boolean act
System.out.println(“Event” + arg);
return true;
}
}
11.对话框(Dialog)
对话框是Window类的子类,是一个容器类,属于特殊组件。对话框和一般窗口的区别在于它依赖于其他窗口。对话框分为非模式(non-modal)和模式(modal)两种。
12.文件对话框(Filedialog)
当用户想打开或存储文件时,使用文件对话框进行操作。主要代码如下:
FileDialog d=new FileDialog(ParentFr,”FileDialog”);
d.setVisible(true);
String filename=d.getFile();
13.菜单(Menu)
菜单的开发相对复杂,我们需要使用3个类:Menu、MenuBar和MenuItem。它们的层次结构如图10-8所示。
图10-8 菜单的层次结构
(1)MenuBar
我们无法直接将菜单添加到容器的某一位置,也无法使用布局管理器对其加以控制。菜单只能被添加到菜单容器(MenuBar)中。MenuBar会被添加到Frame对象中,作为整个菜单树的根基。
Frame fr = new Frame(“MenuBar”);
MenuBar mb = new MenuBar();
fr.setMenuBar(mb);
fr.setSize(150,100);
fr.setVisible(true);
(2)Menu
菜单可以被添加到MenuBar中或其他Menu中。
Frame fr = new Frame(“MenuBar”);
MenuBar mb = new MenuBar();
fr.setMenuBar(mb);
Menu m1 = new Menu(“File”);
Menu m2 = new Menu(“Edit”);
Menu m3 = new Menu(“Help”);
mb.add(m1);
mb.add(m2);
mb.setHelpMenu(m3);
fr.setSize(200,200);
fr.setVisible(true);
(3)MenuItem
MenuItem是菜单树中的“叶子节点”。MenuItem通常被添加到一个Menu中。对于MenuItem对象可以添加ActionListener,使其能够完成相应的操作。
Menu m1 = new Menu(“File”);
MenuItem mi1 = new MenuItem(“Save”);
MenuItem mi2 = new MenuItem(“Load”);
MenuItem mi3 = new MenuItem(“Quit”);
m1.add(mi1);
m1.add(mi2);
m1.addSeparator();
m1.add(mi3);
MenuBar和Menu都没有必要注册监听器,只需要对MenuItem添加监听器ActionListener,完成相应的操作。
我们看一个完整的Menu实例。
/**
* 演示Menu、MenuBar和MenuItem的使用
*/
package sample;
imp
imp
class MenuTest extends Frame {
PopupMenu pop;
public MenuTest() {
super(“Golf Caddy”);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
setVisible(false);
dispose();
System.exit(0);
}
});
this.setSize(300,300);
//this.setLayout(new FlowLayout());
this.add(new Label(“Choose club.”), 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(“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(“1 W”);
pop.add(“3 W”);
pop.add(“5 W”);
final TextArea p = new TextArea(100, 100);
p.setBounds(0,0,100,200);
p.setBackground(Color.green);
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 MenuTest().setVisible(true);
}
}
程序运行结果见图10-9。
图10-9 菜单
3.1 AWT事件处理基本概念
上面我们讲解了如何放置各种组件,使图形界面更加丰富多彩,但是还不能响应用户的任何操作。若使图形界面能够接收用户的操作,我们就必须给各个组件加上事件处理机制。在事件处理的过程中,主要涉及3类对象。
— Event(事件):用户对组件的一个操作,称之为一个事件,以类的形式出现,例如,键盘操作对应的事件类是KeyEvent。
— Event Source(事件源):事件发生的场所,通常就是各个组件,例如按钮Button。
— Event Handler(事件处理者):接收事件对象并对其进行处理的对象事件处理器,通常就是某个Java类中负责处理事件的成员方法。
例如,如果用户用鼠标单击了按钮对象button,则该按钮button就是事件源,而Java运行时系统会生成ActionEvent类的对象actionEvent,该对象中描述了单击事件发生时的一些信息。然后,事件处理者对象将接收由Java运行时系统传递过来的事件对象actionEvent,并进行相应的处理。事件处理模型如图10-10所示。
图10-10 事件处理模型
由于同一个事件源上可能发生多种事件,因此,Java采取了授权模型(Delegation Model),事件源可以把在其自身所有可能发生的事件分别授权给不同的事件处理者来处理。比如,在Canvas对象上既可能发生鼠标事件,也可能发生键盘事件,该Canvas对象就可以授权给事件处理者1来处理鼠标事件,同时授权给事件处理者2来处理键盘事件。有时也将事件处理者称为监听器,主要原因也在于监听器时刻监听着事件源上所有发生的事件类型,一旦该事件类型与自己所负责处理的事件类型一致,就马上进行处理。授权模型把事件的处理委托给外部的处理实体进行处理,实现了将事件源和监听器分开的机制。事件处理者(监听器)通常是一个类,该类如果能够处理某种类型的事件,就必须实现与该事件类型相对的接口。例如,一个ButtonHandler类之所以能够处理ActionEvent事件,原因在于它实现了与ActionEvent事件对应的接口ActionListener。每个事件类都有一个与之相对应的接口。