Java编程思想第4版[中文版](PDF格式)-第107部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
细地考虑。
13。15。1 菜单
直接在程序片中安放一个菜单是不可能的(Java 1。0;Java1。1 和 Swing 库不允许),因为它们是针对应用程
序的。继续,如果您不相信我并且确定在程序片中可以合理地拥有菜单,那么您可以去试验一下。程序片中
没有 setMenuBar()方法,而这种方法是附在菜单中的(我们会看到它可以合理地在程序片产生一个帧,并且
帧包含菜单)。
有四种不同类型的Menuponent (菜单组件),所有的菜单组件起源于抽象类:菜单条(我们可以在一个
事件帧里拥有一个菜单条),菜单去支配一个单独的下拉菜单或者子菜单、菜单项来说明菜单里一个单个的
元素,以及起源于MenuItem;产生检查标志(checkmark)去显示菜单项是否被选择的CheckBoxMenuItem。
不同的系统使用不同的资源,对Java 和 AWT 而言,我们必须在源代码中手工汇编所有的菜单。
//: Menu1。java
// Menus work only with Frames。
// Shows submenus; checkbox menu items
// and swapping menus。
import java。awt。*;
public class Menu1 extends Frame {
String'' flavors = { 〃Chocolate〃; 〃Strawberry〃;
〃Vanilla Fudge Swirl〃; 〃Mint Chip〃;
〃Mocha Almond Fudge〃; 〃Rum Raisin〃;
〃Praline Cream〃; 〃Mud Pie〃 };
TextField t = new TextField(〃No flavor〃; 30);
MenuBar mb1 = new MenuBar();
Menu f = new Menu(〃File〃);
401
…………………………………………………………Page 403……………………………………………………………
Menu m = new Menu(〃Flavors〃);
Menu s = new Menu(〃Safety〃);
// Alternative approach:
CheckboxMenuItem'' safety = {
new CheckboxMenuItem(〃Guard〃);
new CheckboxMenuItem(〃Hide〃)
};
MenuItem'' file = {
new MenuItem(〃Open〃);
new MenuItem(〃Exit〃)
};
// A second menu bar to swap to:
MenuBar mb2 = new MenuBar();
Menu fooBar = new Menu(〃fooBar〃);
MenuItem'' other = {
new MenuItem(〃Foo〃);
new MenuItem(〃Bar〃);
new MenuItem(〃Baz〃);
};
Button b = new Button(〃Swap Menus〃);
public Menu1() {
for(int i = 0; i 《 flavors。length; i++) {
m。add(new MenuItem(flavors'i'));
// Add separators at intervals:
if((i+1) % 3 == 0)
m。addSeparator();
}
for(int i = 0; i 《 safety。length; i++)
s。add(safety'i');
f。add(s);
for(int i = 0; i 《 file。length; i++)
f。add(file'i');
mb1。add(f);
mb1。add(m);
setMenuBar(mb1);
t。setEditable(false);
add(〃Center〃; t);
// Set up the system for swapping menus:
add(〃North〃; b);
for(int i = 0; i 《 other。length; i++)
fooBar。add(other'i');
mb2。add(fooBar);
}
public boolean handleEvent(Event evt) {
if(evt。id == Event。WINDOW_DESTROY)
System。exit(0);
else
return super。handleEvent(evt);
return true;
}
public boolean action(Event evt; Object arg) {
if(evt。target。equals(b)) {
402
…………………………………………………………Page 404……………………………………………………………
MenuBar m = getMenuBar();
if(m == mb1) setMenuBar(mb2);
else if (m == mb2) setMenuBar(mb1);
}
else if(evt。target instanceof MenuItem) {
if(arg。equals(〃Open〃)) {
String s = t。getText();
boolean chosen = false;
for(int i = 0; i 《 flavors。length; i++)
if(s。equals(flavors'i')) chosen = true;
if(!chosen)
t。setText(〃Choose a flavor first!〃);
else
t。setText(〃Opening 〃+ s +〃。 Mmm; mm!〃);
}
else if(evt。target。equals(file'1'))
System。exit(0);
// CheckboxMenuItems cannot use String
// matching; you must match the target:
else if(evt。target。equals(safety'0'))
t。setText(〃Guard the Ice Cream! 〃 +
〃Guarding is 〃 + safety'0'。getState());
else if(evt。target。equals(safety'1'))
t。setText(〃Hide the Ice Cream! 〃 +
〃Is it cold? 〃 + safety'1'。getState());
else
t。setText(arg。toString());
}
else
return super。action(evt; arg);
return true;
}
public static void main(String'' args) {
Menu1 f = new Menu1();
f。resize(300;200);
f。show();
}
} ///:~
在这个程序中,我避免了为每个菜单编写典型的冗长的add()列表调用,因为那看起来像许多的无用的标
志。取而代之的是,我安放菜单项到数组中,然后在一个 for 的循环中通过每个数组调用add()简单地跳
过。这样的话,增加和减少菜单项变得没那么讨厌了。
作为一个可选择的方法(我发现这很难令我满意,因为它需要更多的分配)CheckboxMenuItems 在数组的句
柄中被创建是被称为安全创建;这对数组文件和其它的文件而言是真正的安全。
程序中创建了不是一个而是二个的菜单条来证明菜单条在程序运行时能被交换激活。我们可以看到菜单条怎
样组成菜单,每个菜单怎样组成菜单项(MenuItems),chenkboxMenuItems 或者其它的菜单(产生子菜
单)。当菜单组合后,可以用setMenuBar()方法安装到现在的程序中。值得注意的是当按钮被压下时,它将
检查当前的菜单安装使用getMenuBar(),然后安放其它的菜单条在它的位置上。
当测试是“open”(即开始)时,注意拼写和大写,如果开始时没有对象,Java 发出no error (没有错误)
的信号。这种字符串比较是一个明显的程序设计错误源。
校验和非校验的菜单项自动地运行,与之相关的CheckBoxMenuItems 着实令人吃惊,这是因为一些原因它们
不允许字符串匹配。(这似乎是自相矛盾的,尽管字符串匹配并不是一种很好的办法。)因此,我们可以匹
403
…………………………………………………………Page 405……………………………………………………………
配一个目标对象而不是它们的标签。当演示时,getState()方法用来显示状态。我们同样可以用setState()
改变CheckboxMenuItem 的状态。
我们可能会认为一个菜单可以合理地置入超过一个的菜单条中。这看似合理,因为所有我们忽略的菜单条的
add()方法都是一个句柄。然而,如果我们试图这样做,这个结果将会变得非常的别扭,而远非我们所希望得
到的结果。(很难知道这是一个编程中的错误或者说是他们试图使它以这种方法去运行所产生的。)这个例
子同样向我们展示了为什么我们需要建立一个应用程序以替代程序片。(这是因为应用程序能支持菜单,而
程序片是不能直接使用菜单的。)我们从帧处继承代替从程序片处继承。另外,我们为类建一个构建器以取
代 init()安装事件。最后,我们创建一个main()方法并且在我们建的新型对象里,调整它的大小,然后调用
show()。它与程序片只在很小的地方有不同之处,然而这时它已经是一个独立的设置窗口应用程序并且我们
可以使用菜单。
13。15。2 对话框
对话框是一个从其它窗口弹出的窗口。它的目的是处理一些特殊的争议和它们的细节而不使原来的窗口陷入
混乱之中。对话框大量在设置窗口的编程环境中使用,但就像前面提到的一样,鲜于在程序片中使用。
我们需要从对话类处继承以创建其它类型的窗口、像帧一样的对话框。和窗框不同,对话框不能拥有菜单条
也不能改变光标,但除此之外它们十分的相似。一个对话框拥有布局管理器(默认的是BorderLayout 布局管
理器)和过载action()等等,或用 handleEvent() 去处理事件。我们会注意到handleEvent() 的一个重要差
异:当WINDOW_DESTORY 事件发生时,我们并不希望关闭正在运行的应用程序!
相反,我们可以使用对话窗口通过调用 dispace()释放资源。在下面的例子中,对话框是由定义在那儿作为
类的ToeButton 的特殊按钮组成的网格构成的(利用GridLayout 布局管理器)。ToeButton 按钮围绕它自已
画了一个帧,并且依赖它的状态:在空的中的“X”或者“O”。它从空白开始,然后依靠使用者的选择,
转换成“X”或“O”。但是,当我们单击在按钮上时,它会在“X”和“O”之间来回交换。(这产生了
一种类似填字游戏的感觉,当然比它更令人讨厌。)另外,这个对话框可以被设置为在主应用程序窗口中为
很多的行和列变更号码。
//: ToeTest。java
// Demonstration of dialog boxes
// and creating your own ponents
import java。awt。*;
class ToeButton extends Canvas {
int state = ToeDialog。BLANK;
ToeDialog parent;
ToeButton(ToeDialog parent) {
this。parent = parent;
}
public void paint(Graphics g) {
int x1 = 0;
int y1 = 0;
int x2 = size()。width 1;
int y2 = size()。height 1;
g。drawRect(x1; y1; x2; y2);
x1 = x2/4;
y1 = y2/4;
int wide = x2/2;
int high = y2/2;
if(state == ToeDialog。XX) {
g。drawLine(x1; y1; x1 + wide; y1 + high);
g。drawLine(x1; y1 + high; x1 + wide; y1);
}
if(state == ToeDialog。OO) {
g。drawOval(x1; y1; x1+wide/2; y1+high/2);
404
…………………………………………………………Page 406……………………………………………………………
}
}
public boolean
mouseDown(Event evt; int x; int y) {
if(state == ToeDialog。BLANK) {
state = parent。turn;
parent。turn= (parent。turn == ToeDialog。XX ?
ToeDialog。OO : ToeDialog。XX);
}
else
state = (state == ToeDialog。XX ?
ToeDialog。OO : ToeDialog。XX);
repaint();
return true;
}
}
class ToeDialog extends Dialog {
// w = number of cells wide
// h = number of cells high
static final int BLANK = 0;
static final int XX = 1;
static final int OO = 2;
int turn = XX; // Start with x's turn
public ToeDialog(Frame parent; int w; int h) {
super(parent; 〃The game itself〃; false);
setLayout(new GridLayout(w; h));
for(int i = 0; i 《 w * h; i++)
add(new ToeButton(this));
resize(w * 50; h * 50);
}
public boolean handleEvent(Event evt) {
if(evt。id == Event。WINDOW_DESTROY)
dispose();
else
return super。handleEvent(evt);
return true;
}
}
public class ToeTest extends Frame {
TextField rows = new TextField(〃3〃);
TextField cols = new TextField(〃3〃);
public ToeTest() {
setTitle(〃Toe Test〃);
Panel p = new Panel();
p。setLayout(new GridLayout(2;2));
p。add(new Label(〃Rows〃; Label。CENTER));
p。add(rows);
p。add(