crossapp 触摸事件 处理机制

之前使用webview的时候一直相对触摸事件了解一下,一直没有去深入理解。导致现在一直不理解触摸事件到底是怎么一回事,今天有时间就来研究一下。

环境:vs2013  + crossapp 1.1.12(目前最新版本)


1.  怎么迅速定位我需要的  触摸事件代码?

a.在研究crossapp源码之前,可以看看这篇博文cocos2d-x学习笔记-触屏事件详解    里面详细讲解了 cocos2dx 触摸事件的来龙去脉,当然也有其他解析cocos2dx 触摸事件的博文,只要你看的舒服就行了。

b. 在libCrossApp里面查找相关 Touch 类信息,至于为什么需要找Touch相关的类,可以看看crossapp的来历。

从源码里面我们可以查找到 下面几个类:CATouchDispatcher   CATouch  CATouchController  CATouchView ,通过类的名字可以很明显的猜测了。

CATouchDispatcher   :触摸事件分发类

 CATouch  :触摸事件类

CATouchDispatcher   :触摸事件控制类

 CATouchView :触摸视图  继承 CAView 

接下来我们只需要在 CATouchDispatcher   CATouchDispatcher    构造函数里面下断点就可以了。

当按下F5的时候,程序会停在CATouchDispatcher 构造函数这里 。上图

crossapp 触摸事件 处理机制_第1张图片


估计这里应该是程序初始化的时候new CATouchDispatcher ,有兴趣可以去看看,具体步骤 点击堆栈帧,弹出一个list视图,从上到下依次是被调用的关系。上图:

crossapp 触摸事件 处理机制_第2张图片


在这里再次感叹vs的强大,很多高大上的功能给开发者减少了多少日夜的调试。

继续F5,程序直接弹出来了。上图

crossapp 触摸事件 处理机制_第3张图片


官方自带demo还是不错了 ,基本上控件都涵盖了。

这时候  手动触发一个事件, 随便点击一下 界面上的控件 ,这时候进入了CATouchController构造函数。我们看看哪里调用了这个函数,

void CATouchDispatcher::touchesBegan(CCSet *touches, CAEvent *pEvent)
{
    CC_RETURN_IF(!isDispatchEvents());
    m_bLocked = true;
    
    CATouch *pTouch;
    CCSetIterator setIter;
    for (setIter = touches->begin(); setIter != touches->end(); setIter++)
    {
        pTouch = (CATouch *)(*setIter);
        
        CATouchController* touchController = new CATouchController();
        touchController->setTouch(pTouch);
        touchController->setEvent(pEvent);
        m_vTouchControllers[pTouch->getID()] = touchController;
        touchController->touchBegan();
    }
    m_bLocked = false;
}

在往上一步看看

void CCEGLViewProtocol::handleTouchesBegin(int num, intptr_t ids[], float xs[], float ys[])
{
    CCSet set;
    for (int i = 0; i < num; ++i)
    {
        intptr_t id = ids[i];
        float x = xs[i];
        float y = ys[i];

        int nUnusedIndex = 0;

        // it is a new touch
        if (s_TouchesIntergerDict.find(id) == s_TouchesIntergerDict.end())
        {
            nUnusedIndex = getUnUsedIndex();

            // The touches is more than MAX_TOUCHES ?
            if (nUnusedIndex == -1)
            {
                CCLOG("The touches is more than MAX_TOUCHES, nUnusedIndex = %d", nUnusedIndex);
                continue;
            }

            CATouch* pTouch = s_pTouches[nUnusedIndex] = new CATouch();
			pTouch->setTouchInfo(nUnusedIndex,
                                 (x - m_obViewPortRect.origin.x) / m_fScaleX,
                                 (y - m_obViewPortRect.origin.y) / m_fScaleY);
            
            
            s_TouchesIntergerDict.insert(std::make_pair(id, nUnusedIndex));
            set.addObject(pTouch);
        }
    }

    if (set.count() == 0)
    {
        CCLOG("touchesBegan: count = 0");
        return;
    }

    m_pDelegate->touchesBegan(&set, NULL);
}

还是看不出什么,那就继续往上看

LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    BOOL bProcessed = FALSE;

    switch (message)
    {
    case WM_LBUTTONDOWN:
#if(_MSC_VER >= 1600)
        // Don't process message generated by Windows Touch
        if (m_bSupportTouch && (s_pfGetMessageExtraInfoFunction() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) break;
#endif /* #if(_MSC_VER >= 1600) */

        if (m_pDelegate && MK_LBUTTON == wParam)
        {
            POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)};
            CCPoint pt(point.x, point.y);
            pt.x /= m_fFrameZoomFactor;
            pt.y /= m_fFrameZoomFactor;
            CCPoint tmp = ccp(pt.x, m_obScreenSize.height - pt.y);
            if (m_obViewPortRect.equals(CCRectZero) || m_obViewPortRect.containsPoint(tmp))
            {
                m_bCaptured = true;
                SetCapture(m_hWnd);
                int id = 0;
                handleTouchesBegin(1, &id, &pt.x, &pt.y);
            }
        }
        break;

这个函数只截取了部分。看到WindowProc这个函数的时候估计心里都开花了 ,WIn32编程里面这个函数一般都具有特定的意义----那就是窗口回调函数。

这下就清楚了 。 

LRESULT CCEGLView::WindowProc() 这个函数将鼠标左键按下事件(当然还有其他事件,不分析) 传输到 void CCEGLViewProtocol::handleTouchesBegin() 里面,然后将
鼠标左键按下事件 封装到 CATouch 类里面。然后CATouchDispatcher函数来处理 CATouch 事件,这个时候代码就到了这里了void CATouchDispatcher::touchesBegan()

这个时候才创建 CATouchController类 ,并且将 CATouch 传进去了 ,需要注意的是  CATouch  里面有一些信息的,比如 location  event  等等。

ok到了这里,两个断点都出现了 。

还是回到CATouchController的断点处, 这时候F11继续跟进,跟着进入touchController->touchBegan()函数里面,看看里面都发生了什么。

void CATouchController::touchBegan()
{
    m_tFirstPoint = m_pTouch->getLocation();
    
    std::vector<CAResponder*> vector;
    
    CAView* view = dynamic_cast<CAView*>(CAApplication::getApplication()->getTouchDispatcher()->getFirstResponder());
    bool isContainsFirstPoint = view && view->convertRectToWorldSpace(view->getBounds()).containsPoint(m_tFirstPoint);
    if (isContainsFirstPoint)
    {
        vector = this->getEventListener(m_pTouch, view);
    }
    else
    {
        vector = this->getEventListener(m_pTouch, CAApplication::getApplication()->getRootWindow());
    }
    
    std::vector<CAResponder*>::iterator itr;
    for (itr=vector.begin(); itr!=vector.end(); itr++)
    {
        CC_CONTINUE_IF(!(*itr)->isPriorityScroll());
        CC_CONTINUE_IF(!(*itr)->isScrollEnabled());
        CC_CONTINUE_IF(!(*itr)->isHorizontalScrollEnabled() && !(*itr)->isVerticalScrollEnabled());
        m_vTouchMovedsViewCache.pushBack((*itr));
    }
    m_vTouchesViews.pushBack(vector.back());   //这里需要注意 将vector最后一个元素 添加到了 m_vTouchesViews 里面了 !

    
    if (!m_vTouchMovedsViewCache.empty())
    {
        CAScheduler::schedule(schedule_selector(CATouchController::passingTouchesViews), this, 0, 0, 0.05f);
    }
    else
    {
        this->passingTouchesViews();           //这里进去就快开始进入事件处理了
    }
}

单步跟进 发现程序会 经过这里
vector = this->getEventListener(m_pTouch, CAApplication::getApplication()->getRootWindow());
这看起来是最重要的代码了 ,可能进去之后就得到了我们想要的东西。赶紧进入看看

