Cocos2d-x 觸摸節(jié)點

你是不是奇怪為什么 Node、Sprite、Layer等原生節(jié)點不能響應(yīng)觸摸事件,而 ImageView、Layout等GUI 控件就可以?
恩,事實上我也不知道這份設(shè)定的原因。

而實際上,我們經(jīng)常需要原生節(jié)點可以響應(yīng)觸摸,為了實現(xiàn)目的,通常我們會這么做:在節(jié)點上蓋上一層UILayout,讓UIlayout去處理觸摸事件。這么做的好處是處理簡單;壞處是步驟繁瑣,而且還需要管理一個無關(guān)緊要的節(jié)點。
不!能!忍!那就只好自己來寫一套了。

我們看源碼知道,GUI 控件是這么響應(yīng)觸摸事件的:

void Widget::setTouchEnabled(bool enable)
{
    if (enable == _touchEnabled)
    {
        return;
    }
    _touchEnabled = enable;
    if (_touchEnabled)
    {
        _touchListener = EventListenerTouchOneByOne::create();
        CC_SAFE_RETAIN(_touchListener);
        _touchListener->setSwallowTouches(true);
        _touchListener->onTouchBegan = CC_CALLBACK_2(Widget::onTouchBegan, this);
        _touchListener->onTouchMoved = CC_CALLBACK_2(Widget::onTouchMoved, this);
        _touchListener->onTouchEnded = CC_CALLBACK_2(Widget::onTouchEnded, this);
        _touchListener->onTouchCancelled = CC_CALLBACK_2(Widget::onTouchCancelled, this);
        _eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener, this);
    }
    else
    {
        _eventDispatcher->removeEventListener(_touchListener);
        CC_SAFE_RELEASE_NULL(_touchListener);
    }
}

從代碼上看,給加上TouchOneByOne的事件監(jiān)聽就好了,是不是很簡單?

好,那我們就來模仿一下。
為了秉持盡量不改動源碼的宗旨,我們在lua層做修改。
首先,我們需要定義一些會經(jīng)常用到的基礎(chǔ)的參數(shù):_touchBegan、_touchEnded、_touchMoved分別代表觸摸開始、觸摸結(jié)束和觸摸移動事件回調(diào),_isTouchEnabled是觸摸使能標(biāo)識,_touchListener是觸摸事件監(jiān)聽句柄 :

local TouchNode = cc.Node
TouchNode._touchBegan = nil
TouchNode._touchEnded = nil
TouchNode._touchMoved = nil
TouchNode._isTouchEnabled = nil
TouchNode._touchListener = nil

接著,我們開始添加觸摸事件監(jiān)聽:

function TouchNode:enableTouchEvent()
    self:setTouchEnabled(true)
    self:onListenTouchEvent()
end

function TouchNode:disableTouchEvent()
    self:setTouchEnabled(false)
end

function TouchNode:isTouchEnabled()
    return self._isTouchEnabled
end

function TouchNode:setTouchEnabled(var)
    if var == self._isTouchEnabled then
        return 
    end
    self._isTouchEnabled = var and true or false
    return self._isTouchEnabled and self:onListenTouchEvent() or self:unListenTouchEvent()
end

function TouchNode:onListenTouchBegan(callback)
    if type(callback) == 'function' then
        self._touchBegan = callback
    end
end

function TouchNode:onListenTouchEnded(callback)
    if type(callback) == 'function' then
        self._touchEnded = callback
    end
end

function TouchNode:onListenTouchMoved(callback)
    if type(callback) == 'function' then
        self._touchMoved = callback
    end
end

-- 監(jiān)聽觸摸事件響應(yīng)
function TouchNode:onListenTouchEvent()
    local function onTouchBegan(touch, event)
        local isFocus = isTouchFocusNode(touch, self)
        if isFocus then
            if self._touchBegan then
                print('onListenTouchBegan')
                self._touchBegan(self, ccui.TouchEventType.began)
            end
        end
        return isFocus
    end
    local function onTouchEnded(touch, event)
        local isFocus = isTouchFocusNode(touch, self)
        if isFocus then
            if self._touchEnded then
                print('onListenTouchEnded')
                self._touchEnded(self, ccui.TouchEventType.ended)
            end
        end
    end
    local function onTouchMoved(touch, event)
        if self._touchMoved then
            print('onListenTouchMoved')
            self._touchMoved(self, ccui.TouchEventType.moved)
        end
    end

    local listener = cc.EventListenerTouchOneByOne:create()
    listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
    listener:registerScriptHandler(onTouchEnded, cc.Handler.EVENT_TOUCH_ENDED)
    listener:registerScriptHandler(onTouchMoved, cc.Handler.EVENT_TOUCH_MOVED)
    self:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, self)
    self._touchListener = listener
end

-- 設(shè)置是否吞噬響應(yīng)
function TouchNode:setSwallowTouches(var)
    if not self._touchListener then return end
    var = var and true or false
    self._touchListener:setSwallowTouches(false)
end

-- 取消監(jiān)聽
function TouchNode:unListenTouchEvent()
    if not self._touchListener then return end
    self:getEventDispatcher():removeEventListener(self._touchListener)
    self._touchListener = nil
end

注意,isTouchFocusNode 還沒有實現(xiàn),你可能奇怪這是什么鬼?從字面上看,這是一個判斷觸摸點是否在節(jié)點上的方法。那為什么需要這個判斷?因為如果不判斷,觸摸點滿屏幕都是,我們無法確定觸摸點是否被節(jié)點正確接收,因此這步判斷是非常有必要的。

最后,我們就來實現(xiàn)isTouchFocusNode

function isTouchFocusNode(touch, node)
    local touchP = touch:getLocation()
    local bound  = node:getBoundingBox()
    local point  = node:convertTouchToNodeSpaceAR(touch)
    local anchor = node:getAnchorPoint()
    if (point.x >= -bound.width * anchor.x) and (point.x <= bound.width * (1-anchor.x)) and
       (point.y >= -bound.height * anchor.y) and (point.y <= bound.height * (1-anchor.y)) then
        print('Focus ...')
        return true
    end
    return false
end

注意:由于有些Node的BoundingBox為0,因此并不能接收到觸摸事件,這時候需要根據(jù)具體情況調(diào)整節(jié)點的ContentSize。

PS:
源碼中確定控件的觸摸響應(yīng)區(qū)域的具體實現(xiàn)與以上確定節(jié)點的實現(xiàn)是不一樣的,有興趣的可以看看 UIWidget的 hitTest方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,174評論 25 708
  • 在iOS開發(fā)中經(jīng)常會涉及到觸摸事件。本想自己總結(jié)一下,但是遇到了這篇文章,感覺總結(jié)的已經(jīng)很到位,特此轉(zhuǎn)載。作者:L...
    WQ_UESTC閱讀 6,250評論 4 26
  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的?困惑于Cell怎么突然不能點擊了?糾結(jié)于如何實現(xiàn)這個奇葩響應(yīng)需求?亦或是...
    Lotheve閱讀 59,589評論 51 604
  • “愿此時平淡,若彼時燦爛” --張愛玲 01 ...
    無燈閱讀 499評論 5 7
  • 過幾天我妹妹就要回老家讀書,作為大她們12、17歲的大姐要為她們添置點什么,就陪她們折騰一天。 學(xué)會擔(dān)當(dāng),就是愛與...
    愛旅行的Shane閱讀 280評論 0 1

友情鏈接更多精彩內(nèi)容