分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
53.java编程思想——创建窗口和程序片 新型AWT
在Java 1.1 中一个显著的改变就是完善了新AWT 的创新。大多数的改变围绕在Java 1.1 中使用的新事件模型:老的事件模型是糟糕的、笨拙的、非面向对象的,而新的事件模型可能是我所见过的最优秀的。难以理解一个如此糟糕的(老的AWT)和一个如此优秀的(新的事件模型)程序语言居然出自同一个集团之手。新的考虑事件的方法看来中止了,因此争议不再变成障碍,从而轻易进入我们的意识里;相反,它是一个帮助我们设计系统的工具。它同样是Java Beans 的精华,我们会在本章后面部分进入讲述。
新的方法设计对象做为“事件源”和“事件接收器”以代替老AWT 的非面向对象串联的条件语句。正象我们将看到的内部类的用途是集成面向对象的原始状态的新事件。另外,事件现在被描绘为在一个类体系以取代单一的类并且我们可以创建自己的事件类型。
我们同样会发现,如果我们采用老的AWT 编程,Java 1.1 版会产生一些看起来不合理的名字转换。例如,setsize()改成resize()。当我们学习Java Beans 时这会变得更加的合理,因为Beans 使用一个独特的命名协议。名字必须被修改以在Beans 中产生新的标准AWT 组件。剪贴板操作在Java 1.1 版中也得到支持,尽管拖放操作“将在新版本中被支持”。我们可能访问桌面色彩组织,所以我们的Java 可以同其余桌面保持一致。可以利用弹出式菜单,并且为图像和图形作了改进。也同样支持鼠标操作。还有简单的为打印的API 以及简单地支持滚动。
1 新的事件模型
在新的事件模型的组件可以开始一个事件。每种类型的事件被一个个别的类所描绘。当事件开始后,它受理一个或更多事件指明“接收器”。因此,事件源和处理事件的地址可以被分离。每个事件接收器都是执行特定的接收器类型接口的类对象。因此作为一个程序开发者,我们所要做的是创建接收器对象并且在被激活事件的组件中进行注册。event-firing 组件调用一个addXXXListener()方法来完成注册,以描述XXX 事件类型接受。我们可以容易地了解到以addListened 名的方法通知我们任何的事件类型都可以被处理,如果我们试图接收事件我们会发现编译时我们的错误。Java Beans 同样使用这种addListener 名的方法去判断那一个程序可以运行。
我们所有的事件逻辑将装入到一个接收器类中。当我们创建一个接收器类时唯一的一点限制是必须执行专用的接口。我们可以创建一个全局接收器类,这种情况在内部类中有助于被很好地使用,不仅仅是因为它们提供了一个理论上的接收器类组到它们服务的UI 或业务逻辑类中,但因为(正像我们将会在本章后面看到的)事实是一个内部类维持一个句柄到它的父对象,提供了一个很好的通过类和子系统边界的调用方法。一个简单的例子将使这一切变得清晰明确。
2 代码
import java.awt.*;
import java.awt.event.*;// Must add this
import java.applet.*;
publicclass Button2New extends Applet {
Button b1 =new Button("Button 1"),b2 = new Button("Button 2");
publicvoidinit() {
b1.addActionListener(new B1());
b2.addActionListener(new B2());
add(b1);
add(b2);
}
class B1implementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
getAppletContext().showStatus("Button 1");
}
}
class B2implementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
getAppletContext().showStatus("Button 2");
}
}
/*
* The old way: public boolean action(Eventevt,Object arg) {
* if(evt.target.equals(b1))getAppletContext().showStatus("Button 1"); else
* if(evt.target.equals(b2))getAppletContext().showStatus("Button 2"); //
* Let the base class handle it: else 410return super.action(evt,arg);
* return true; // We've handled it here }
*/
} /// :~
3 执行
可比较两种方法,老的代码在左面作为注解。在init()方法里,只有一个改变就是增加了下面的两行:
b1.addActionListener(new B1());
b2.addActionListener(new B2());
按钮按下时,addActionListener()通知按钮对象被激活。B1 和B2 类都是执行接口ActionListener的内部类。这个接口包括一个单一的方法actionPerformed()(这意味着当事件激活时,这个动作将被执行)。注意actionPreformed()方法不是一个普通事件,说得更恰当些是一个特殊类型的事件,ActionEvent。如果我们想提取特殊ActionEvent 的信息,因此我们不需要故意去测试和下溯造型自变量。
对编程者来说一个最好的事便是actionPerformed()十分的简单易用。它是一个可以调用的方法。同老的action()方法比较,老的方法我们必须指出发生了什么和适当的动作,同样,我们会担心调用基础类action()的版本并且返回一个值去指明是否被处理。在新的事件模型中,我们知道所有事件测试推理自动进行,因此我们不必指出发生了什么;我们刚刚表示发生了什么,它就自动地完成了。如果我们还没有提出用新的方法覆盖老的方法,我们会很快提出。
4 事件和接收者类型
所有AWT 组件都被改变成包含addXXXListener()和removeXXXListener()方法,因此特定的接收器类型可从每个组件中增加和删除。我们会注意到“XXX”在每个场合中同样表示自变量的方法,例如,
addFooListener(FooListener fl)。。
事件,接收器接口及添加和删除方法 支持这个事件的组件一旦知道了一个特定的组件支持哪些事件,就不必再去寻找任何东西来响应那个事件。只需简单地:
(1) 取得事件类的名字,并删掉其中的“Event”字样。在剩下的部分加入“Listener”字样。这就是在我们的内部类里需要实现的接收器接口。
(2) 实现上面的接口,针对想要捕获的事件编写方法代码。例如,假设我们想捕获鼠标的移动,所以需要为MouseMotiionListener 接口的mouseMoved()方法编写代(当然还必须实现其他一些方法,但这里有捷径可循,马上就会讲到这个问题)。
(3) 为步骤2 中的接收器类创建一个对象。随自己的组件和方法完成对它的注册,方法是在接收器的名字里加入一个前缀“add”。比如addMouseMotionListener()。
4.1 用接收器适配器简化操作
注意到一些接收器接口只有唯一的一个方法。它们的执行是无轻重的,因为我们仅当需要书写特殊方法时才会执行它们。然而,接收器接口拥有多个方法,使用起来却不太友好。例如,我们必须一直运行某些事物,当我们创建一个应用程序时对帧提供一个WindowListener,以便当我们得到windowClosing()事件时可以调用System.exit(0)以退出应用程序。但因为WindowListener 是一个接口,我们必须执行其它所有的方法即使它们不运行任何事件。这真令人讨厌。
为了解决这个问题,每个拥有超过一个方法的接收器接口都可拥有适配器,它们的名我们可以在上面的表格中看到。每个适配器为每个接口方法提供默认的方法。(WindowAdapter 的默认方法不是windowClosing(),而是System.exit(0)方法。)此外我们所要做的就是从适配器处继承并过载唯一的需要变更的方法。例如,典型的WindowListener 我们会像下面这样的使用。
class MyWindowListener extendsWindowAdapter {
public voidwindowClosing(WindowEvent e) {
System.exit(0);
}
}
适配器的全部宗旨就是使接收器的创建变得更加简便。但所谓的“适配器”也有一个缺点,而且较难发觉。假定我们象上面那样写一个WindowAdapter :
class MyWindowListener extendsWindowAdapter {
public voidWindowClosing(WindowEvent e) {
System.exit(0);
}
}
表面上一切正常,但实际没有任何效果。每个事件的编译和运行都很正常——只是关闭窗口不会退出程序。
在方法的名字里:是WindowClosing(),而不是windowClosing()。大小写的一个简单失误就会造成一个崭新的方法。但是,这并非我们关闭窗口时调用的方法,所以当然没有任何效果。
4.2 用J a v a 1 . 1 A W T制作窗口和程序片
我们经常都需要创建一个类,使其既可作为一个窗口调用,亦可作为一个程序片调用。为做到这一点,只需为程序片简单地加入一个main()即可,令其在一个Frame(帧)里构建程序片的一个实例。作为一个简单的示例,下面让我们来看看如何对Button2New.java 作一番修改,使其能同时作为应用程序和程序片使用:
4.2.1 代码
import java.awt.*;
import java.awt.event.*;// Must add this
import java.applet.*;
publicclass Button2NewB extends Applet {
Button b1 =new Button("Button 1"),b2 = new Button("Button 2");
TextField t =new TextField(20);
publicvoidinit() {
b1.addActionListener(new B1());
b2.addActionListener(new B2());
add(b1);
add(b2);
add(t);
}
class B1implementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
t.setText("Button 1");
}
}
class B2implementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
t.setText("Button 2");
}
}
// To closethe application:
staticclassWL extendsWindowAdapter {
publicvoid windowClosing(WindowEvent e) {
System.exit(0);
}
}
// A main()for the application:
publicstaticvoidmain(String[]args){
Button2NewB applet =new Button2NewB();
Frame aFrame =new Frame("Button2NewB");
aFrame.addWindowListener(new WL());
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(300, 200);
applet.init();
applet.start();
aFrame.setVisible(true);
}
} /// :~
4.2.2 执行
内部类WL 和main()方法是加入程序片的唯一两个元素,程序片剩余的部分则原封未动。事实上,我们通常将WL 类和main()方法做一结小的改进复制和粘贴到我们自己的程序片里(请记住创建内部类时通常需要一个外部类来处理它,形成它静态地消除这个需要)。我们可以看到在main()方法里,程序片明确地初始化和开始,因为在这个例子里浏览器不能为我们有效地运行它。当然,这不会提供全部的浏览器调用stop()和destroy()的行为,但对大多数的情况而言它都是可接受的。如果它变成一个麻烦,我们可以:
(1) 使程序片句柄为一个静态类(以代替局部可变的main()),然后:
(2) 在我们调用System.exit()之前在WindowAdapter.windowClosing()中调用applet.stop()和applet.destroy()。
注意最后一行:
aFrame.setVisible(true);
这是Java 1.1 AWT 的一个改变。show()方法不再被支持,而setVisible(true)则取代了show()方法。当我们学习Java Beans 时,这些表面上易于改变的方法将会变得更加的合理。
这个例子同样被使用TextField 修改而不是显示到控制台或浏览器状态行上。在开发程序时有一个限制条件就是程序片和应用程序我们都必须根据它们的运行情况选择输入和输出结构。
这里展示了Java 1.1 AWT 的其它小的新功能。我们不再需要去使用有错误倾向的利用字符串指定BorderLayout 定位的方法。当我们增加一个元素到Java 1.1 版的BorderLayout 中时,我们可以这样写:
aFrame.add(applet,BorderLayout.CENTER);
我们对位置规定一个BorderLayout 的常数,以使它能在编译时被检验(而不是对老的结构悄悄地做不合适的事)。这是一个显著的改善。
4.3 将窗口接收器变成匿名类
任何一个接收器类都可作为一个匿名类执行,但这一直有个意外,那就是我们可能需要在其它场合使用它们的功能。但是,窗口接收器在这里仅作为关闭应用程序窗口来使用,因此我们可以安全地制造一个匿名类。
然后,main()中的下面这行代码:
aFrame.addWindowListener(newWL());
会变成:
aFrame.addWindowListener(
new WindowAdapter() {
public voidwindowClosing(WindowEvent e) {
System.exit(0);
}
});
这有一个优点就是它不需要其它的类名。我们必须对自己判断是否它使代码变得易于理解或者更难。匿名内部类将通常被使用在窗口接收器中。
4.4 将程序片封装到JAR 文件里
一个重要的JAR 应用就是完善程序片的装载。在Java 1.0 版中,人们倾向于试法将它们的代码填入到单个的程序片类里,因此客户只需要单个的服务器就可适合下载程序片代码。但这不仅使结果凌乱,难以阅读(当然维护也然)程序,但类文件一直不能压缩,因此下载从来没有快过。
JAR 文件将我们所有的被压缩的类文件打包到一个单个儿的文件中,再被浏览器下载。现在我们不需要创建一个糟糕的设计以最小化我们创建的类,并且用户将得到更快地下载速度。仔细想想上面的例子,这个例子看起来像Button2NewB,是一个单类,但事实上它包含三个内部类,因此共有四个。每当我们编译程序,我会用这行代码打包它到一个JAR 文件:
jar cf Button2NewB.jar *.class
这是假定只有一个类文件在当前目录中,其中之一来自Button2NewB.java(否则我们会得到特别的打包)。
现在我们可以创建一个使用新文件标签来指定JAR 文件的HTML 页,如下所示:
<head><title>Button2NewBExample Applet
</title></head>
<body><appletcode="Button2NewB.class"
archive="Button2NewB.jar"
width=200 height=150>
</applet>
</body>
与HTML 文件中的程序片标记有关的其他任何内容都保持不变。
4.5 以前的例子
为注意到一些利用新事件模型的例子和为学习程序从老到新事件模型改变的方法,下面的例子回到在利用事件模型来证明的一些争议。另外,每个程序包括程序片和应用程序现在都可以借助或不借助浏览器来运行。
4.5.1 代码2
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
publicclass TextNew extends Applet {
Button b1 =new Button("Get Text"),b2 = new Button("Set Text");
TextField t1 =new TextField(30), t2 = new TextField(30),t3 = new TextField(30);
String s =new String();
publicvoidinit() {
b1.addActionListener(new B1());
b2.addActionListener(new B2());
t1.addTextListener(new T1());
t1.addActionListener(new T1A());
t1.addKeyListener(new T1K());
add(b1);
add(b2);
add(t1);
add(t2);
add(t3);
}
class T1implementsTextListener {
publicvoid textValueChanged(TextEvent e) {
t2.setText(t1.getText());
}
}
class T1AimplementsActionListener {
privateint count = 0;
publicvoid actionPerformed(ActionEvent e) {
t3.setText("t1 Action Event " +count++);
}
}
class T1KextendsKeyAdapter {
publicvoid keyTyped(KeyEvent e) {
String ts =t1.getText();
if (e.getKeyChar() == KeyEvent.VK_BACK_SPACE) {
// Ensure it's not empty:
if (ts.length() > 0) {
ts =ts.substring(0, ts.length()- 1);
t1.setText(ts);
}
} else
t1.setText(t1.getText() + Character.toUpperCase(e.getKeyChar()));
t1.setCaretPosition(t1.getText().length());
// Stop regular character from appearing:
e.consume();
}
}
class B1implementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
s =t1.getSelectedText();
if (s.length() == 0)
s =t1.getText();
t1.setEditable(true);
}
}
class B2implementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
t1.setText("Inserted by Button 2: "+s);
t1.setEditable(false);
}
}
publicstaticvoidmain(String[]args){
TextNew applet =new TextNew();
Frame aFrame =new Frame("TextNew");
aFrame.addWindowListener(new WindowAdapter() {
publicvoid windowClosing(WindowEvent e) {
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(300, 200);
applet.init();
applet.start();
aFrame.setVisible(true);
}
} /// :~
4.5.2 执行
当TextField t1 的动作接收器被激活时,TextField t3 就是一个需要报告的场所。我们注意到仅当我们按下“enter”键时,动作接收器才会为“TextField”所激活。
TextField t1 附有几个接收器。T1 接收器从t1 复制所有文字到t2,强制所有字符串转换成大写。我们会发现这两个工作同是进行的,并且如果我们增加T1K 接收器后我们再增加T1 接收器,它就不那么重要:在文字字段内的所有的字符串将一直被强制变为大写。这看起来键盘事件一直在文字组件事件前被激活,并且如果我们需要保留t2 的字符串原来输入时的样子,我们就必须做一些特别的工作。
T1K 有着其它的一些有趣的活动。我们必须测试backspace(因为我们现在控制着每一个事件)并执行删除。
caret 必须被明确地设置到字段的结尾;否则它不会像我们希望的运行。最后,为了防止原来的字符串被默认的机制所处理,事件必须利用为事件对象而存在的consume()方法所“耗尽”。这会通知系统停止激活其余特殊事件的事件处理器。
这个例子同样无声地证明了设计内部类的带来的诸多优点。注意下面的内部类:
class T1 implements TextListener{
public voidtextValueChanged(TextEvent e) {
t2.setText(t1.getText());
}
}
t1 和t2 不属于T1 的一部分,并且到目前为止它们都是很容易理解的,没有任何的特殊限制。这是因为一个内部类的对象能自动地捕捉一个句柄到外部的创建它的对象那里,因此我们可以处理封装类对象的方法和内容。正像我们看到的,这十分方便。它也解决了“回调”的问题,不必为Java 加入任何令人恼火的“方法指针”特性。
4.5.3 代码3
Java 1.1 版中Text Area 最重要的改变就滚动条。对于TextArea 的构建器而言,我们可以立即控制TextArea 是否会拥有滚动条:水平的,垂直的,两者都有或者都没有。这个例子更正了前面Java 1.0 版TextArea1.java 程序片,演示了Java 1.1 版的滚动条构建器:
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
publicclass TextAreaNew extends Applet {
Button b1 =new Button("Text Area 1");
Button b2 =new Button("Text Area 2");
Button b3 =new Button("Replace Text");
Button b4 =new Button("Insert Text");
TextAreat1 = newTextArea("t1", 1, 30);
TextAreat2 = newTextArea("t2", 4, 30);
TextAreat3 = newTextArea("t3", 1, 30,TextArea.SCROLLBARS_NONE);
TextAreat4 = newTextArea("t4", 10, 10,TextArea.SCROLLBARS_VERTICAL_ONLY);
TextAreat5 = newTextArea("t5", 4, 30,TextArea.SCROLLBARS_HORIZONTAL_ONLY);
TextAreat6 = newTextArea("t6", 10, 10,TextArea.SCROLLBARS_BOTH);
publicvoidinit() {
b1.addActionListener(new B1L());
add(b1);
add(t1);
b2.addActionListener(new B2L());
add(b2);
add(t2);
b3.addActionListener(new B3L());
add(b3);
b4.addActionListener(new B4L());
add(b4);
add(t3);
add(t4);
add(t5);
add(t6);
}
class B1LimplementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
t5.append(t1.getText() +"\n");
}
}
class B2LimplementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
t2.setText("Inserted by Button 2");
t2.append(": " +t1.getText());
t5.append(t2.getText() +"\n");
}
}
class B3LimplementsActionListener {
publicvoid actionPerformed(ActionEvent e) {
String s ="Replacement ";
t2.replaceRange(s
给我老师的人工智能教程打call!http://blog.youkuaiyun.com/jiangjunshow