std::vector<CAResponder*> CATouchController::getEventListener(CATouch* touch, CAView* view)
{
    CAResponder* responder = view;<span style="white-space:pre">		</span>//主视图
    
    std::vector<CAResponder*> vector;
    
    do
    {
        vector.push_back(responder);           //视图进入vector里面

        CAResponder* lastResponder = NULL;
        
        if (CAView* view = dynamic_cast<CAView*>(responder))
        {
            if (view->getViewDelegate())            //查看是否有触摸回调函数
            {
                lastResponder = view->nextResponder();
            }
            else
            {
                                                        //程序一般会进入这里  
                CAVector<CAView*>::const_reverse_iterator itr;    //for循环很重要
                for (itr=view->CAView::getSubviews().rbegin();
                     itr!=view->CAView::getSubviews().rend(); itr++)
                {
                    CAView* subview = *itr;
                    if (subview->isVisible() && subview->isTouchEnabled())
                    {
                        CCPoint point = subview->convertTouchToNodeSpace(touch);
                        
                        if (subview->getBounds().containsPoint(point))
                        {
                            lastResponder = subview;                  //如果最后一个视图的视图局域包含了 鼠标点击(触摸事件)的点位置 ,将
                            break;                                    //将视图push_back到vector里面 ,继续循环。
                        }
                    }
                }
            }
        }
        else if (CAViewController* viewController = dynamic_cast<CAViewController*>(responder))
        {
            CAVector<CAView*>::const_reverse_iterator itr;
            for (itr=viewController->getView()->CAView::getSubviews().rbegin();
                 itr!=viewController->getView()->CAView::getSubviews().rend();
                 itr++)
            {
                CAView* subview = *itr;
                if (subview->isVisible() && subview->isTouchEnabled())
                {
                    //CC_BREAK_IF(!subview->isTouchEnabled());
                    
                    CCPoint point = subview->convertTouchToNodeSpace(touch);
                    
                    if (subview->getBounds().containsPoint(point))
                    {
                        lastResponder = subview;
                        break;
                    }
                }
            }
        }
        
        responder = lastResponder;
    }
    while (responder);
    
    return vector;
}

我在代码里面注释了一下 ,简单一点讲就是将 每个 “容器” 的视图 的cbegin排序的视图找到,然后返回vector。

这里提到了容器的概念 , 这里的容器一般是指 一个CAView 里面含有多个 CAView视图的类,比如常用的控制器CADrawerController CATabBarController CADrawerController ,也可能有 其余的控件类,因为有些控件类我还没有研究过,所以不下定论。

回到 touchBegan函数 ,得到 视图的 vector 之后 ,将 最后一个视图(也就是可能处理事件的控件类或者控制器类) 添加到  m_vTouchesViews

然后进入passingTouchesViews里面查看一下

void CATouchController::passingTouchesViews(float dt)
{
    CAView* view = dynamic_cast<CAView*>(CAApplication::getApplication()->getTouchDispatcher()->getFirstResponder());
    bool isContainsFirstPoint = view && view->convertRectToWorldSpace(view->getBounds()).containsPoint(m_tFirstPoint);
    if (!isContainsFirstPoint && view)
    {
        view->ccTouchBegan(m_pTouch, m_pEvent);
    }
    
    CC_RETURN_IF(m_vTouchesViews.empty());
    
    CAResponder* responder = m_vTouchesViews.front();
    while (responder->nextResponder())
    {
        responder = responder->nextResponder();
        m_vTouchesViews.pushBack(responder);
    }
    
    for (int i=0; i<m_vTouchesViews.size();)
    {
        if (!m_vTouchesViews.at(i)->ccTouchBegan(m_pTouch, m_pEvent))
        {
            m_vTouchesViews.erase(i);
        }
        else
        {
            i++;
        }
    }
}

看到  ccTouchBegan 终于看到了终点 ,但是上面的while循环是个什么鬼东西 ,只有进去看看了 。

CAResponder* CAView::nextResponder()
{
    if (!m_bHaveNextResponder)
    {
        return NULL;
    }
    
    if (m_pViewDelegate)
    {
        return dynamic_cast<CAResponder*>(m_pViewDelegate);
    }
    return this->getSuperview();
}

原来是没有 对事件处理就直接抛给上一级视图(也可能是控制器)处理。

分析结束了 。





你可能感兴趣的:(crossapp 触摸事件 处理机制)