推荐编码要领(java)
内部类是新的事件模子,而且事实上旧的事件模子连同新库的特征都被它好的支持,依赖老式的编程要领无疑增加了一个新的杂乱的因素。此刻有更多差异的要领为我们编写讨厌的代码。凑巧的是,这种代码显此刻本书中和措施样本中,而且甚至在文件和措施样本中同SUN公司区别开来。在这一节中,我们将看到一些关于我们会和不会运行新AWT的争执,并由向我们展示除了可以原谅的环境,我们可以随时利用吸收器类去办理我们的事件处理惩罚需要来竣事。因为这种要领同样是最简朴和最清晰的要领,它将会对我们进修它组成有效的辅佐。
在看到任何事以前,我们知道尽量Java 1.1向后兼容Java 1.0(也就是说,我们可以在1.1中编译和运行1.0的措施),但我们并不能在同一个措施里殽杂事件模子。换言之,当我们试图集成老的代码到一个新的措施中时,我们不能利用老式的action()要领在同一个措施中,因此我们必需抉择是否对新措施利用老的,难以维护的要领可能进级老的代码。这不会有太多的竞争因为新的要领对老的要领而言是如此的优秀。
1. 准则:运行它的好要领
为了给我们一些事物来举办较量,这儿有一个措施例子演示向我们推荐的要领。到此刻它会变得相当的熟悉和舒适。
//: 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); } } ///:~
这是颇有点微不敷道的:每个按钮有它本身的印出一些事物到节制台的吸收器。但请留意在整个措施中这不是一个条件语句,可能是一些暗示“我想要知道奈何使事件产生”的语句。每块代码都与运行有关,而不是范例检讨。也就是说,这是最好的编写我们的代码的要领;不只仅是它更易使我们领略观念,至少是使我们更易阅读和维护。剪切和粘贴到新的措施是同样如此的容易。
2. 将主类作为吸收器实现
第一个坏主意是一个凡是的和推荐的要领。这使得主类(有代表性的是措施片或帧,但它能酿成一些类)执行各类差异的吸收器。下面是一个例子:
//: 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举办较量,我们可以正好捕获一个吸收器类并粘贴它和最小的焦虑到任那里所。别的我们可觉得一个单独的事件注册多个吸收器类,答允甚至更多的模块在每个吸收器类在每个吸收器中运行。
3. 要领的殽杂
第二个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); } } ///:~
#p#分页标题#e#
因为actionPerformed()行动完成要领同主类细密地团结,所以难以复用代码。它的代码读起来同样是缭乱和令人厌烦的,远远高出了内部类要领。不公道的是,我们不得不在Java 1.1版中为事件利用那些老的思路。
4. 担任一个组件
建设一个新范例的组件时,在运行事件的老要领中,我们会常常看到差异的处所产生了变革。这里有一个措施例子来演示这种新的事情要领:
//: 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); } } ///:~
#p#分页标题#e#
这个措施例子同样证明白各类百般的发明和显示关于它们的信息的事件。这种显示是一种会合显示信息的要领。一组字符串去获取关于每种范例的事件的信息,而且show()要领对任何图像工具都配置了一个句柄,我们回收并直接地写在外观代码上。这种设计是有意的被某种事件反复利用。
激活面板代表了这种新型的组件。它是一个底部有一个按钮的彩色的面板,而且它由操作吸收器类为每一个单独的事件来激发捕获所有产生在它之上的事件,除了那些在激活面板过载的老式的processEvent()要领(留意它应该同样挪用super.processEvent())。操作这种要领的独一来由是它捕获产生的每一个事件,因此我们可以调查一连产生的每一事件。processEvent()要领没有更多的展示代表每个事件的字符串,不然它会不得不利用一串条件语句去寻谋事件。在其它方面,内嵌吸收类早已清晰地知道被发明的事件。(假定我们注册它们到组件,我们不需要任何的控件的逻辑,这将成为我们的目标。)因此,它们不会去查抄任何事件;这些事件正好做它们的原质料。
每个吸收器修改显示字符串和它的指定事件,而且挪用重画要领repaint()因此将显示这个字符串。我们同样能留意到一个凡是能消除闪烁的法门:
public void update(Graphics g) {
paint(g);
}
我们不会始终需要过载update(),但假如我们写下一些闪烁的措施,并运行它。默认的最新版本的排除配景然后挪用paint()要领从头画出一些图画。这个排除行动凡是会发生闪烁,可是不须要的,因为paint()重画了整个的外观。
我们可以看到很多的吸收器——可是,对吸收器输入查抄指令,但我们却不能吸收任何组件不支持的事件。(不像BadTechnuque.java那样我们能每时每刻看到)。
试验这个措施是十分的有教诲意义的,因为我们进修了很多的关于在Java中事件产生的要领。一则它展示了大大都开窗口的系统中设计上的瑕疵:它相当的难以去单击和释放鼠标,除非移动它,而且当我们实际上正试图用鼠标单击在某物体上时开窗口的会经常认为我们是在拖动。一个办理这个问题的方案是利用mousePressed()鼠标按下要领和mouseReleased()鼠标释放要领去取代mouseClicked()鼠标单击要领,然后判定是否去挪用我们本身的以时间和4个像素的鼠标滞后浸染的“mouseReallyClicked()真实的鼠标单击”要领。
5. 蹩脚的组件担任
另一种做法是挪用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); } } ///:~
简直,它可以或许事情。但却实在太蹩脚,并且很难编写、阅读、调试、维护以及再生。既然如此,为什么还不利用内部吸收器类呢?