g.drawLine(x1, y1,
x1 + wide, y1 + high);
g.drawLine(x1, y1 + high,
x1 + wide, y1);
}
if(state == OO) {
g.drawOval(x1, y1,
x1 + wide/2, y1 + high/2);
}
}
class ML extends MouseAdapter {
public void mousePressed(MouseEvent e) {
if(state == BLANK) {
state = turn;
turn = (turn == XX ? OO : XX);
}
else
state = (state == XX ? OO : XX);
repaint();
}
}
}
}
class BL implements ActionListener {
public void actionPerformed(ActionEvent e) {
Dialog d = new ToeDialog(
Integer.parseInt(rows.getText()),
Integer.parseInt(cols.getText()));
d.show();
}
}
public static void main(String[] args) {
Frame f = new ToeTestNew();
f.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setSize(200,100);
f.setVisible(true);
}
} ///:~
由于“静态”的东西只能位于类的外部一级,所以内部类不可能拥有静态数据或者静态内部类。
- 文件对话框
这个例子是直接用新事件模型对FileDialogTest.java修改而来。
//: FileDialogNew.java
// Demonstration of File dialog boxes
import java.awt.;
import java.awt.event.;
public class FileDialogNew extends Frame {
TextField filename = new TextField();
TextField directory = new TextField();
Button open = new Button("Open");
Button save = new Button("Save");
public FileDialogNew() {
setTitle("File Dialog Test");
Panel p = new Panel();
p.setLayout(new FlowLayout());
open.addActionListener(new OpenL());
p.add(open);
save.addActionListener(new SaveL());
p.add(save);
add(p, BorderLayout.SOUTH);
directory.setEditable(false);
filename.setEditable(false);
p = new Panel();
p.setLayout(new GridLayout(2,1));
p.add(filename);
p.add(directory);
add(p, BorderLayout.NORTH);
}
class OpenL implements ActionListener {
public void actionPerformed(ActionEvent e) {
// Two arguments, defaults to open file:
FileDialog d = new FileDialog(
FileDialogNew.this,
"What file do you want to open?");
d.setFile(".java");
d.setDirectory("."); // Current directory
d.show();
String yourFile = ".";
if((yourFile = d.getFile()) != null) {
filename.setText(yourFile);
directory.setText(d.getDirectory());
} else {
filename.setText("You pressed cancel");
directory.setText("");
}
}
}
class SaveL implements ActionListener {
public void actionPerformed(ActionEvent e) {
FileDialog d = new FileDialog(
FileDialogNew.this,
"What file do you want to save?",
FileDialog.SAVE);
d.setFile(".java");
d.setDirectory(".");
d.show();
String saveFile;
if((saveFile = d.getFile()) != null) {
filename.setText(saveFile);
directory.setText(d.getDirectory());
} else {
filename.setText("You pressed cancel");
directory.setText("");
}
}
}
public static void main(String[] args) {
Frame f = new FileDialogNew();
f.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setSize(250,110);
f.setVisible(true);
}
} ///:~
如果所有的改变是这样的容易那将有多棒,但至少它们已足够容易,并且我们的代码已受益于这改进的可读性上。
13.16.5 动态绑定事件
新AWT事件模型给我们带来的一个好处就是灵活性。在老的模型中我们被迫为我们的程序动作艰难地编写代码。但新的模型我们可以用单一方法调用增加和删除事件动作。下面的例子证明了这一点:
//: DynamicEvents.java
// The new Java 1.1 event model allows you to
// change event behavior dynamically. Also
// demonstrates multiple actions for an event.
import java.awt.;
import java.awt.event.;
import java.util.*;
public class DynamicEvents extends Frame {
Vector v = new Vector();
int i = 0;
Button
b1 = new Button("Button 1"),
b2 = new Button("Button 2");
public DynamicEvents() {
setLayout(new FlowLayout());
b1.addActionListener(new B());
b1.addActionListener(new B1());
b2.addActionListener(new B());
b2.addActionListener(new B2());
add(b1);
add(b2);
}
class B implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("A button was pressed");
}
}
class CountListener implements ActionListener {
int index;
public CountListener(int i) { index = i; }
public void actionPerformed(ActionEvent e) {
System.out.println(
"Counted Listener " + index);
}
}
class B1 implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Button 1 pressed");
ActionListener a = new CountListener(i++);
v.addElement(a);
b2.addActionListener(a);
}
}
class B2 implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Button 2 pressed");
int end = v.size() -1;
if(end >= 0) {
b2.removeActionListener(
(ActionListener)v.elementAt(end));
v.removeElementAt(end);
}
}
}
public static void main(String[] args) {
Frame f = new DynamicEvents();
f.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
f.setSize(300,200);
f.show();
}
} ///:~
这个例子采取的新手法包括:
(1) 在每个按钮上附着不少于一个的接收器。通常,组件把事件作为多造型处理,这意味着我们可以为单个事件注册许多接收器。当在特殊的组件中一个事件作为单一造型被处理时,我们会得到TooManyListenersException(即太多接收器异常)。
(2) 程序执行期间,接收器动态地被从按钮B2中增加和删除。增加用我们前面见到过的方法完成,但每个组件同样有一个removeXXXListener()(删除XXX接收器)方法来删除各种类型的接收器。
这种灵活性为我们的编程提供了更强大的能力。
我们注意到事件接收器不能保证在命令他们被增加时可被调用(虽然事实上大部分的执行工作都是用这种方法完成的)。
13.16.6 将事务逻辑与UI逻辑区分开
一般而言,我们需要设计我们的类如此以至于每一类做“一件事”。当涉及用户接口代码时就更显得尤为重要,因为它很容易地封装“您要做什么”和“怎样显示它”。这种有效的配合防止了代码的重复使用。更不用说它令人满意的从GUI中区分出我们的“事物逻辑”。使用这种方法,我们可以不仅仅更容易地重复使用事物逻辑,它同样可以更容易地重复使用GUI。
其它的争议是“动作对象”存在的完成分离机器的多层次系统。动作主要的定位规则允许所有新事件修改后立刻生效,并且这是如此一个引人注目的设置系统的方法。但是这些动作对象可以被在一些不同的应用程序使用并且因此不会被一些特殊的显示模式所约束。它们会合理地执行动作操作并且没有多余的事件。
下面的例子演示了从GUI代码中多么地轻松的区分事物逻辑:
//: Separation.java
// Separating GUI logic and business objects
import java.awt.;
import java.awt.event.;
import java.applet.;
class BusinessLogic {
private int modifier;
BusinessLogic(int mod) {
modifier = mod;
}
public void setModifier(int mod) {
modifier = mod;
}
public int getModifier() {
return modifier;
}
// Some business operations:
public int calculation1(int arg) {
return arg modifier;
}
public int calculation2(int arg) {
return arg + modifier;
}
}
public class Separation extends Applet {
TextField
t = new TextField(20),
mod = new TextField(20);
BusinessLogic bl = new BusinessLogic(2);
Button
calc1 = new Button("Calculation 1"),
calc2 = new Button("Calculation 2");
public void init() {
add(t);
calc1.addActionListener(new Calc1L());
calc2.addActionListener(new Calc2L());
add(calc1); add(calc2);
mod.addTextListener(new ModL());
add(new Label("Modifier:"));
add(mod);
}
static int getValue(TextField tf) {
try {
return Integer.parseInt(tf.getText());
} catch(NumberFormatException e) {
return 0;
}
}
class Calc1L implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.setText(Integer.toString(
bl.calculation1(getValue(t))));
}
}
class Calc2L implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.setText(Integer.toString(
bl.calculation2(getValue(t))));
}
}
class ModL implements TextListener {
public void textValueChanged(TextEvent e) {
bl.setModifier(getValue(mod));
}
}
public static void main(String[] args) {
Separation applet = new Separation();
Frame aFrame = new Frame("Separation");
aFrame.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(200,200);
applet.init();
applet.start();
aFrame.setVisible(true);
}
} ///:~
可以看到,事物逻辑是一个直接完成它的操作而不需要提示并且可以在GUI环境下使用的类。它正适合它的工作。区分动作记录了所有UI的详细资料,并且它只通过它的公共接口与事物逻辑交流。所有的操作围绕中心通过UI和事物逻辑对象来回获取信息。因此区分,轮流做它的工作。因为区分中只知道它同事物逻辑对象对话(也就是说,它没有高度的结合),它可以被强迫同其它类型的对象对话而没有更多的烦恼。
思考从事物逻辑中区分UI的条件,同样思考当我们调整传统的Java代码使它运行时,怎样使它更易存活。
13.16.7 推荐编码方法
内部类是新的事件模型,并且事实上旧的事件模型连同新库的特征都被它好的支持,依赖老式的编程方法无疑增加了一个新的混乱的因素。现在有更多不同的方法为我们编写讨厌的代码。凑巧的是,这种代码显现在本书中和程序样本中,并且甚至在文件和程序样本中同SUN公司区别开来。在这一节中,我们将看到一些关于我们会和不会运行新AWT的争执,并由向我们展示除了可以原谅的情况,我们可以随时使用接收器类去解决我们的事件处理需要来结束。因为这种方法同样是最简单和最清晰的方法,它将会对我们学习它构成有效的帮助。
在看到任何事以前,我们知道尽管Java 1.1向后兼容Java 1.0(也就是说,我们可以在1.1中编译和运行1.0的程序),但我们并不能在同一个程序里混合事件模型。换言之,当我们试图集成老的代码到一个新的程序中时,我们不能使用老式的action()方法在同一个程序中,因此我们必须决定是否对新程序使用老的,难以维护的方法或者升级老的代码。这不会有太多的竞争因为新的方法对老的方法而言是如此的优秀。
- 准则:运行它的好方法
为了给我们一些事物来进行比较,这儿有一个程序例子演示向我们推荐的方法。到现在它会变得相当的熟悉和舒适。
//: GoodIdea.java
// The best way to design classes using the new
// Java 1.1 event model: use an inner class for
// each different event. This maximizes
// flexibility and modularity.
import java.awt.;
import java.awt.event.;
import java.util.*;
public class GoodIdea extends Frame {
Button
b1 = new Button("Button 1"),
b2 = new Button("Button 2");
public GoodIdea() {
setLayout(new FlowLayout());
b1.addActionListener(new B1L());
b2.addActionListener(new B2L());
add(b1);
add(b2);
}
public class B1L implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Button 1 pressed");
}
}
public class B2L implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Button 2 pressed");
}
}
public static void main(String[] args) {
Frame f = new GoodIdea();
f.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e){
System.out.println("Window Closing");
System.exit(0);
}
});
f.setSize(300,200);
f.setVisible(true);
}
} ///:~
这是颇有点微不足道的:每个按钮有它自己的印出一些事物到控制台的接收器。但请注意在整个程序中这不是一个条件语句,或者是一些表示“我想要知道怎样使事件发生”的语句。每块代码都与运行有关,而不是类型检验。也就是说,这是最好的编写我们的代码的方法;不仅仅是它更易使我们理解概念,至少是使我们更易阅读和维护。剪切和粘贴到新的程序是同样如此的容易。
- 将主类作为接收器实现
第一个坏主意是一个通常的和推荐的方法。这使得主类(有代表性的是程序片或帧,但它能变成一些类)执行各种不同的接收器。下面是一个例子:
//: BadIdea1.java
// Some literature recommends this approach,
// but it's missing the point of the new event
// model in Java 1.1
import java.awt.;
import java.awt.event.;
import java.util.*;
public class BadIdea1 extends Frame
implements ActionListener, WindowListener {
Button
b1 = new Button("Button 1"),
b2 = new Button("Button 2");
public BadIdea1() {
setLayout(new FlowLayout());
addWindowListener(this);
b1.addActionListener(this);
b2.addActionListener(this);
add(b1);
add(b2);
}
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if(source == b1)
System.out.println("Button 1 pressed");
else if(source == b2)
System.out.println("Button 2 pressed");
else
System.out.println("Something else");
}
public void windowClosing(WindowEvent e) {
System.out.println("Window Closing");
System.exit(0);
}
public void windowClosed(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
public static void main(String[] args) {
Frame f = new BadIdea1();
f.setSize(300,200);
f.setVisible(true);
}
} ///:~
这样做的用途显示在下述三行里:
addWindowListener(this);
b1.addActionListener(this);
b2.addActionListener(this);
因为Badidea1执行动作接收器和窗中接收器,这些程序行当然可以接受,并且如果我们一直坚持设法使少量的类去减少服务器检索期间的程序片载入的作法,它看起来变成一个不错的主意。但是:
(1) Java 1.1版支持JAR文件,因此所有我们的文件可以被放置到一个单一的压缩的JAR文件中,只需要一次服务器检索。我们不再需要为Internet效率而减少类的数量。
(2) 上面的代码的组件更加的少,因此它难以抓住和粘贴。注意我们必须不仅要执行各种各样的接口为我们的主类,但在actionPerformed()方法中,我们利用一串条件语句测试哪个动作被完成了。不仅仅是这个状态倒退,远离接收器模型,除此之外,我们不能简单地重复使用actionPerformed()方法因为它是指定为这个特殊的应用程序使用的。将这个程序例子与GoodIdea.java进行比较,我们可以正好捕捉一个接收器类并粘贴它和最小的焦急到任何地方。另外我们可以为一个单独的事件注册多个接收器类,允许甚至更多的模块在每个接收器类在每个接收器中运行。
- 方法的混合
第二个bad idea混合了两种方法:使用内嵌接收器类,但同样执行一个或更多的接收器接口以作为主类的一部分。这种方法无需在书中和文件中进行解释,而且我可以臆测到Java开发者认为他们必须为不同的目的而采取不同的方法。但我们却不必——在我们编程时,我们或许可能会倾向于使用内嵌接收器类。
//: BadIdea2.java
// An improvement over BadIdea1.java, since it
// uses the WindowAdapter as an inner class
// instead of implementing all the methods of
// WindowListener, but still misses the
// valuable modularity of inner classes
import java.awt.;
import java.awt.event.;
import java.util.*;
public class BadIdea2 extends Frame
implements ActionListener {
Button
b1 = new Button("Button 1"),
b2 = new Button("Button 2");
public BadIdea2() {
setLayout(new FlowLayout());
addWindowListener(new WL());
b1.addActionListener(this);
b2.addActionListener(this);
add(b1);
add(b2);
}
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if(source == b1)
System.out.println("Button 1 pressed");
else if(source == b2)
System.out.println("Button 2 pressed");
else
System.out.println("Something else");
}
class WL extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.out.println("Window Closing");
System.exit(0);
}
}
public static void main(String[] args) {
Frame f = new BadIdea2();
f.setSize(300,200);
f.setVisible(true);
}
} ///:~
因为actionPerformed()动作完成方法同主类紧密地结合,所以难以复用代码。它的代码读起来同样是凌乱和令人厌烦的,远远超过了内部类方法。不合理的是,我们不得不在Java 1.1版中为事件使用那些老的思路。
- 继承一个组件
创建一个新类型的组件时,在运行事件的老方法中,我们会经常看到不同的地方发生了变化。这里有一个程序例子来演示这种新的工作方法:
//: GoodTechnique.java
// Your first choice when overriding components
// should be to install listeners. The code is
// much safer, more modular and maintainable.
import java.awt.;
import java.awt.event.;
class Display {
public static final int
EVENT = 0, COMPONENT = 1,
MOUSE = 2, MOUSE_MOVE = 3,
FOCUS = 4, KEY = 5, ACTION = 6,
LAST = 7;
public String[] evnt;
Display() {
evnt = new String[LAST];
for(int i = 0; i < LAST; i++)
evnt[i] = new String();
}
public void show(Graphics g) {
for(int i = 0; i < LAST; i++)
g.drawString(evnt[i], 0, 10 * i + 10);
}
}
class EnabledPanel extends Panel {
Color c;
int id;
Display display = new Display();
public EnabledPanel(int i, Color mc) {
id = i;
c = mc;
setLayout(new BorderLayout());
add(new MyButton(), BorderLayout.SOUTH);
addComponentListener(new CL());
addFocusListener(new FL());
addKeyListener(new KL());
addMouseListener(new ML());
addMouseMotionListener(new MML());
}
// To eliminate flicker:
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
g.setColor(c);
Dimension s = getSize();
g.fillRect(0, 0, s.width, s.height);
g.setColor(Color.black);
display.show(g);
}
// Don't need to enable anything for this:
public void processEvent(AWTEvent e) {
display.evnt[Display.EVENT]= e.toString();
repaint();
super.processEvent(e);
}
class CL implements ComponentListener {
public void componentMoved(ComponentEvent e){
display.evnt[Display.COMPONENT] =
"Component moved";
repaint();
}
public void
componentResized(ComponentEvent e) {
display.evnt[Display.COMPONENT] =
"Component resized";
repaint();
}
public void
componentHidden(ComponentEvent e) {
display.evnt[Display.COMPONENT] =
"Component hidden";
repaint();
}
public void componentShown(ComponentEvent e){
display.evnt[Display.COMPONENT] =
"Component shown";
repaint();
}
}
class FL implements FocusListener {
public void focusGained(FocusEvent e) {
display.evnt[Display.FOCUS] =
"FOCUS gained";
repaint();
}
public void focusLost(FocusEvent e) {
display.evnt[Display.FOCUS] =
"FOCUS lost";
repaint();
}
}
class KL implements KeyListener {
public void keyPressed(KeyEvent e) {
display.evnt[Display.KEY] =
"KEY pressed: ";
showCode(e);
}
public void keyReleased(KeyEvent e) {
display.evnt[Display.KEY] =
"KEY released: ";
showCode(e);
}
public void keyTyped(KeyEvent e) {
display.evnt[Display.KEY] =
"KEY typed: ";
showCode(e);
}
void showCode(KeyEvent e) {
int code = e.getKeyCode();
display.evnt[Display.KEY] +=
KeyEvent.getKeyText(code);
repaint();
}
}
class ML implements MouseListener {
public void mouseClicked(MouseEvent e) {
requestFocus(); // Get FOCUS on click
display.evnt[Display.MOUSE] =
"MOUSE clicked";
showMouse(e);
}
public void mousePressed(MouseEvent e) {
display.evnt[Display.MOUSE] =
"MOUSE pressed";
showMouse(e);
}
public void mouseReleased(MouseEvent e) {
display.evnt[Display.MOUSE] =
"MOUSE released";
showMouse(e);
}
public void mouseEntered(MouseEvent e) {
display.evnt[Display.MOUSE] =
"MOUSE entered";
showMouse(e);
}
public void mouseExited(MouseEvent e) {
display.evnt[Display.MOUSE] =
"MOUSE exited";
showMouse(e);
}
void showMouse(MouseEvent e) {
display.evnt[Display.MOUSE] +=
", x = " + e.getX() +
", y = " + e.getY();
repaint();
}
}
class MML implements MouseMotionListener {
public void mouseDragged(MouseEvent e) {
display.evnt[Display.MOUSE_MOVE] =
"MOUSE dragged";
showMouse(e);
}
public void mouseMoved(MouseEvent e) {
display.evnt[Display.MOUSE_MOVE] =
"MOUSE moved";
showMouse(e);
}
void showMouse(MouseEvent e) {
display.evnt[Display.MOUSE_MOVE] +=
", x = " + e.getX() +
", y = " + e.getY();
repaint();
}
}
}
class MyButton extends Button {
int clickCounter;
String label = "";
public MyButton() {
addActionListener(new AL());
}
public void paint(Graphics g) {
g.setColor(Color.green);
Dimension s = getSize();
g.fillRect(0, 0, s.width, s.height);
g.setColor(Color.black);
g.drawRect(0, 0, s.width - 1, s.height - 1);
drawLabel(g);
}
private void drawLabel(Graphics g) {
FontMetrics fm = g.getFontMetrics();
int width = fm.stringWidth(label);
int height = fm.getHeight();
int ascent = fm.getAscent();
int leading = fm.getLeading();
int horizMargin =
(getSize().width - width)/2;
int verMargin =
(getSize().height - height)/2;
g.setColor(Color.red);
g.drawString(label, horizMargin,
verMargin + ascent + leading);
}
class AL implements ActionListener {
public void actionPerformed(ActionEvent e) {
clickCounter++;
label = "click #" + clickCounter +
" " + e.toString();
repaint();
}
}
}
public class GoodTechnique extends Frame {
GoodTechnique() {
setLayout(new GridLayout(2,2));
add(new EnabledPanel(1, Color.cyan));
add(new EnabledPanel(2, Color.lightGray));
add(new EnabledPanel(3, Color.yellow));
}
public static void main(String[] args) {
Frame f = new GoodTechnique();
f.setTitle("Good Technique");
f.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e){
System.out.println(e);
System.out.println("Window Closing");
System.exit(0);
}
});
f.setSize(700,700);
f.setVisible(true);
}
} ///:~
这个程序例子同样证明了各种各样的发现和显示关于它们的信息的事件。这种显示是一种集中显示信息的方法。一组字符串去获取关于每种类型的事件的信息,并且show()方法对任何图像对象都设置了一个句柄,我们采用并直接地写在外观代码上。这种设计是有意的被某种事件重复使用。
激活面板代表了这种新型的组件。它是一个底部有一个按钮的彩色的面板,并且它由利用接收器类为每一个单独的事件来引发捕捉所有发生在它之上的事件,除了那些在激活面板过载的老式的processEvent()方法(注意它应该同样调用super.processEvent())。利用这种方法的唯一理由是它捕捉发生的每一个事件,因此我们可以观察持续发生的每一事件。processEvent()方法没有更多的展示代表每个事件的字符串,否则它会不得不使用一串条件语句去寻找事件。在其它方面,内嵌接收类早已清晰地知道被发现的事件。(假定我们注册它们到组件,我们不需要任何的控件的逻辑,这将成为我们的目的。)因此,它们不会去检查任何事件;这些事件正好做它们的原材料。
每个接收器修改显示字符串和它的指定事件,并且调用重画方法repaint()因此将显示这个字符串。我们同样能注意到一个通常能消除闪烁的秘诀:
public void update(Graphics g) {
paint(g);
}
我们不会始终需要过载update(),但如果我们写下一些闪烁的程序,并运行它。默认的最新版本的清除背景然后调用paint()方法重新画出一些图画。这个清除动作通常会产生闪烁,但是不必要的,因为paint()重画了整个的外观。
我们可以看到许多的接收器——但是,对接收器输入检查指令,但我们却不能接收任何组件不支持的事件。(不像BadTechnuque.java那样我们能时时刻刻看到)。
试验这个程序是十分的有教育意义的,因为我们学习了许多的关于在Java中事件发生的方法。一则它展示了大多数开窗口的系统中设计上的瑕疵:它相当的难以去单击和释放鼠标,除非移动它,并且当我们实际上正试图用鼠标单击在某物体上时开窗口的会常常认为我们是在拖动。一个解决这个问题的方案是使用mousePressed()鼠标按下方法和mouseReleased()鼠标释放方法去代替mouseClicked()鼠标单击方法,然后判断是否去调用我们自己的以时间和4个像素的鼠标滞后作用的“mouseReallyClicked()真实的鼠标单击”方法。
- 蹩脚的组件继承
另一种做法是调用enableEvent()方法,并将与希望控制的事件对应的模型传递给它(许多参考书中都曾提及这种做法)。这样做会造成那些事件被发送至老式方法(尽管它们对Java 1.1来说是新的),并采用象processFocusEvent()这样的名字。也必须要记住调用基础类版本。下面是它看起来的样子。
//: BadTechnique.java
// It's possible to override components this way,
// but the listener approach is much better, so
// why would you?
import java.awt.;
import java.awt.event.;
class Display {
public static final int
EVENT = 0, COMPONENT = 1,
MOUSE = 2, MOUSE_MOVE = 3,
FOCUS = 4, KEY = 5, ACTION = 6,
LAST = 7;
public String[] evnt;
Display() {
evnt = new String[LAST];
for(int i = 0; i < LAST; i++)
evnt[i] = new String();
}
public void show(Graphics g) {
for(int i = 0; i < LAST; i++)
g.drawString(evnt[i], 0, 10 * i + 10);
}
}
class EnabledPanel extends Panel {
Color c;
int id;
Display display = new Display();
public EnabledPanel(int i, Color mc) {
id = i;
c = mc;
setLayout(new BorderLayout());
add(new MyButton(), BorderLayout.SOUTH);
// Type checking is lost. You can enable and
// process events that the component doesn't
// capture:
enableEvents(
// Panel doesn't handle these:
AWTEvent.ACTION_EVENT_MASK |
AWTEvent.ADJUSTMENT_EVENT_MASK |
AWTEvent.ITEM_EVENT_MASK |
AWTEvent.TEXT_EVENT_MASK |
AWTEvent.WINDOW_EVENT_MASK |
// Panel can handle these:
AWTEvent.COMPONENT_EVENT_MASK |
AWTEvent.FOCUS_EVENT_MASK |
AWTEvent.KEY_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK |
AWTEvent.CONTAINER_EVENT_MASK);
// You can enable an event without
// overriding its process method.
}
// To eliminate flicker:
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
g.setColor(c);
Dimension s = getSize();
g.fillRect(0, 0, s.width, s.height);
g.setColor(Color.black);
display.show(g);
}
public void processEvent(AWTEvent e) {
display.evnt[Display.EVENT]= e.toString();
repaint();
super.processEvent(e);
}
public void
processComponentEvent(ComponentEvent e) {
switch(e.getID()) {
case ComponentEvent.COMPONENT_MOVED:
display.evnt[Display.COMPONENT] =
"Component moved";
break;
case ComponentEvent.COMPONENT_RESIZED:
display.evnt[Display.COMPONENT] =
"Component resized";
break;
case ComponentEvent.COMPONENT_HIDDEN:
display.evnt[Display.COMPONENT] =
"Component hidden";
break;
case ComponentEvent.COMPONENT_SHOWN:
display.evnt[Display.COMPONENT] =
"Component shown";
break;
default:
}
repaint();
// Must always remember to call the "super"
// version of whatever you override:
super.processComponentEvent(e);
}
public void processFocusEvent(FocusEvent e) {
switch(e.getID()) {
case FocusEvent.FOCUS_GAINED:
display.evnt[Display.FOCUS] =
"FOCUS gained";
break;
case FocusEvent.FOCUS_LOST:
display.evnt[Display.FOCUS] =
"FOCUS lost";
break;
default:
}
repaint();
super.processFocusEvent(e);
}
public void processKeyEvent(KeyEvent e) {
switch(e.getID()) {
case KeyEvent.KEY_PRESSED:
display.evnt[Display.KEY] =
"KEY pressed: ";
break;
case KeyEvent.KEY_RELEASED:
display.evnt[Display.KEY] =
"KEY released: ";
break;
case KeyEvent.KEY_TYPED:
display.evnt[Display.KEY] =
"KEY typed: ";
break;
default:
}
int code = e.getKeyCode();
display.evnt[Display.KEY] +=
KeyEvent.getKeyText(code);
repaint();
super.processKeyEvent(e);
}
public void processMouseEvent(MouseEvent e) {
switch(e.getID()) {
case MouseEvent.MOUSE_CLICKED:
requestFocus(); // Get FOCUS on click
display.evnt[Display.MOUSE] =
"MOUSE clicked";
break;
case MouseEvent.MOUSE_PRESSED:
display.evnt[Display.MOUSE] =
"MOUSE pressed";
break;
case MouseEvent.MOUSE_RELEASED:
display.evnt[Display.MOUSE] =
"MOUSE released";
break;
case MouseEvent.MOUSE_ENTERED:
display.evnt[Display.MOUSE] =
"MOUSE entered";
break;
case MouseEvent.MOUSE_EXITED:
display.evnt[Display.MOUSE] =
"MOUSE exited";
break;
default:
}
display.evnt[Display.MOUSE] +=
", x = " + e.getX() +
", y = " + e.getY();
repaint();
super.processMouseEvent(e);
}
public void
processMouseMotionEvent(MouseEvent e) {
switch(e.getID()) {
case MouseEvent.MOUSE_DRAGGED:
display.evnt[Display.MOUSE_MOVE] =
"MOUSE dragged";
break;
case MouseEvent.MOUSE_MOVED:
display.evnt[Display.MOUSE_MOVE] =
"MOUSE moved";
break;
default:
}
display.evnt[Display.MOUSE_MOVE] +=
", x = " + e.getX() +
", y = " + e.getY();
repaint();
super.processMouseMotionEvent(e);
}
}
class MyButton extends Button {
int clickCounter;
String label = "";
public MyButton() {
enableEvents(AWTEvent.ACTION_EVENT_MASK);
}
public void paint(Graphics g) {
g.setColor(Color.green);
Dimension s = getSize();
g.fillRect(0, 0, s.width, s.height);
g.setColor(Color.black);
g.drawRect(0, 0, s.width - 1, s.height - 1);
drawLabel(g);
}
private void drawLabel(Graphics g) {
FontMetrics fm = g.getFontMetrics();
int width = fm.stringWidth(label);
int height = fm.getHeight();
int ascent = fm.getAscent();
int leading = fm.getLeading();
int horizMargin =
(getSize().width - width)/2;
int verMargin =
(getSize().height - height)/2;
g.setColor(Color.red);
g.drawString(label, horizMargin,
verMargin + ascent + leading);
}
public void processActionEvent(ActionEvent e) {
clickCounter++;
label = "click #" + clickCounter +
" " + e.toString();
repaint();
super.processActionEvent(e);
}
}
public class BadTechnique extends Frame {
BadTechnique() {
setLayout(new GridLayout(2,2));
add(new EnabledPanel(1, Color.cyan));
add(new EnabledPanel(2, Color.lightGray));
add(new EnabledPanel(3, Color.yellow));
// You can also do it for Windows:
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
}
public void processWindowEvent(WindowEvent e) {
System.out.println(e);
if(e.getID() == WindowEvent.WINDOW_CLOSING) {
System.out.println("Window Closing");
System.exit(0);
}
}
public static void main(String[] args) {
Frame f = new BadTechnique();
f.setTitle("Bad Technique");
f.setSize(700,700);
f.setVisible(true);
}
} ///:~
的确,它能够工作。但却实在太蹩脚,而且很难编写、阅读、调试、维护以及再生。既然如此,为什么还不使用内部接收器类呢?
