fairygui的cocos2dx类源码分析:UIEventDispatcher类

本文详细介绍了Cocos2d-x框架中的UIEventDispatcher类,它是事件调度器,负责事件监听的添加、删除及事件的派遣。UIEventDispatcher继承自Ref类,具备内存管理机制。类内部使用EventCallbackItem结构体存储事件回调信息,并通过std::vector管理这些回调。文章还展示了如何添加、删除事件监听,以及如何进行事件的派遣和冒泡处理。同时,UIEventDispatcher提供了判断事件是否正在派遣的功能。

继承Ref类,相当于给这个类加上了cocos2dx的内存管理机制

class UIEventDispatcher : public cocos2d::Ref

private成员变量:

_dispatching:一个判断标志,判断自身是否正在派遣事件。在构造类实例时默认初始为0.
EventCallbackItem:callBack事件回调函数。eventType事件类型,tag事件标记(EventTag类),dispatching事件是否正在派遣的标志

	struct EventCallbackItem
	{
        EventCallback callback;
        int eventType;
        EventTag tag;
        int dispatching;
    };
    std::vector<EventCallbackItem*> _callbacks;
    int _dispatching;

EventCallback一个参数为EventContext* context返回为空的函数指针

typedef std::function<void(EventContext* context)> EventCallback;

EventContext
事件上下文类:成员变量

//绑定要派送这个上下文的事件调度器(即触发这个事件的object)
cocos2d::Ref* _sender;
//inputEvent输入事件类的一些信息(触摸事件的位置,按键事件的键盘,点击事件的次数等等)
InputEvent* _inputEvent;
//事件的数据value
cocos2d::Value _dataValue;
//指向事件数据的指针
void* _data;
//冒泡事件,是否停止传播这个事件,用于判断是否向父节点继续传播
bool _isStopped;
//是否默认阻止,作为派遣事件的返回,做一些派遣事件的后续处理。目前来看,在GObject的touchMove里进行DragStart事件,以及slider进行Changed事件有判断
bool _defaultPrevented;
//捕获触摸事件的判断,1为捕获触摸事件,2为释放触摸事件
int _touchCapture;
//事件类型
int _type;
friend class UIEventDispatcher;

EventTag类注意有个value值,isNone是value是否0的判断就行,其他函数就是EventTag的构造和一些操作符的重载

class EventTag
{
public:
	//...
	//...
    bool isNone() const { return _value == 0; }
private:
    uintptr_t _value;
};

public成员函数:

构造和析构:

UIEventDispatcher::UIEventDispatcher() :_dispatching(0)
{
}

UIEventDispatcher::~UIEventDispatcher()
{
    _dispatching = 0;
    removeEventListeners();
}

根据事件类型添加事件监听:
如果有tag标记的监听者,则判断是否更新旧的监听回调。否则增加新的监听者
无tag的监听者直接加入

void addEventListener(int eventType, const EventCallback& callback) { return addEventListener(eventType, callback, EventTag::None); }
void UIEventDispatcher::addEventListener(int eventType, const EventCallback& callback, const EventTag& tag)
{
    if (!tag.isNone())
    {
        for (auto it = _callbacks.begin(); it != _callbacks.end(); it++)
        {
            if ((*it)->eventType == eventType && (*it)->tag == tag)
            {
                (*it)->callback = callback;
                return;
            }
        }
    }
    EventCallbackItem* item = new EventCallbackItem();
    item->callback = callback;
    item->eventType = eventType;
    item->tag = tag;
    item->dispatching = 0;
    _callbacks.push_back(item);
}

根据事件类型删除事件监听

void removeEventListener(int eventType) { removeEventListener(eventType, EventTag::None); }

有tag则删除tag对应的监听,无tag则删除所有tag为None的监听
如果当前正在派遣事件,则把监听回调置空,否则直接删除监听

void UIEventDispatcher::removeEventListener(int eventType, const EventTag& tag)
{
    if (_callbacks.empty())
        return;

    for (auto it = _callbacks.begin(); it != _callbacks.end(); )
    {
        if ((*it)->eventType == eventType && ((*it)->tag == tag || tag.isNone()))
        {
            if (_dispatching > 0)
            {
                (*it)->callback = nullptr;
                it++;
            }
            else
            {
                delete (*it);
                it = _callbacks.erase(it);
            }
        }
        else
            it++;
    }
}

删除全部监听者,与单个删除同理

