原文連結:http://blog.csdn.net/zhangxaochen/article/details/8064010
今天看別人 cocos2d 代碼的時候發現個怪現象, 整個工程裡面 沒有 setTouchEnabled(true) 這樣的代碼, 但是程式跑起來的時候卻可以響應 觸屏事件。於是下斷點跟蹤啊跟蹤,發現 m_bIsTouchEnabled 這個變數確實是 false。。 那麼這個layer 到底是怎麼響應觸摸事件的呢?
存在即合理,空穴不來風。經過別人提醒, 意識到原因在這裡:
virtual void onEnter(){ CCLayer::onEnter(); d->getTouchDispatcher()->addTargetedDelegate(this, 0, false); }
類內覆寫了 onEnter 函數,並且 addTargetedDelegate , 居然就可以不用 setTouchEnabled 了。 很有意思的樣子, 那麼不得不看一下 setTouchEnabled 是怎麼工作的了。轉到 CCLayer.cpp line:151,看到函數實現:
/// isTouchEnabled settervoid CCLayer::setTouchEnabled(bool enabled){ if (m_bIsTouchEnabled != enabled) { m_bIsTouchEnabled = enabled; if (m_bIsRunning) { if (enabled) { this->registerWithTouchDispatcher(); } else { // have problems? CCDirector* pDirector = CCDirector::sharedDirector(); pDirector->getTouchDispatcher()->removeDelegate(this); } } }}
這個setTouchEnabled 函數做兩件事情:
1. 賦值 m_bIsTouchEnabled = enabled
2. if(m_bIsRunning){ if (enabled) this->registerWithTouchDispatcher(); else removeDelegate(this); }
注意: 這裡首先判斷 if( m_bIsRunning),就是說,如果在初始化 函數init 裡面設定 setTouchEnabled(true), 這時候還沒 run起來,所以並不會執行這一段。否則這裡 register一次, onEnter 裡面預設再 register一次程式就會出問題了
那麼 registerWithTouchDispatcher() 幹什麼事情呢?
跳轉到定義可以看到:
void CCLayer::registerWithTouchDispatcher(){ CCDirector* pDirector = CCDirector::sharedDirector(); if (m_pScriptHandlerEntry) { if (m_pScriptHandlerEntry->isMultiTouches()) { pDirector->getTouchDispatcher()->addStandardDelegate(this, 0); LUALOG("[LUA] Add multi-touches event handler: %d", m_pScriptHandlerEntry->getHandler()); } else { pDirector->getTouchDispatcher()->addTargetedDelegate(this, m_pScriptHandlerEntry->getPriority(), m_pScriptHandlerEntry->getSwallowsTouches()); LUALOG("[LUA] Add touch event handler: %d", m_pScriptHandlerEntry->getHandler()); } return; } pDirector->getTouchDispatcher()->addStandardDelegate(this,0);}
除去if (m_pScriptHandlerEntry) 那一段不管(似乎是用來控制 script 代碼的行為,我也不大懂), 函數主要做的事情就是一句話:
pDirector->getTouchDispatcher()->addStandardDelegate(this,0);
就是說,預設情況下, CCLayer 註冊的是 standardDelegate。
理順一下, 我們的代碼應該:
1. init 函數裡面設定 setTouchEnabled(true);
2. 類內覆寫函數 registerWithTouchDispatcher, 設定 addStandardDelegate 或者 targetedDelegate,
這樣,當初始化時調用 setTouchEnabled(true) 的時候,首先會設定 m_bIsTouchEnabled 為 true, 由於 m_bIsRunning==false, 這時還不會 registerWithTouchDispatcher。 然後程式run起來, 調用 onEnter 函數的時候,這裡會(見 CCLayer.cpp line:233):
if (m_bIsTouchEnabled) { this->registerWithTouchDispatcher(); }
如果沒有 setTouchEnabled, 沒有覆寫 registerWithTouchDispatcher,而是直接覆寫的 onEnter函數,像本文一開始提到的:
virtual void onEnter(){ CCLayer::onEnter(); d->getTouchDispatcher()->addTargetedDelegate(this, 0, false); }
會怎樣?會節省代碼嗎?不會。因為 m_bIsTouchEnabled==false, 所以 onExit 的時候, CCLayer::onExit() 並不會 removeDelegate, 所以我們還需要手動再覆寫 onExit 函數,像這樣:
void TestLayer::onExit(){ CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this); CCLayer::onExit();}
同時, 因為 m_bIsTouchEnabled==false, 當你注意到這裡的時候你總會陷入邏輯混亂: 沒有 enable touch 這個類居然也可以響應觸摸!! 就像我看人家代碼一樣鬱悶。什麼JB事嘛。。
原文連結:http://blog.csdn.net/zhangxaochen/article/details/8064010
{{OVER}}