cocos2d-x (四):触摸事件处理机制

来源:互联网 发布:wget和yum 编辑:程序博客网 时间:2024/05/16 14:38

触摸事件可以分为如下四个状态
开始、移动、结束、取消
cocos2d-x使用EventTouch来表示一个触摸事件
触摸点的状态使用 EventCode来表示 
class CC_DLL EventTouch : public Event{public:    static const int MAX_TOUCHES = 15;        enum class EventCode    {        BEGAN,        MOVED,        ENDED,        CANCELLED    };    EventTouch();    inline EventCode getEventCode() const { return _eventCode; };    inline const std::vector<Touch*>& getTouches() const { return _touches; };


}
根据状态不同,同一个触摸点会经历多次事件分发。

首先介绍事件监听器的2种类型
EventListenerTouchOneByOne 监听的是单点触摸
class CC_DLL EventListenerTouchOneByOne : public EventListener{public:    static const std::string LISTENER_ID;        static EventListenerTouchOneByOne* create();        virtual ~EventListenerTouchOneByOne();        void setSwallowTouches(bool needSwallow);    bool isSwallowTouches();        /// Overrides    virtual EventListenerTouchOneByOne* clone() override;    virtual bool checkAvailable() override;    //public:    typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;    typedef std::function<void(Touch*, Event*)> ccTouchCallback;    ccTouchBeganCallback onTouchBegan;    ccTouchCallback onTouchMoved;    ccTouchCallback onTouchEnded;    ccTouchCallback onTouchCancelled;    CC_CONSTRUCTOR_ACCESS:    EventListenerTouchOneByOne();    bool init();    private:    std::vector<Touch*> _claimedTouches;    bool _needSwallow;        friend class EventDispatcher;};

EventListenerTouchAllAtOnce 监听的就是多点触摸
class CC_DLL EventListenerTouchAllAtOnce : public EventListener{public:    static const std::string LISTENER_ID;        static EventListenerTouchAllAtOnce* create();    virtual ~EventListenerTouchAllAtOnce();        /// Overrides    virtual EventListenerTouchAllAtOnce* clone() override;    virtual bool checkAvailable() override;    //public:    typedef std::function<void(const std::vector<Touch*>&, Event*)> ccTouchesCallback;    ccTouchesCallback onTouchesBegan;    ccTouchesCallback onTouchesMoved;    ccTouchesCallback onTouchesEnded;    ccTouchesCallback onTouchesCancelled;    CC_CONSTRUCTOR_ACCESS:    EventListenerTouchAllAtOnce();    bool init();private:        friend class EventDispatcher;};

可以看到这单点触摸和多点触摸成员变量定义的主要区别
EventListenerTouchOneByOne
    typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
    typedef std::function<void(Touch*, Event*)> ccTouchCallback;
EventListenerTouchAllAtOnce
typedef std::function<void(const std::vector<Touch*>&, Event*)> ccTouchesCallback;

它们都定义了4个回调函数,分别用来处理触摸点的开始、移动、结束及取消4中不同的状态
其中,EventListenerTouchOneByOne里的ccTouchBeganCallback的返回值为bool,因为onTouchBegan是必须实现的,否则将
接收不到任何触摸事件的通知,它的返回值是用来告诉EventDispatcher是否将该触摸点后续的触摸状态传递给订阅者,如果为
fasle,onTouchesMoved、onTouchesEnded、onTouchesCancelled将接收不到任何回调。

setSwallowTouches是用来设置是否阻止一个触摸点向后面的订阅者继续分发的。setSwallowTouches(true)即为阻止,例如通常两个按钮
不能同时处理一个触摸点。
在EventDispaycher内部,每次触摸事件,它首先将每个触摸点单独作用在每个EventListenerTouchOneByOne订阅者上面,然后再将所有触摸点集合作用在每个EventListenerTouchAllAtOnce订阅者上面的

注册订阅者:
可以用如下两种方法来注册一个订阅者
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)  
可以先看下这两种方法的实现方式:
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node){    CCASSERT(listener && node, "Invalid parameters.");    CCASSERT(!listener->isRegistered(), "The listener has been registered.");         if (!listener->checkAvailable())        return;         listener->setSceneGraphPriority(node);    listener->setFixedPriority(0);    listener->setRegistered(true);         addEventListener(listener);} void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority){    CCASSERT(listener, "Invalid parameters.");    CCASSERT(!listener->isRegistered(), "The listener has been registered.");    CCASSERT(fixedPriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority.");         if (!listener->checkAvailable())        return;         listener->setSceneGraphPriority(nullptr);    listener->setFixedPriority(fixedPriority);    listener->setRegistered(true);    listener->setPaused(false);     addEventListener(listener);}  

EventDispatcher分发事件的顺序如下:
priority<0,scene graph(priority=0),priority>0
首先分发优先级小于0的订阅者,按优先级从小到大分发,然后分发所有与node元素关联的订阅者,他们按照自身关联的node在ui场景中的层级从前往后分发,最后分发所有有限级大于0的订阅者。也是按照优先级从小到大的顺序分发。
其中的 addEventListenerWithSceneGraphPriority 的事件监听器优先级是 0 ;而且在 addEventListenerWithFixedPriority 中的事件监听器的优先级不可以设置为 0,因为这个是保留给 SceneGraphPriority 使用的

综上所述,我们来看如何实现各种触摸事件
步骤:新建精灵->新建监听器->按需求实现监听器的4个回调函数(onTouchBegan必须实现)->把精灵作为订阅者添加到监听器中
1.单点触摸例子
  //新建精灵 auto label = LabelTTF::create("click me ","Courier",30); label->setPosition(size.width/2,size.height/2); addChild(label); //新建监听器 auto listener = EventListenerTouchOneByOne::create();  //按需求实现监听器的4个回调函数(onTouchBegan必须实现) listener->onTouchBegan = [label](Touch * t,Event *e){  if(e->getCurrentTarget()->getBoundingBox().containsPoint(t->getLocation())){   log("onTouchBegan");  }  //返回值如果是 false 后面的时间就不会再被触发 返回值是true TouchMoved End会被触发  return false; }; listener->onTouchMoved = [](Touch *t ,Event *e){  log("onTouchMoved"); }; listener->onTouchEnded = [](Touch *t,Event *e){  log("onTouchEnded"); };  //把精灵作为订阅者添加到监听器中 Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,label);


2.多点触摸
  多点触摸实现过程与单点触摸类似
 auto listener = EventListenerTouchAllAtOnce::create(); listener->onTouchesBegan=[](std::vector<Touch*> s,Event *e){  log("onTouchesBegan");  return true; }; listener->onTouchesMoved=[](std::vector<Touch*> s,Event *e) { }; Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,this);


3.加速度传感器
 //加速度传感器默认是关闭的 Device::setAccelerometerEnabled(true); //加速度传感器 Director::getInstance()->getEventDispatcher()->  addEventListenerWithSceneGraphPriority(  EventListenerAcceleration::create([](Acceleration *a,Event *e){   log("x:%g,y:%g,z:%g",a->x,a->y,a->z);  }),this);
4.物理按键
物理按键对手机来说通常就是指返回键,如下便是当手机按下返回键的时候进行的处理
auto listener = EventListenerKeyboard::create();listener->onKeyReleased=[](EventKeyboard::KeyCode code,Event *e){ log("key code: %d",code); switch (code) { case EventKeyboard::KeyCode::KEY_BACKSPACE:  Director::getInstance()->end();  break; default:  break; }};Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,this);
0 0
原创粉丝点击