通用的事件监听器模式是怎样的?
事件监听器模式在我们编写Android、Java、QT等GUI程序时,是无处不在的存在。上文中,我们利用回调技术实现了事件监听器模式,但是该事件监听器模式还不够通用。今天我们从零开始,以不同手机设备使用QQ为应用实例,实现一个通用的事件监听器模式,
一个通用的事件监听模式:事件源(例如:按钮或滚动条)的本身被注册了事件监听器,当事件源发生了一个事件(例如:点击或滚动),它会将该事件的相关信息封装在一个相应类型的事件对象中并以通知的形式发送给所有注册的所有事件监听器对象,事件监听器对象可以对该通知进行响应。
敲黑板
- 事件监听机制包括了事件源、事件监听器、事件对象三个要素。
- 事件源是一个能够注册监听器对象并发送事件对象的对象。
- 当事件源发生操作事件时,它会将事件对象传递给所有注册的监听器。
- 事件监听器对象是一个实现了特定监听接口的类的实例,通常由开发人员自行实现。它将对事件对象进行处理,利用事件对象中的信息决定如何对事件做出响应。
实现一种通用的事件监听器模式
第一步:内置事件名称。将所有的事件名称列表以常量的形式实现在该类中。
代码1:
//内置QQ聊天常用事件名称
public class Event{
//文本聊天事件
public static final String
MSG_CHAT="textChat";
//语音聊天事件
public static final String
VOICE_CHAT="voiceChat";
//视频聊天事件
public static final String
VOIDE_CHAT="voideChat";
//......
}
第二步:封装事件对象。将事件的名称、数据等信息进行封装,这些事件对象在事件源对象中产生,并在监听器对象中进行使用。
代码2:
import java.util.HashMap;
import java.util.Map;
//定义事件对象
//把事件相应属性进行封装为对象
public class EventObject{
//事件名称
protected String name;
//事件属性集合
protected Map<String, Object> properties;
//简单构造函数
public EventObject(String name){
this(name, (Object[]) null);
}
//复杂构造函数
//name:事件名称
//args:属性集合键值对依次排列存放,key1,v1,key2,v2,key3,v3
public EventObject(String name, Object... args){
this.name = name;
properties = new HashMap<String, Object>();
if (args != null){
for (int i = 0; i < args.length; i += 2){
if (args[i + 1] != null){
properties.put(String.valueOf(args[i]), args[i + 1]);
}
}
}
}
//获取事件名称
public String getName(){
return name;
}
//获取事件对象的属性列表
public Map<String, Object> getProperties(){
return properties;
}
//根据特定属性关键字,获取相应属性值
public Object getProperty(String key){
return properties.get(key);
}
}
第三步:定义事件源对象。实现监听器对象在事件源中的注册、注销;实现特定事件名称与监听器对象进行绑定;实现特定事件发生后,通过调用注册的监听器对象的接口函数进行事件响应。
代码3:
import java.util.ArrayList;
import java.util.List;
//定义事件源
//管理监听器注册、注销
//触发事件后告知监听器对象
public class EventSource{
//用于存放该事件源所注册的监听器
//由于特定监听器往往与特定事件相关联
//为此,该数据结构存放的内容设计为:
//[事件名称1,监听器1,事件名称2,监听器2,......]
protected List<Object> eventListeners = null;
//用于存放事件源
protected Object eventSource;
//设置该事件源是否可以触发外部事件标识
protected boolean eventsEnabled = true;
//创建一个简单的事件源
public EventSource(){
this(null);
}
//按照给定的source创建事件源
public EventSource(Object source){
setEventSource(source);
}
//获取事件源对象
public Object getEventSource(){
return eventSource;
}
//设置事件源对象
public void setEventSource(Object value){
this.eventSource = value;
}
//获取该事件源能否触发外部事件响应
public boolean isEventsEnabled(){
return eventsEnabled;
}
//设置该事件源能否触发外部事件响应
public void setEventsEnabled(boolean eventsEnabled){
this.eventsEnabled = eventsEnabled;
}
//1 绑定事件名称与相应的事件监听器,
//这样保证一旦特定事件发生,相应的监听器就被触发执行
//2 如果没有给定事件名称,
//那么该监听器就被注册到所有的事件中
public void addListener(String eventName, IEventListener listener){
if (eventListeners == null){
eventListeners = new ArrayList<Object>();
}
eventListeners.add(eventName);
eventListeners.add(listener);
}
//删除特定的所有事件监听器
public void removeListener(IEventListener listener){
removeListener(listener, null);
}
//如果事件名称不为空,则删除与之绑定的所有事件监听器
//如果事件名称为空,则删除特定的所有事件监听器
public void removeListener(IEventListener listener, String eventName){
if (eventListeners != null){
for (int i = eventListeners.size() - 2; i > -1; i -= 2){
if (eventListeners.get(i + 1) == listener
&& (eventName == null || String.valueOf(
eventListeners.get(i)).equals(eventName))){
eventListeners.remove(i + 1);
eventListeners.remove(i);
}
}
}
}
//以this为事件源,触发监听器对象
public void fireEvent(EventObject evt){
fireEvent(evt, null);
}
//触发所有的绑定了特定事件对象evt的所有监听器
public void fireEvent(EventObject evt, Object sender){
if (eventListeners != null && !eventListeners.isEmpty()
&& isEventsEnabled()){
if (sender == null){
sender = getEventSource();
}
if (sender == null){
sender = this;
}
for (int i = 0; i < eventListeners.size(); i += 2){
String listen = (String) eventListeners.get(i);
if (listen == null || listen.equals(evt.getName())){
((IEventListener) eventListeners.get(i + 1)).response(
sender, evt);
}
}
}
}
}
第四步:自定义事件监听对象。实现监听接口,通过对事件对象进行解析、处理,完成对事件的响应过程。
//自定义Andorid QQ监听对象
//实现了IEventListener监听接口
public class AndroidQQ implements IEventListener {
//对不同的事件进行响应
@Override
public void response(Object sender, EventObject evt) {
//对事件对象进行解析
String msgType = evt.getName();
String msg = "Android设备 => ";
if(msgType.equals(Event.MSG_CHAT)){
msg += evt.getProperty("text");
}else if(msgType.equals(Event.VOICE_CHAT)){
msg += evt.getProperty("voice");
}else if (msgType.equals(Event.VOIDE_CHAT)){
msg += evt.getProperty("voide");
}
System.out.println(msg);
}
}
代码5:
//自定义IPhone QQ监听对象
//实现了IEventListener监听接口
public class IPhoneQQ implements IEventListener {
//对不同的事件进行响应
@Override
public void response(Object sender, EventObject evt) {
//对事件对象进行解析
String msgType = evt.getName();
String msg = "IPhone设备 => ";
if(msgType.equals(Event.MSG_CHAT)){
msg += evt.getProperty("text");
}else if(msgType.equals(Event.VOICE_CHAT)){
msg += evt.getProperty("voice");
}else if (msgType.equals(Event.VOIDE_CHAT)){
msg += evt.getProperty("voide");
}
System.out.println(msg);
}
}
第五步:自定义事件源对象。实现一些事件的发起,封装事件对象,并将事件对象告知注册的事件监听器对象。
代码6:
//自定义QQ聊天事件源
//继承事件源对象EventSource
public class ChatQQ extends EventSource {
//聊天回话名称
private String name;
//构造函数
public ChatQQ(String n){
this.name = n;
}
//发起文字聊天事件
public void textChat(){
EventObject text = new EventObject(
Event.MSG_CHAT,"text","text:Hello world!");
this.fireEvent(text);
}
//发起语音聊天事件
public void voiceChat(){
EventObject voice = new EventObject(
Event.VOICE_CHAT,"voice","voice:Hello world!");
this.fireEvent(voice);
}
//发起视频聊天事件
public void voideChat(){
EventObject voide = new EventObject(
Event.VOIDE_CHAT,"voide","voide:Hello world!");
this.fireEvent(voide);
}
}
第六步:使用场景。将事件与监听事件对象相绑定。可以把相同的事件绑定到不同的事件监听器对象上;同一事件源也可以发起多个事件。
代码7:
//测试客户端
public class TestEvent {
public static void main(String[] args) {
ChatQQ chatQQ = new ChatQQ("QQ会话");
System.out.println("Android QQ与IPhone QQ上线了...");
//可以把相同的事件绑定到不同的事件监听器对象上
chatQQ.addListener(Event.MSG_CHAT,
new IPhoneQQ());
chatQQ.addListener(Event.MSG_CHAT,
new AndroidQQ());
chatQQ.addListener(Event.VOICE_CHAT,
new IPhoneQQ());
chatQQ.addListener(Event.VOICE_CHAT,
new AndroidQQ());
chatQQ.addListener(Event.VOIDE_CHAT,
new IPhoneQQ());
chatQQ.addListener(Event.VOIDE_CHAT,
new AndroidQQ());
//事件源发生不同的事件
chatQQ.textChat();
chatQQ.voiceChat();
chatQQ.voideChat();
}
}
结果:
Android QQ与IPhone QQ上线了...
IPhone设备 => text:Hello world!
Android设备 => text:Hello world!
IPhone设备 => voice:Hello world!
Android设备 => voice:Hello world!
IPhone设备 => voide:Hello world!
Android设备 => voide:Hello world!
温故知新
本文实现了一个通用的事件监听器模式框架,包括了Event(事件名称定义)、IEventListener(事件监听接口)、EventObject(事件对象)、EventSource(事件源);老铁们可以把上述框架直接应用于实际的开发场景中,只需要:
- 在Event中增加自己的事件名称;
- implements接口IEventListener,实现自定义事件监听对象;
- extends类EventSource,实现自定义事件源对象。
通过上述3步,我们就可以很容易应对多事件处理了。降低事件源与事件监听对象之间的耦合,并很容易实现对一个事件的不同监听器对象的不同响应。
转载自公众号:代码荣耀