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


触摸事件可以分为如下四个状态
开始、移动、结束、取消
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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值