void UIEventDispatcher::removeEventListeners()
{
    if (_callbacks.empty())
        return;

    if (_dispatching > 0)
    {
        for (auto it = _callbacks.begin(); it != _callbacks.end(); ++it)
            (*it)->callback = nullptr;
    }
    else
    {
        for (auto it = _callbacks.begin(); it != _callbacks.end(); it++)
            delete (*it);
        _callbacks.clear();
    }
}

判断是否有对应的事件类型监听
比较简单,没啥说的

bool hasEventListener(int eventType) const { return hasEventListener(eventType, EventTag::None); }
bool hasEventListener(int eventType, const EventTag& tag) const;

事件派遣
设置事件上下文类的数据:设置自身为派送这个事件的调度器和事件的类型。_activeProcessor是InputProcessor静态成员,用来判断有无输入事件(触摸,按键,点击等等),事件的数据

bool UIEventDispatcher::dispatchEvent(int eventType, void* data, const Value& dataValue)
{
    if (_callbacks.size() == 0)
        return false;

    EventContext context;
    context._sender = this;
    context._type = eventType;
    if (InputProcessor::_activeProcessor)
        context._inputEvent = InputProcessor::_activeProcessor->getRecentInput();
    context._dataValue = dataValue;
    context._data = data;
    doDispatch(eventType, &context);
    return context._defaultPrevented;
}

进行事件的派遣:doDispatch

void UIEventDispatcher::doDispatch(int eventType, EventContext* context)
{
    retain();
    //设置正在派遣的数量
    _dispatching++;
    context->_sender = this;
    //是否有需要删除的监听者
    bool hasDeletedItems = false;
    size_t cnt = _callbacks.size(); //dont use iterator, because new item would be added in callback.
    for (size_t i = 0; i < cnt; i++)
    {
        EventCallbackItem* ci = _callbacks[i];
        //事件回调为空的,需要删除无用的监听者
        if (ci->callback == nullptr)
        {
            hasDeletedItems = true;
            continue;
        }
        //对应事件类型的监听者触发回调
        if (ci->eventType == eventType)
        {
            ci->dispatching++;
            //重置触摸捕获
            context->_touchCapture = 0;
            //触发回调
            ci->callback(context);
            ci->dispatching--;
            //判断是否捕获触摸,并且自身为GObject对象,则设置自身为该触摸事件的监听之一(触摸事件是TouchInfo类,有个vector成员变量,储存这个触摸事件的监听者)
            if (context->_touchCapture != 0 && dynamic_cast<GObject*>(this))
            {
            	//捕获触摸监听者
                if (context->_touchCapture == 1 && eventType == UIEventType::TouchBegin)
                    context->getInput()->getProcessor()->addTouchMonitor(context->getInput()->getTouchId(), dynamic_cast<GObject*>(this));
                //释放触摸监听者
                else if (context->_touchCapture == 2)
                    context->getInput()->getProcessor()->removeTouchMonitor(dynamic_cast<GObject*>(this));
            }
        }
    }
    _dispatching--;
    //判断是否需要删除无用监听者,并删除
    if (hasDeletedItems && _dispatching == 0)
    {
        for (auto it = _callbacks.begin(); it != _callbacks.end(); )
        {
            if ((*it)->callback == nullptr)
            {
                delete (*it);
                it = _callbacks.erase(it);
            }
            else
                it++;
        }
    }
    release();
}

根据事件类型进行冒泡派遣
与单独派送类似,只是不用设置派送事件的对象

bool UIEventDispatcher::bubbleEvent(int eventType, void* data, const Value& dataValue)
{
    EventContext context;
    if (InputProcessor::_activeProcessor)
        context._inputEvent = InputProcessor::_activeProcessor->getRecentInput();
    context._type = eventType;
    context._dataValue = dataValue;
    context._data = data;
    doBubble(eventType, &context);
    return context._defaultPrevented;
}
void UIEventDispatcher::doBubble(int eventType, EventContext* context)
{
    //parent maybe disposed in callbacks
    //获取父节点的弱引用
    WeakPtr wptr(((GObject*)this)->findParent());
    if (!_callbacks.empty())
    {
    	//重置冒泡标志
        context->_isStopped = false;
        //进行自身事件回调
        doDispatch(eventType, context);
        if (context->_isStopped)
            return;
    }
    GObject* p = wptr.ptr();
    //如果回调中父节点还在,则调用父节点冒泡派遣
    if (p)
        p->doBubble(eventType, context);
}

判断某类型事件是否正在派遣

bool UIEventDispatcher::isDispatchingEvent(int eventType)
{
    for (auto it = _callbacks.begin(); it != _callbacks.end(); ++it)
    {
        if ((*it)->eventType == eventType)
            return (*it)->dispatching > 0;
    }
    return false;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值