Your Ad Here
首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 编程语言 > Java > 事件监听器 将JavaBeans接通起来的方法(2)
【标  题】:事件监听器 将JavaBeans接通起来的方法(2)
【关键字】:方法,Java,事件,Bean,JavaBean,Beans,Be,JavaBeans
【来  源】:网络

事件监听器 将JavaBeans接通起来的方法(2)

Your Ad Here 事件监听器 将JavaBeans接通起来的方法(2)

所有这些与Beans有何关系?
JavaBeans主要利用事件监听器接口进行通讯

事件监听器为对象提供了一种普适的不经过继承关系而进行通讯的方法。正因为如此,他们对于组件技术来说,是一种非常好的通讯机制,从某种角度来讲,它们即是JavaBeans。虽然上面看到的事件监听器全都出现在AWT中,但他们的应用不仅仅限于用户接口。他们可以被应用于各式各样的事件:属性的变更,传感器的阅读,时钟事件,文件系统行为,对象命名等。

现在开始“Beany”部分

#你能够为它们定义你自己的事件类型和事件监听器。

#如果你的新事件类型被称为Eventtype,那么通过实现下面两个方法,你的Beans就能成为你的新事件类型的源。


o addEventtypeListener(EventObject e)
o removeEventtypeListener(EventObject e)

#那么通过实现接口EventListener,其它Beans能够成为事件的目标。

#最后,你可以通过调用sourceBean。addEventtypeListener(targetBean)"接通"事件的源和事件目标。

创建和利用你自已的EventObject类型
让我们看一个创建EventObject类型的例子。这个例子是在上个月的一个例子,BarChartBean的基础上进行"脑外科式"(brainsurgery)的改造而成的。我先在BarChartBean中增加代码,以使得在Bar区域内,用户每次点击或拖动鼠标时,都重先设置percent属性。这为我们提供了一个通过鼠标来改变Percent属性的方法。

BarChartBean通过预先定义的PropertyChangeListener接口(在java。beans包中定义的,通用的事件监听器接口),来通知其它对象它的percent属性变化情况。现在,我们通过定义一个新的事件类型,PercentEvent,为外部Beans增加另一个方法,以使这些Beans能够被通知到每一次percent的变化。



import java.util.*;

//
//该类封装每一次Percent属性的变化,并将变化传递给"PercentListener".
//

public class PercentEvent extends java.util.EventObject
{
protected int iOld_, iNew_;

public PercentEvent(Object source, int iOld, int iNew)
{
super(source);
iOld_ = iOld;
iNew_ = iNew;
}

public int getOldPercent() { return iOld_; }

public int getPercent() { return iNew_; }

public int getChangedBy() { return iNew_ - iOld_; }
}

你是否还记得,在前面我们曾提到过在事件中封装类规范(class-specific)数据?妤了,在此,新的和旧的百分比值都规范于PercentEvent事件类。

现在,让我们为这一新的事件类型定义一个监听器接口。

import java.util.*;

//每一个想监听"percent"变化情况的类都
//应该实现这个接口

public interface PercentListener extends EventListener
{
public void percentChanged(EventObject e);
}

接下来,我们要把BarChartBean变成为一个PercentEvent的源。为达此目的,我们将在BarChartBean中实现addPercentListener()和removePercentListener(),并且无论何时,当percent属性改变时,都能够去修改所有的监听器。(在此,我们只需看源代码中相关的部分)

//
// BarChart Bean现在接收输入
//
public class BarChartBean extends Canvas
implements Serializable, PropertyChangeListener
{
// ...
// List of percent listeners.
private Vector percentListeners_;

// ... a whole lotta methods...

// Set/Get methods for percent
public void setPercent(int iPercent)
{
// Set new percent, and only if necessary repaint()
// This is the only place that iPercent´s range is controlled
if (iPercent <= 100 && iPercent>= 0)
{
// Save old value, set new value FIRST
int prevPercent = iPercent_;
iPercent_ = iPercent;

// Notify property listeners of change to "percent" property
pcs_.firePropertyChange("percent",
new Integer(prevPercent),
new Integer(iPercent_));

// Notify all listeners for "percent" change
notifyPercentChanged(prevPercent, iPercent);

// Repaint only if necessary.
if (prevPercent != iPercent_)
{
repaint();
}
}
}

// ...

//
// These methods are for handling "PercentListeners"
//

// Add a new percent listener
public synchronized void addPercentListener(PercentListener listener)
{
percentListeners_.addElement(listener);
}

// Remove a percent listener
public synchronized void removePercentListener(PercentListener listener)
{
percentListeners_.removeElement(listener);
}

// Notify all listeners that "percent" changed
protected void notifyPercentChanged(int oldPct, int newPct)
{
Vector thisList = new Vector();
PercentEvent thisEvent = new PercentEvent(this, oldPct, newPct);

// Make a copy of the list so potential changes to it by
// other threads won´t affect traversal.
synchronized (this)
{
thisList = (Vector)percentListeners_.clone();
}

// Send a "PercentEvent" to every listener.
for (int elem = 0; elem

矢量percentListeners_是一列当percent属性改变时,需要被通知的PercentListeners(实现了PercentListener接口的对象)清单。在源程序的更下方,以前的setPercent()方法调用firePropertyChange(),而现在,它还调用notifyPercentChanged()以通知在percentListeners_清单中的所有对象。(在此,实际上,我们提供了两种通知percent变化的方法:(以前的)作为一个PropertyChange,和现在的作为一个PercentEvent。)

addPercentListener()和removePercentListener()方法仅仅向监听器清单中追加或从清单中删除对象。追加和删除同步进行,因为多线索将尽可能向清单中追加或从清单中删除监听器。如果在处理这一列清单的过程中,发生一个上下文转换(acontextswitch),那么可怕的事情将会发生。(这个清单可能被毁坏。如果你足够幸运,它可能会导致错误难以查找;如果你不幸运的话,它将会使应用程序行为不可预测,更有胜之,毁坏数据。)

实质上的工作是在notifyPercentChanged()中进行的。根据输入的数据,它将创建一个新的PercentEvent事件,接着percentListeners_清单将被克隆,也就是说,它将被彻底复制到一个新的矢量中。synchronized关键词暗示我们为什么这样做的理由:当我们部分遍历这一清单时,另一条线索正要删除某一监听器,那会出现什么情况呢?(参见前一段中的"可怕事件"。)notifyPercentChanged()中的循环只简单地根据我们创建的清单,将PercentEvent事件传递给所有的监听器。

当然,如果没有事件目标,一个事件源对于我们也没有任何价值。下面,我们将创建一个Bean,它其实是一条标签,能够在接收到一个PercentEvent事件后,随之改变。


import java.util.*;
import java.io.*;
import java.awt.*;
import PercentListener;
import PercentEvent;

public class PercentLabel
extends Label
implements Serializable, PercentListener
{
public PercentLabel() { }

public void percentChanged(EventObject event)
{
if (event instanceof PercentEvent)
{
PercentEvent pe = (PercentEvent) event;
setText(Integer.toString(pe.getPercent()));
}
}
}

上面例子非常简单。现在,我们所要做的就是将二者连接起来:

import java.awt.*;
import java.io.*;
import BarChartBean;

public class Example
extends Panel
implements Serializable
{
private PercentLabel pl_ = new PercentLabel();
private BarChartBean bcb_ = new BarChartBean();

public Example()
{
bcb_.addPercentListener(pl_);
setLayout(new BorderLayout());
add("North", pl_);
add("South", bcb_);
}
}

下面就是所得到的BEAN:



PercentChange事件工作正常

你可以下载JAR文件并在BeanBox中自己试着这样做。

现在的问题是,既然PropertyChange接口已经存在,那么,为什么每个人都还想去做额外的工作,来创建他们自已的事件类型呢?一个可能的原因是,同PropertyChange所允许传递的信息相比,在事件中,你能够传递更多的信息。你可以在一个事件中,封装你所想要的任何东西。实际上,上一个月我们所见到的PropertyChange机制其实是根据我们刚才所见的事件监听器实现的。

事件也比PropertyChange更加通用。新的AWT定义了诸如鼠标移动、敲击键盘、调整组件大小等等的事件类型。你可能想在你的应用程序中,为这些新的事件,分别定义一个新的事件处理类,并且使得他们不全都去本能地模仿PropertyChange。例如,在一个ModemControlBean中,你可以创建一个新的ModemEvent类,并创建一个适当的监听器接口,使其它Beans能够监听到诸如ModemConnectEvent,ModemDisconnectEvent等等之类的事件。这种做法将比有一个被称为"ModemState"(或其它什么的)属性的做法更为合理。后者的做法中,"ModemState"属性在有些情况下只能读出(readonly),而其他的情况下既可读又可写,其实并不是真正意义上的"属性"。

IDEs能够利用新的Java映像机制,这种机制可使Java程序分析类文件,以分析Beans。IDEs能够寻找实现EventListener的类(以找出事件的目标),并能够寻找名字象addSomethingListener()的方法(以找出事件的源)。(请记住上面关于空的EventListener接口对IDEs有用的评论-这就是有用的原因。)你也可以在你的Beans中增加方法以明确地告诉IDEs(或其它任何提问者)你的Bean产生或处理什么事件。

我们刚刚手工编写了一个名为Example的类,它能够利用某个监听器接口使一个Bean和另一个Bean"接通"。在此,我们将把它做得更好,利用可视化编程环境实现同样的功能。下面的几步将在BeanBox上实现。



在你的jars目录中,打开带有ColorBar。Jar的BeanBox,以使得exampleBean能被装载。

向BeanBox中增加一个BarChartBean。

往BeanBox中加入一个PercentLabel(最好将其变为白色,以便容易看到)。

选择在BeanBox中的BarchartBean。

选择菜单项Edit->Events->percent->percentChanged。现在你能够看到:BeanBox自动地识别新的事件类型并将其加入到事件列表清单中,这一清单你可从BarChartBean中得到。真Cool!

你需要一条红色的橡皮带式生成线。移动鼠标到PercentLabel上,并点击它。

一个对话框将出现,问你当一个percentChanged事件到达PercentLabel时,你希望选择调用哪一个方法。你选择percentChanged。
BeanBox创建一个"adaptor"类,它即是一个PercentListener。

接下来,BeanBox编译它所写的adaptor类,创建一个实例,并且利用BarChartBean。addPercentListener()调用它。一旦BarChartBean产生了percent事件,它将这些事件传递给adaptor(因为adaptor是一直在"监听"),adaptor接着将事件传递给PercentListener。percentChanged()。

实际上,下面是BeanBox产生的"adaptor"类的代码:

//自动生成的事件连接文件.



package tmp.sun.beanbox;
import PercentLabel;
import PercentListener;

public class ___Hookup_1452f9e502 implements PercentListener, java.io.Serializable
{

public void setTarget(PercentLabel t)
{
target = t;
}

public void percentChanged(java.util.EventObject arg0)
{
target.percentChanged(arg0);
}

private PercentLabel target;
}




现在,只须象以前一样应用BarChartBean,PercentListener就会自动地跟踪percent事件。这就是可视化的应用程序开发。
如果你的浏览器支持动态GIF文件,请访问该地址(http://www。javaworld。com/javaworld/jw-10-1997/images/percentdemo.gif)以看到这一过程的动态演示。

Bean-a-palooza:为同一类型的事件分组
在你写监听器接口的时候,应当注意到,通常情况下,仅创建一个监听器接口,使其包括一个若干相关事件共用的Eventtypeperformed()方法,比为每一个事件都创建一个监听器接口的做法更加省事而且简便。

关于这种思想的一个好例子是JKD1。1AWT中鼠标事件的处理方式。(实际上,其中有两种这样的接口,根据处理效率的不同要求,将鼠标事件进行分组)。

为了捕捉新的AWT中的鼠标事件,一个类必须实现的下面两个接口中的一个或者全部:java。awt。event。MouseListener或java.awt.event.MouseMotionListener.这两个接口分别包括以下方法:



public interface MouseListener extends EventListener
{
public void mouseClicked(MouseEvent e);
public void mouseEntered(MouseEvent e);
public void mouseExited(MouseEvent e);
public void mousePressed(MouseEvent e);
public void mouseReleased(MouseEvent e);
};

public interface MouseMotionListener extends EventListener
{
public void mouseDragged(MouseEvent e);
public void mouseMoved(MouseEvent e);
}

就目前而言,AWT的设计者原本能够非常简便地定义大量的接口和相关的事件类型:

public interface MouseClickListener extends EventListener
{
public void mouseClicked(MouseClickEvent e);
}

public interface MouseEnterListener extends EventListener
{
public void mouseEntered(MouseEnterEvent e);
}

// 等 等

但是,AWT的设计者其实是将多个方法进行分组,分别归类到上面所说的两个接口中。一个需要接收鼠标事件的类只须声明它实现MouseListener,并接着实现所有的诸如voidmouseClicked(MouseEvente),voidmouseEntered(MouseEvente)等等之类的方法,即可当事件发生时,能够去做任何需要做的事。如果你对mouseExits事件不感兴趣,你所要做的仅仅就是使这一方法不做任何事情。(实现一个接口时,Java需要其中所有的方法都被定义。如果接口中的所有方法没有被完全被定义,那么你的类将不能通过编译。当然也有办法绕过这一情况--可参见下面的"DoNothing类"。)

为什么Java设计者不将所有的鼠标事件处理方法都归入到同一个接口中,而是将这些方法分成两类分别归到MouseListener和MouseMotionListener两个接口中呢?这涉及到一个性能问题:鼠标移动事件出现频率高而且速度快,远远快于点击鼠标按钮的速度。如果实际运行中,没有一个类来监听鼠标的移动,那么,AWT(内部地)就会丢弃它所接收到的任何此类事件,以节省时间,而不将时间浪费在调用一个其实并不做任何事情的方法之上。

在前面,我已提到了将要进一步讨论的一个办法,它希望绕过Java恼人的而且顽固坚持的行径:一个接口中的所有方法都必须被定义,即使这些方法不做任何事情。下面我们将写一个类,它将跟踪并保存下鼠标被点击的次数。这个类的相关部分大致如下:



// count right mouse clicks
public class ClickCounter extends Panel implements MouseListener
{
private int iClicks_ = 0;

// Listen for events on self
ClickCounter() { addMouseListener(this); }

public void mouseClicked(MouseEvent e) { iClicks_++; repaint();}
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void paint() { // paint the # of clicks on the panel }
}

从上可以注意到,必须定义许多无用的空函数的确是件恼人的事。所幸的是,AWT的设计者为你解决了这一难题:他们创造了不做任何事情的类。

不做任何事情的类(Do-nothingclasses)
java。awt。event。MouseAdapter类所要做的事就是不做任何事情。它实现MouseListener类,并且它的方法丢弃所接收到事件。其方法不做任何事件的类能够有什么用途呢?对于类自身而言,几乎一无所用??然而,你可以创建一个MouseAdapter的子类,并在子类中仅仅实现那些你感兴趣的方法。上面所说的ClickCounter例子现在可以这样实现:



public class ClickCounter extends MouseAdapter
{
private int iClicks_ = 0;

// Listen for events on self
ClickCounter() { addMouseListener(this); }

public void mouseClicked(MouseEvent e) { iClicks_++; repaint();}
public void paint() { // paint the # of clicks on the panel }
}

与前面的将类定义为实现MouseListener的做法不同,在这里,类的定义通过扩展MouseAdapter完成,并且将MouseAdapter类中不做任何事的函数斩草除根(因此你就不必写出那些不做任何事的方法)。你要写的所有的函数就是mouseClicked(),它也就是你所感兴趣的所有事情。这难道不够方便吗?

你将会发现,在AWT中的绝大多数的事件监听器接口群组都包括三个相关的定义:

EventtypeEvent:所发生的事件

EventtypeListener:事件监听接口

EventtypeAdapter:不做任何事件的adaptor类,你可因此而不必。。。
所有这些对于Beans的重要意义是:

当为你Beans创建事件监听接口时:

在事件监听器接口中,为相关的事件类型分组

如果在接口中有多个方法,提供一个adapter类以使你的接口更容易应用。
内嵌类
现在,想象你有20个用户接口widget类型,并且当某一MouseEvent事件发生时,这些接口类型各自做不同的事情。你的名字空间将会被诸如:DrawPaneMouseEvent,MousePositionMouseEvent等等的各种事件处理类弄得混杂不堪。而且这些类都仅仅能够被用于那些"拥有"它们的类。Java1。1中,有一种名为内嵌类的新机制允许你来控制类的定义范围。

在Java中,内嵌类是在另一个类中定义的类。它的"活动范围"(或"可见范围")仅仅限于类被定义时所在的块。

内嵌类是Java1。1的新特性,它对于定义某些adaptor类是十分有用的,这些类"通晓"它们所操纵的对象的实现详情。ClickCounter提供了这样的一个(非常简单的)例子:



public class ClickCounter extends Panel
{
private int iClicks_ = 0;

class MouseHandler extends MouseAdapter
{
public void mouseClicked(MouseEvent e)
{ iClicks_++; repaint();}
}

// Listen for events on self
ClickCounter() { addMouseListener(new MouseHandler()); }

public void paint() { // paint the # of clicks on the panel }
};
// 下面的语句不合法,将不会通过编译!
MouseHandler m = new MouseHandler();

上面的程序对被称为MouseHandler的内嵌类的点击鼠标事件进行推迟处理。这个类"知道"包括它的类所拥有的变量"iClicks"。如果你企图在一个"顶层"类(也就是说,任何不是内嵌类的类)中这样做,那么,你就不可能得到ClickCounter中(iClicks的)具体的实现情况。你将不得不增加一些方法来控制点击的计数,这样一来,你也破坏了封装机制。(词语"顶层类"(top-levelclass)是一个retronym,就是说,它是对旧事物的一个新名字。)

在此例中,我故意包括了一个错误:在ClickCounter类的范围之外,定义了一个MouseHandler对象。程序在编译时将会出错,因为MouseHandler并不是在ClickCounter类之外定义的。如果某一顶层类碰巧被命名为MouseHandler,那么代码将会正常被编译,但MouseHandlerm将不会与ClickCounter的内嵌类同型。应该看到:用这种办法分离名字空间是内嵌类的一个目的。

内嵌类有一个值得引起注意的地方是:虽然它们的范围不同于顶层类的范围,但每一个你定义的内嵌类都被编译到它自己的类文件中。在Win32系统中,ClickCounter的内嵌类文件名将会是ClickCounter$MouseHandler。class。(核查你的文件或做一点小试验,来看一看在你的系统上它是怎么工作的。)这里值得注意的是,即使这些内嵌类不是Beans,你的Beans所定义的每一个内嵌类将会用到它们的(内嵌)类文件,否则,Beans将不会转动。所以,一定要在你的JAR文件中包括你所定义的内嵌类所用到的类文件。

如果你还想参看另一个利用内嵌类事件adaptor处理鼠标事件的例子,你可以查阅BarChartBean的源码。参见下面的Resources,你可以获得此源码及更多的有关内嵌类的信息(其中有些东西的确不可思意)。

(全文完)

漫谈EJB(4):【上一篇】
漫谈EJB(3):【下一篇】
【相关文章】
  • JavaBeans 程序开发入门教程(1)
  • JavaBeans 程序开发入门教程(2)
  • 从EJB会话bean访问EJB实体bean
  • 将DBMS存储过程封装为会话EJB组件中的方法(1)
  • 根据应用剪裁JavaBeans(1)
  • 将DBMS存储过程封装为会话EJB组件中的方法(2)
  • MD5的Java Bean实现
  • EJB-Java服务器端构件模型介绍
  • EJB-Java服务器端构件模型使用入门(1)
  • EJB-Java服务器端构件模型使用入门(2)
  • 【随机文章】
  • 转:孙鑫老师大作 - 在对话框程序中让对话框捕获WM_KEYDOWN消息
  • 一定要有计划
  • 装了ZoneAlarm后无法浏览局域网电脑的解决方法
  • 用任务条跟踪文件下载
  • uClinux 下如何写framebuffer 设备驱动
  • gui的设计笔记(3)
  • 项目中文档组织的之不良习惯
  • ORACLE函数大全
  • PhotoImpact 6.0中文版入门教程-发光的文字(2)
  • 测试方法的辩证统一(之三)
  • 【相关评论】
    没有相关评论
    【发表评论】
    姓名:
    邮件:
    随机码*
    评论*
          
    |  首 页  |  版权声明  |  联系我们   |  网站地图  |
    CopyRight © 2004-2007 软讯网络 All Rigths Reserved.