在开发一些窗口层次比复杂的cocos2d项目时,会发现一些由于没有窗口层次而引起的bug。这些bug让一些从windows平台过来的人觉得很无奈。比如,我们有一个列表控件,我们在其中放了一些菜单,当我们滑动列表控件使菜单选项(称为A)滑出列表控件的可视范围时,按理我们是无法再点击到A的,因为它滚动出了父控件可视范围,不被用户所看到。但是cocos2d的默认情况是能点击到的,这是因为cocos2d的消息管理是优先级消息机制,只要控件登记接收消息,那么cocos2d会一如既往的发给他。所以我们刚才讲的情形在cocos2d看来,它无法根据A被遮挡而不给A发消息。究其根本,是没有一个层级窗口消息机制(当然你能通过其他的方法帮助cocos2d,但我个人觉得有点不够彻底)。

我想建立一个相对完整cocos2d的的层级窗口消息机制,且不改变cocos2d任何源码(因为改变源码的话,不知道以后升级起来是不是很麻烦)。基本思路有如下几条:

  1. 在任何一个场景中,我们会有一个最底层的Layer(我称为祖层),这个Layer将接受cocos2d的触摸消息。并且这个Layer能将触摸消息传递给其所有的子控件。
  2. 一个场景中除了祖层之外,所有其他的控件都将不接受任何触摸消息,其触摸消息的来源于父控件。当然任何非叶子控件都会将其触摸消息传递给其子控件。
  3. 控件将消息传递给子控件时,如果子控件消息处理函数返回真,则记录该子控件,以方便将后续消息传递给这个子控件。
  4. 尽可能兼容已知和未知的cocos2d控件库,这样在这个机制类我们会有更多好用的子控件。
  5. 提供模式和非模式两种窗口模式。就像我们在用windows时,点新建文件,会弹出一个模式窗口,不关闭它,我们无法和其他窗口交互。
下面看一下类的组织架构图:
我们分别说一下各个类的大体作用:
  1. wmTouchDelegate并非继承于ccTouchDelegate或其他类(但消息处理函数名同ccTouchDelegate一样)。它是一个消息传播大使,所有继承于该类的类都能自动地将消息传播到所有的子控件。不继承于ccTouchDelegate主要出于设计原则中避免多重继承中的基类重复。
  2. wmLayer继承于wmTouchDelegate和CCLayer(图中未指出),负责将消息处理函数转接到wmTouchDelegate的消息处理函数,这是因为wmTouchDelegate的消息处理函数同ccTouchDelegate是一样的,而CCLayer已经继承了ccTouchDelegate,这样如果不显示的转接处理函数,C++编译器会提示有两个版本选择的错误。
  3. wmLayerAncestor继承于wmLayer,这样它已经具备了消息转发的功能。同时它接受触摸消息。并注册了优先级为0且吞并消息的touch target。这样它将是上面提到的祖层,一个Scene中只有一个祖层。
  4. wmLayerDescendant继承于wmLayer,这样它已经具备了消息转发功能,同时它屏蔽了消息触摸消息。一个scene中多数层都应该是继承与wmLayerDescendant的。
  5. wmLayerModal继承于wmLayerAncestor,所以它接受触摸消息,为了达到模式窗口的功能(程序中当有模式窗口时,只有最顶层的模式窗口能接受到消息,其他层都屏蔽),我们将wmLayerModal注册为优先级-128且吞并消息的窗口。
通过这个类层次,我实现了在cocos2d-x 2.0.4中的CCTableView中放多层元素,且元素滚动出View可视范围无法点中的功能,且点击中菜单时,TableView依旧能滚动的功能(因为原来的CCTableView中放CCMenu的话,一旦点击中菜单,就无法拖动TableView了,因为CCMenu把消息吞了)。且可与CCControl等cocos控件一同使用。
下面来看源码:
  1. wmTouchDelegate:
    wmTouchDelegate.h

    [cpp] view plaincopyprint?
    1. //
    2. //  Created by jason on 12-12-25.
    3. //
    4. //
    5. #ifndef __TableTest__wmTouchDelegate__
    6. #define __TableTest__wmTouchDelegate__
    7. #include <iostream>
    8. #include "cocos2d.h"
    9. USING_NS_CC;
    10. #define WM_TOUCH_DELEGATE_OWNER_CONSTRUCTOR_IN_HEAD_FILE( ownerClassName ) \
    11. ownerClassName() : wmTouchDelegate( this ){}
    12. #define WM_TOUCH_DELEGATE_IMPLEMENT_IN_HEAD_FILE()                      \
    13. virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)             \
    14. {                                                                       \
    15. return wmTouchDelegate::ccTouchBegan( pTouch, pEvent );             \
    16. }                                                                       \
    17. virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)             \
    18. {                                                                       \
    19. wmTouchDelegate::ccTouchMoved( pTouch, pEvent );                    \
    20. }                                                                       \
    21. virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)             \
    22. {                                                                       \
    23. wmTouchDelegate::ccTouchEnded( pTouch, pEvent );                    \
    24. }                                                                       \
    25. virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)         \
    26. {                                                                       \
    27. wmTouchDelegate::ccTouchCancelled( pTouch, pEvent );                \
    28. }
    29. //window message mechanism
    30. //We only change the TargetedTouch message mechansim.
    31. //Node who want standard touch message should register all by itself.
    32. class wmTouchDelegate
    33. {
    34. public:
    35. wmTouchDelegate( CCNode* pOwner ) :
    36. m_pOwner( pOwner ),
    37. m_bDraging( false )
    38. {
    39. m_pItemsClaimTouch = CCArray::createWithCapacity( CHILD_MAX );
    40. assert( m_pItemsClaimTouch );
    41. m_pItemsClaimTouch->retain();
    42. }
    43. virtual ~wmTouchDelegate()
    44. {
    45. CC_SAFE_RELEASE_NULL( m_pItemsClaimTouch );
    46. }
    47. protected:
    48. // default implements are used to call script callback if exist
    49. virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
    50. virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
    51. virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
    52. virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
    53. private:
    54. //return value:
    55. //true: pParent is touched by user
    56. //false: pParent isn't touched by user.
    57. bool passMessage( CCNode* pParent, CCTouch *pTouch, CCEvent *pEvent );
    58. //if has significant touch handler.
    59. bool hasNonTrivalTouchHandler( cocos2d::CCNode *pItem );
    60. private:
    61. CCNode* m_pOwner;
    62. bool m_bDraging;
    63. //items claim touch message
    64. CCArray* m_pItemsClaimTouch;
    65. //store all class whose touch handler is non-trival
    66. static const void* m_pNonTrivalTouchHandlerClasses[];
    67. };#endif /* defined(__TableTest__wmTouchDelegate__) */
    //
    //  Created by jason on 12-12-25.
    //
    //
    #ifndef __TableTest__wmTouchDelegate__
    #define __TableTest__wmTouchDelegate__
    #include <iostream>
    #include "cocos2d.h"
    USING_NS_CC;
    #define WM_TOUCH_DELEGATE_OWNER_CONSTRUCTOR_IN_HEAD_FILE( ownerClassName ) \
    ownerClassName() : wmTouchDelegate( this ){}
    #define WM_TOUCH_DELEGATE_IMPLEMENT_IN_HEAD_FILE()                      \
    virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)             \
    {                                                                       \
    return wmTouchDelegate::ccTouchBegan( pTouch, pEvent );             \
    }                                                                       \
    virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)             \
    {                                                                       \
    wmTouchDelegate::ccTouchMoved( pTouch, pEvent );                    \
    }                                                                       \
    virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)             \
    {                                                                       \
    wmTouchDelegate::ccTouchEnded( pTouch, pEvent );                    \
    }                                                                       \
    virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)         \
    {                                                                       \
    wmTouchDelegate::ccTouchCancelled( pTouch, pEvent );                \
    }
    //window message mechanism
    //We only change the TargetedTouch message mechansim.
    //Node who want standard touch message should register all by itself.
    class wmTouchDelegate
    {
    public:
    wmTouchDelegate( CCNode* pOwner ) :
    m_pOwner( pOwner ),
    m_bDraging( false )
    {
    m_pItemsClaimTouch = CCArray::createWithCapacity( CHILD_MAX );
    assert( m_pItemsClaimTouch );
    m_pItemsClaimTouch->retain();
    }
    virtual ~wmTouchDelegate()
    {
    CC_SAFE_RELEASE_NULL( m_pItemsClaimTouch );
    }
    protected:
    // default implements are used to call script callback if exist
    virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
    private:
    //return value:
    //true: pParent is touched by user
    //false: pParent isn't touched by user.
    bool passMessage( CCNode* pParent, CCTouch *pTouch, CCEvent *pEvent );
    //if has significant touch handler.
    bool hasNonTrivalTouchHandler( cocos2d::CCNode *pItem );
    private:
    CCNode* m_pOwner;
    bool m_bDraging;
    //items claim touch message
    CCArray* m_pItemsClaimTouch;
    //store all class whose touch handler is non-trival
    static const void* m_pNonTrivalTouchHandlerClasses[];
    };#endif /* defined(__TableTest__wmTouchDelegate__) */
    
  2. [cpp] view plaincopyprint?
    1. //
    2. //  wmTouchDelegate.cpp
    3. //  TableTest
    4. //
    5. //  Created by jason on 12-12-25.
    6. //
    7. //
    8. #include "wmTouchDelegate.h"
    9. #include "cocos-ext.h"
    10. #include "wmControl.h"//include wmTableView,wmMenu,wmControl
    11. USING_NS_CC_EXT;
    12. const void* wmTouchDelegate::m_pNonTrivalTouchHandlerClasses[ ] =
    13. {
    14. &typeid( wmTableView ),
    15. &typeid( wmMenu ),
    16. &typeid( wmControlButton ),
    17. };
    18. #pragma mark- input touche
    19. bool wmTouchDelegate::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
    20. {
    21. //pass message to all children
    22. return passMessage( m_pOwner, pTouch, pEvent );
    23. }
    24. void wmTouchDelegate::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
    25. {
    26. //special process for menu, we won't pass ccTouchMoved message to menu. Because we think menu doesn't need ccTouchMoved message in ios device where user always want to dray layer instead menu. The fllowing block for menu will only go once.
    27. if( false == m_bDraging )
    28. {
    29. for( int i = 0; i < m_pItemsClaimTouch->count(); )
    30. {
    31. CCLayer* pItem = ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i );
    32. //menu items doesn't process ccTouchMove(), cancel it.
    33. assert( NULL != pItem );
    34. //if it's menu
    35. if( dynamic_cast< CCMenu* >( pItem ) )
    36. {
    37. pItem->ccTouchCancelled( pTouch, pEvent );
    38. m_pItemsClaimTouch->removeObjectAtIndex( i );
    39. }
    40. else
    41. {
    42. ++i;
    43. }
    44. }
    45. }
    46. //pass ccTouchMoved message to un-CCMenu item
    47. int iNumItemsNotMenu = m_pItemsClaimTouch->count();
    48. for( int i = 0; i < iNumItemsNotMenu; ++i )
    49. {
    50. wmTouchDelegate* pItemWindowMessage = NULL;
    51. CCLayer* pItem = NULL;
    52. pItem = ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i );
    53. assert( NULL != pItem );
    54. //window message items
    55. if( ( pItemWindowMessage = dynamic_cast< wmTouchDelegate* >( pItem ) ) )
    56. {
    57. pItemWindowMessage->ccTouchMoved( pTouch, pEvent );
    58. }
    59. else//coscos2d-x items
    60. {
    61. pItem->ccTouchMoved( pTouch, pEvent );
    62. }
    63. }
    64. m_bDraging = true;
    65. }
    66. void wmTouchDelegate::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
    67. {
    68. int iNumItems = m_pItemsClaimTouch->count();
    69. for( int i = 0; i < iNumItems; ++i )
    70. {
    71. wmTouchDelegate* pItemWindowMessage = NULL;
    72. CCLayer* pItem = NULL;
    73. pItem = ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i );
    74. assert( NULL != pItem );
    75. //window message items
    76. if( ( pItemWindowMessage = dynamic_cast< wmTouchDelegate* >( pItem ) ) )
    77. {
    78. pItemWindowMessage->ccTouchEnded( pTouch, pEvent );
    79. }
    80. else//coscos2d-x items
    81. {
    82. pItem->ccTouchEnded( pTouch, pEvent );
    83. }
    84. }
    85. m_pItemsClaimTouch->removeAllObjects();
    86. m_bDraging = false;
    87. }
    88. void wmTouchDelegate::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)
    89. {
    90. int iNumItems = m_pItemsClaimTouch->count();
    91. for( int i = 0; i < iNumItems; ++i )
    92. {
    93. wmTouchDelegate* pItemWindowMessage = NULL;
    94. CCLayer* pItem = NULL;
    95. pItem = ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i );
    96. assert( NULL != pItem );
    97. //window message items
    98. if( ( pItemWindowMessage = dynamic_cast< wmTouchDelegate* >( pItem ) ) )
    99. {
    100. pItemWindowMessage->ccTouchCancelled( pTouch, pEvent );
    101. }
    102. else//coscos2d-x items
    103. {
    104. pItem->ccTouchCancelled( pTouch, pEvent );
    105. }
    106. }
    107. m_pItemsClaimTouch->removeAllObjects();
    108. m_bDraging = false;
    109. }
    110. bool wmTouchDelegate::passMessage( CCNode* pParent, CCTouch *pTouch, CCEvent *pEvent )
    111. {
    112. if( !pParent || !pParent->isVisible() )
    113. {
    114. return false;
    115. }
    116. CCPoint pt;
    117. CCRect rcBoundingBox;
    118. //hande message to items
    119. int iNumChildren = 0;
    120. CCArray* pChildren = NULL;
    121. //if the item'size > 1, check whether use touches it. Such as TableView.
    122. //some items doesn't get size. they are medium for maintaining some children. Such as CCTableViewCell.
    123. if( pParent->getContentSize().width * pParent->getContentSize().height > 1.0f )
    124. {
    125. pt = pParent->convertTouchToNodeSpace( pTouch );
    126. rcBoundingBox.setRect( 0, 0, pParent->getContentSize().width, pParent->getContentSize().height );
    127. //whether hit the node
    128. if( !rcBoundingBox.containsPoint( pt ) )
    129. {
    130. return false;
    131. }
    132. }
    133. pChildren = pParent->getChildren();
    134. //no children, but use touch this item, so return true.
    135. if( !pChildren )
    136. {
    137. return true;
    138. }
    139. iNumChildren = pParent->getChildren()->count();
    140. //pass to all children
    141. for( int iChildIndex = 0; iChildIndex < iNumChildren; ++iChildIndex )
    142. {
    143. //if the item claims the touch message
    144. bool bClaim = false;
    145. wmTouchDelegate* pItemWindowMessage = NULL;
    146. CCNode* pItem = NULL;
    147. pItem = ( CCNode*  )( pChildren->objectAtIndex( iChildIndex ) );
    148. assert( pItem );
    149. //items derives from wmTouchDelegate
    150. if( ( pItemWindowMessage = dynamic_cast< wmTouchDelegate* >( pItem ) ) )
    151. {
    152. bClaim = pItemWindowMessage->ccTouchBegan( pTouch, pEvent );
    153. }
    154. else//items doesn't derive from wmTouchDelegate
    155. {
    156. //classes have non-trival ccTouchX() hander
    157. if( hasNonTrivalTouchHandler( pItem ) )
    158. {
    159. bClaim = ( ( CCLayer* ) pItem )->ccTouchBegan( pTouch, pEvent );
    160. }
    161. //items who doesn't derive from wmTouchDelegate can't pass touch message to its children,
    162. //so we have to help them to pass touch message.
    163. passMessage( pItem, pTouch, pEvent );
    164. }
    165. //if this item is interested in this message, add it to array for other messages
    166. if( bClaim )
    167. {
    168. m_pItemsClaimTouch->addObject( pItem );
    169. }
    170. }
    171. return true;
    172. }
    173. bool wmTouchDelegate::hasNonTrivalTouchHandler( cocos2d::CCNode *pItem )
    174. {
    175. //classes have non-trival ccTouchX() hander
    176. const void* pItemAddress = &typeid( *pItem );
    177. for ( int i = 0; i < sizeof( m_pNonTrivalTouchHandlerClasses ) / sizeof( void* ); ++i)
    178. {
    179. if( m_pNonTrivalTouchHandlerClasses[ i ] == pItemAddress )
    180. {
    181. return true;
    182. }
    183. }
    184. return false;
    185. }
    //
    //  wmTouchDelegate.cpp
    //  TableTest
    //
    //  Created by jason on 12-12-25.
    //
    //
    #include "wmTouchDelegate.h"
    #include "cocos-ext.h"
    #include "wmControl.h"//include wmTableView,wmMenu,wmControl
    USING_NS_CC_EXT;
    const void* wmTouchDelegate::m_pNonTrivalTouchHandlerClasses[ ] =
    {
    &typeid( wmTableView ),
    &typeid( wmMenu ),
    &typeid( wmControlButton ),
    };
    #pragma mark- input touche
    bool wmTouchDelegate::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
    {
    //pass message to all children
    return passMessage( m_pOwner, pTouch, pEvent );
    }
    void wmTouchDelegate::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
    {
    //special process for menu, we won't pass ccTouchMoved message to menu. Because we think menu doesn't need ccTouchMoved message in ios device where user always want to dray layer instead menu. The fllowing block for menu will only go once.
    if( false == m_bDraging )
    {
    for( int i = 0; i < m_pItemsClaimTouch->count(); )
    {
    CCLayer* pItem = ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i );
    //menu items doesn't process ccTouchMove(), cancel it.
    assert( NULL != pItem );
    //if it's menu
    if( dynamic_cast< CCMenu* >( pItem ) )
    {
    pItem->ccTouchCancelled( pTouch, pEvent );
    m_pItemsClaimTouch->removeObjectAtIndex( i );
    }
    else
    {
    ++i;
    }
    }
    }
    //pass ccTouchMoved message to un-CCMenu item
    int iNumItemsNotMenu = m_pItemsClaimTouch->count();
    for( int i = 0; i < iNumItemsNotMenu; ++i )
    {
    wmTouchDelegate* pItemWindowMessage = NULL;
    CCLayer* pItem = NULL;
    pItem = ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i );
    assert( NULL != pItem );
    //window message items
    if( ( pItemWindowMessage = dynamic_cast< wmTouchDelegate* >( pItem ) ) )
    {
    pItemWindowMessage->ccTouchMoved( pTouch, pEvent );
    }
    else//coscos2d-x items
    {
    pItem->ccTouchMoved( pTouch, pEvent );
    }
    }
    m_bDraging = true;
    }
    void wmTouchDelegate::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
    {
    int iNumItems = m_pItemsClaimTouch->count();
    for( int i = 0; i < iNumItems; ++i )
    {
    wmTouchDelegate* pItemWindowMessage = NULL;
    CCLayer* pItem = NULL;
    pItem = ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i );
    assert( NULL != pItem );
    //window message items
    if( ( pItemWindowMessage = dynamic_cast< wmTouchDelegate* >( pItem ) ) )
    {
    pItemWindowMessage->ccTouchEnded( pTouch, pEvent );
    }
    else//coscos2d-x items
    {
    pItem->ccTouchEnded( pTouch, pEvent );
    }
    }
    m_pItemsClaimTouch->removeAllObjects();
    m_bDraging = false;
    }
    void wmTouchDelegate::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)
    {
    int iNumItems = m_pItemsClaimTouch->count();
    for( int i = 0; i < iNumItems; ++i )
    {
    wmTouchDelegate* pItemWindowMessage = NULL;
    CCLayer* pItem = NULL;
    pItem = ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i );
    assert( NULL != pItem );
    //window message items
    if( ( pItemWindowMessage = dynamic_cast< wmTouchDelegate* >( pItem ) ) )
    {
    pItemWindowMessage->ccTouchCancelled( pTouch, pEvent );
    }
    else//coscos2d-x items
    {
    pItem->ccTouchCancelled( pTouch, pEvent );
    }
    }
    m_pItemsClaimTouch->removeAllObjects();
    m_bDraging = false;
    }
    bool wmTouchDelegate::passMessage( CCNode* pParent, CCTouch *pTouch, CCEvent *pEvent )
    {
    if( !pParent || !pParent->isVisible() )
    {
    return false;
    }
    CCPoint pt;
    CCRect rcBoundingBox;
    //hande message to items
    int iNumChildren = 0;
    CCArray* pChildren = NULL;
    //if the item'size > 1, check whether use touches it. Such as TableView.
    //some items doesn't get size. they are medium for maintaining some children. Such as CCTableViewCell.
    if( pParent->getContentSize().width * pParent->getContentSize().height > 1.0f )
    {
    pt = pParent->convertTouchToNodeSpace( pTouch );
    rcBoundingBox.setRect( 0, 0, pParent->getContentSize().width, pParent->getContentSize().height );
    //whether hit the node
    if( !rcBoundingBox.containsPoint( pt ) )
    {
    return false;
    }
    }
    pChildren = pParent->getChildren();
    //no children, but use touch this item, so return true.
    if( !pChildren )
    {
    return true;
    }
    iNumChildren = pParent->getChildren()->count();
    //pass to all children
    for( int iChildIndex = 0; iChildIndex < iNumChildren; ++iChildIndex )
    {
    //if the item claims the touch message
    bool bClaim = false;
    wmTouchDelegate* pItemWindowMessage = NULL;
    CCNode* pItem = NULL;
    pItem = ( CCNode*  )( pChildren->objectAtIndex( iChildIndex ) );
    assert( pItem );
    //items derives from wmTouchDelegate
    if( ( pItemWindowMessage = dynamic_cast< wmTouchDelegate* >( pItem ) ) )
    {
    bClaim = pItemWindowMessage->ccTouchBegan( pTouch, pEvent );
    }
    else//items doesn't derive from wmTouchDelegate
    {
    //classes have non-trival ccTouchX() hander
    if( hasNonTrivalTouchHandler( pItem ) )
    {
    bClaim = ( ( CCLayer* ) pItem )->ccTouchBegan( pTouch, pEvent );
    }
    //items who doesn't derive from wmTouchDelegate can't pass touch message to its children,
    //so we have to help them to pass touch message.
    passMessage( pItem, pTouch, pEvent );
    }
    //if this item is interested in this message, add it to array for other messages
    if( bClaim )
    {
    m_pItemsClaimTouch->addObject( pItem );
    }
    }
    return true;
    }
    bool wmTouchDelegate::hasNonTrivalTouchHandler( cocos2d::CCNode *pItem )
    {
    //classes have non-trival ccTouchX() hander
    const void* pItemAddress = &typeid( *pItem );
    for ( int i = 0; i < sizeof( m_pNonTrivalTouchHandlerClasses ) / sizeof( void* ); ++i)
    {
    if( m_pNonTrivalTouchHandlerClasses[ i ] == pItemAddress )
    {
    return true;
    }
    }
    return false;
    }           

    其中比较重要的地方解释一下:

    1.首先是m_bDragging是为了判断拖动用的,主要用于菜单处理,当点击了一下之后,菜单对此消息感兴趣,我们记录菜单,并传递后续消息,然后点击完之后传来的是TouchMoved消息,那么就停止菜单消息处理。

    2.passMessage同ccTouchBegan一起来完成消息递归传递。passMessage还将帮助没有继承wmTouchDelegate的类(这些类的ccTouchBegan不具备消息传递功能)传递消息给其子控件。

    3.hasNonTrivalTouchHandler()函数是用于判断哪些类具有重要消息处理函数的,这些类都是没有继承wmTouchDelegate,我们在帮助它把消息传递给子控件的同时,也让它调用自己的消息处理函数来完成自己的操作,这里目前只加了wmTableView,wmMenu这两个类,他们都是简单地继承ccTableView,ccMenu,并屏蔽触摸消息(因为任何加入窗口层级消息体制的控件都应该只能从父控件那里获得消息)。当然通过typeid来设计是并不好的,扩展性差,当添加一个新控件进去我们都要往这个函数中添加一个判断。但是由于我们基于不修改cocos2d源码的考虑,目前只能这么做。如果想做彻底,那么我们将会从ccNode那里开始设计。

  3. wmLayer, wmLayerAncestor, wmLayerDescendant, wmLayerModal都在一个头文件中完成,没有cpp。因为主要功能都有wmTouchDelegate完成了,这些类只是做了简单功能和约束的的添加。
    wmLayer.h

    [cpp] view plaincopyprint?
    1. //
    2. //  Created by jason on 12-12-21.
    3. //
    4. //
    5. #ifndef __TableTest__WMLayer__
    6. #define __TableTest__WMLayer__
    7. #include <iostream>
    8. #include "cocos2d.h"
    9. #include "wmTouchDelegate.h"
    10. USING_NS_CC;
    11. #define WM_INIT_DEFAULT( parentClassName )              \
    12. virtual bool init()                                     \
    13. {                                                       \
    14. if( !parentClassName::init() )                      \
    15. {                                                   \
    16. return false;                                   \
    17. }                                                   \
    18. \
    19. return true;                                        \
    20. }
    21. #define WM_TOUCH_REGISTER_DEFAULT( iPriority )          \
    22. virtual void registerWithTouchDispatcher( void  )       \
    23. {                                                       \
    24. CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate( this, iPriority, true );\
    25. }
    26. //Users shouldn't derive from wmLayerAncestor, wmLayerDescendant, wmLayerModal instead wmLayer.
    27. //wmLayer can't be touched.
    28. //wmLayer pass message to it's all descendant.
    29. class wmLayer : public CCLayer, public wmTouchDelegate
    30. {
    31. protected:
    32. WM_INIT_DEFAULT( CCLayer );
    33. WM_TOUCH_DELEGATE_OWNER_CONSTRUCTOR_IN_HEAD_FILE( wmLayer );
    34. WM_TOUCH_DELEGATE_IMPLEMENT_IN_HEAD_FILE();
    35. //static
    36. public:
    37. CREATE_FUNC( wmLayer );
    38. };
    39. //wmLayerAncestor can be touched.
    40. //all secene should have only one wmLayerAncestor for bottom layer.
    41. //all the other layer should be wmLayerDescendant.
    42. class wmLayerAncestor : public wmLayer
    43. {
    44. protected:
    45. bool virtual init()
    46. {
    47. if( !wmLayer::init() )
    48. {
    49. return false;
    50. }
    51. setTouchEnabled( true );
    52. return true;
    53. }
    54. WM_TOUCH_REGISTER_DEFAULT( 0 );
    55. //static
    56. public:
    57. CREATE_FUNC( wmLayerAncestor );
    58. //data
    59. };
    60. class wmLayerDescendant : public wmLayer
    61. {
    62. protected:
    63. WM_INIT_DEFAULT( wmLayer );
    64. virtual void registerWithTouchDispatcher(){};
    65. //static
    66. public:
    67. CREATE_FUNC( wmLayerDescendant );
    68. //data
    69. };
    70. //wmLayerModal stopes touch messages from being passed to other layers which are not it's children.
    71. //And will pass touch messages to it's children normally.
    72. class wmLayerModal : public wmLayerAncestor
    73. {
    74. protected:
    75. WM_INIT_DEFAULT( wmLayerAncestor );
    76. WM_TOUCH_REGISTER_DEFAULT( kCCMenuHandlerPriority );
    77. //static
    78. public:
    79. CREATE_FUNC( wmLayerModal );
    80. //data
    81. };
    82. #endif /* defined(__TableTest__WMLayer__) */
    //
    //  Created by jason on 12-12-21.
    //
    //
    #ifndef __TableTest__WMLayer__
    #define __TableTest__WMLayer__
    #include <iostream>
    #include "cocos2d.h"
    #include "wmTouchDelegate.h"
    USING_NS_CC;
    #define WM_INIT_DEFAULT( parentClassName )              \
    virtual bool init()                                     \
    {                                                       \
    if( !parentClassName::init() )                      \
    {                                                   \
    return false;                                   \
    }                                                   \
    \
    return true;                                        \
    }
    #define WM_TOUCH_REGISTER_DEFAULT( iPriority )          \
    virtual void registerWithTouchDispatcher( void  )       \
    {                                                       \
    CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate( this, iPriority, true );\
    }
    //Users shouldn't derive from wmLayerAncestor, wmLayerDescendant, wmLayerModal instead wmLayer.
    //wmLayer can't be touched.
    //wmLayer pass message to it's all descendant.
    class wmLayer : public CCLayer, public wmTouchDelegate
    {
    protected:
    WM_INIT_DEFAULT( CCLayer );
    WM_TOUCH_DELEGATE_OWNER_CONSTRUCTOR_IN_HEAD_FILE( wmLayer );
    WM_TOUCH_DELEGATE_IMPLEMENT_IN_HEAD_FILE();
    //static
    public:
    CREATE_FUNC( wmLayer );
    };
    //wmLayerAncestor can be touched.
    //all secene should have only one wmLayerAncestor for bottom layer.
    //all the other layer should be wmLayerDescendant.
    class wmLayerAncestor : public wmLayer
    {
    protected:
    bool virtual init()
    {
    if( !wmLayer::init() )
    {
    return false;
    }
    setTouchEnabled( true );
    return true;
    }
    WM_TOUCH_REGISTER_DEFAULT( 0 );
    //static
    public:
    CREATE_FUNC( wmLayerAncestor );
    //data
    };
    class wmLayerDescendant : public wmLayer
    {
    protected:
    WM_INIT_DEFAULT( wmLayer );
    virtual void registerWithTouchDispatcher(){};
    //static
    public:
    CREATE_FUNC( wmLayerDescendant );
    //data
    };
    //wmLayerModal stopes touch messages from being passed to other layers which are not it's children.
    //And will pass touch messages to it's children normally.
    class wmLayerModal : public wmLayerAncestor
    {
    protected:
    WM_INIT_DEFAULT( wmLayerAncestor );
    WM_TOUCH_REGISTER_DEFAULT( kCCMenuHandlerPriority );
    //static
    public:
    CREATE_FUNC( wmLayerModal );
    //data
    };
    #endif /* defined(__TableTest__WMLayer__) */
    

    里面通过一些宏,让我编写的更快一些。但希望没能阻碍你阅读。这里面没有太多需要说的。

整个体制是如何做到消息递归传递的呢?
答:任何一个wmLayer对象或者派生自wmLayer的对象,将记录每个直接子控件(对象的第一层子控件),并试图沿每个直接子控件路径逐层向下记录所有非直接子控件(不是这个对象的第一层子控件),直到路径上碰到继承自wmTouchDelegate子控件,我们称它为X(也记录这个)。这个子控件X将负责记录它的所有子控件(按照上面的方法,任何一个wmLaye...)。
也正是通过这个方法,我们能让一些没有继承wmTouchDelegate的控件也能加入这个体系,其消息传递功能由离它最近的上层继承自wmTouchDelegate控件来完成。
扩展提示:
  • 如果你想让其他控件加入这个体制,简单的方法继承这些控件并屏蔽他们接受消息,我们的体制会确保消息能传递给他们身上的所有子控件。如wmMenu,wmTableView,wmControl,代码如下:

    [cpp] view plaincopyprint?
    1. class wmTableView : public CCTableView
    2. {
    3. public:
    4. static wmTableView* create(cocos2d::extension::CCTableViewDataSource *dataSource, cocos2d::CCSize size, CCNode *container = NULL )
    5. {
    6. wmTableView *table = new wmTableView();
    7. table->initWithViewSize(size, container);
    8. table->autorelease();
    9. table->setDataSource(dataSource);
    10. table->_updateContentSize();
    11. return table;
    12. }
    13. void registerWithTouchDispatcher(void){}
    14. };
    15. //this menu won't get any touch message becase it doesn't register target touches.
    16. class wmMenu : public CCMenu
    17. {
    18. public:
    19. virtual void registerWithTouchDispatcher()
    20. {
    21. }
    22. static wmMenu * create(CCMenuItem* item, ...)
    23. {
    24. va_list args;
    25. va_start(args,item);
    26. wmMenu *pRet = new wmMenu();
    27. if (pRet && pRet->initWithItems(item, args))
    28. {
    29. pRet->autorelease();
    30. va_end(args);
    31. return pRet;
    32. }
    33. va_end(args);
    34. CC_SAFE_DELETE(pRet);
    35. return NULL;
    36. }
    37. };
    38. class wmControlButton : public CCControlButton
    39. {
    40. public:
    41. WM_TOUCH_REGISTER_EMPTY();
    42. static wmControlButton* create(CCNode* label, CCScale9Sprite* backgroundSprite)
    43. {
    44. wmControlButton *pRet = new wmControlButton();
    45. pRet->initWithLabelAndBackgroundSprite(label, backgroundSprite);
    46. pRet->autorelease();
    47. return pRet;
    48. }
    49. };
    class wmTableView : public CCTableView
    {
    public:
    static wmTableView* create(cocos2d::extension::CCTableViewDataSource *dataSource, cocos2d::CCSize size, CCNode *container = NULL )
    {
    wmTableView *table = new wmTableView();
    table->initWithViewSize(size, container);
    table->autorelease();
    table->setDataSource(dataSource);
    table->_updateContentSize();
    return table;
    }
    void registerWithTouchDispatcher(void){}
    };
    //this menu won't get any touch message becase it doesn't register target touches.
    class wmMenu : public CCMenu
    {
    public:
    virtual void registerWithTouchDispatcher()
    {
    }
    static wmMenu * create(CCMenuItem* item, ...)
    {
    va_list args;
    va_start(args,item);
    wmMenu *pRet = new wmMenu();
    if (pRet && pRet->initWithItems(item, args))
    {
    pRet->autorelease();
    va_end(args);
    return pRet;
    }
    va_end(args);
    CC_SAFE_DELETE(pRet);
    return NULL;
    }
    };
    class wmControlButton : public CCControlButton
    {
    public:
    WM_TOUCH_REGISTER_EMPTY();
    static wmControlButton* create(CCNode* label, CCScale9Sprite* backgroundSprite)
    {
    wmControlButton *pRet = new wmControlButton();
    pRet->initWithLabelAndBackgroundSprite(label, backgroundSprite);
    pRet->autorelease();
    return pRet;
    }
    };
    

  • 但如果想更好的优化消息记录,应该避免一个wmLayer上面放N多层 非本体制控件(即没有继承wmTouchDelegate的控件)。可以在其中穿插一些本体制的控件,因为这些控件具有消息传递功能。如果创建本体制控件呢?

    1.第一种情况,类对象已经继承了CCLayer,且没有自己重要的消息处理函数。如CCLayerColor,我们想把它变成本体系控件,我们应该继承CCLayerColor,并继承wmTouchDelegate。然后转接消         息处理函数。如下代码:

    [cpp] view plaincopyprint?
    1. class wmLayerColor : public CCLayerColor, public wmTouchDelegate
    2. {
    3. protected:
    4. WM_TOUCH_DELEGATE_OWNER_CONSTRUCTOR_IN_HEAD_FILE( wmLayerColor );
    5. WM_TOUCH_DELEGATE_IMPLEMENT_IN_HEAD_FILE();
    6. WM_INIT_DEFAULT( CCLayerColor );
    7. //static
    8. public:
    9. CREATE_FUNC( wmLayerColor );
    10. };
    class wmLayerColor : public CCLayerColor, public wmTouchDelegate
    {
    protected:
    WM_TOUCH_DELEGATE_OWNER_CONSTRUCTOR_IN_HEAD_FILE( wmLayerColor );
    WM_TOUCH_DELEGATE_IMPLEMENT_IN_HEAD_FILE();
    WM_INIT_DEFAULT( CCLayerColor );
    //static
    public:
    CREATE_FUNC( wmLayerColor );
    };

    这是因为这类对象已经继承了CCLayer,我们不能让它再继承wmLayerDescendant,因为wmLayerDescendant也继承自ccLayer,这样多重继承中会有多份CCLayer,比较危险。所以我们简单的继承自wmTouchDelegate就可以了。

    2.第二种情况,我们自己创造新型控件,如我们要在商店放一些物品,每个物品都是有一套共同的UI(物品图片,物品名称,价钱,购买数量,购买按钮),我们可以写     一个控件,然后重用这个控件将会让商店编程更简单。此时我们就让这个新控件继承自wmLayerDescendant即可。其他就不用管了。

    [cpp] view plaincopyprint?
    1. class wmUISellItem : public wmLayerDescendant
    2. {
    3. //macro
    4. public:
    5. enum
    6. {
    7. ITEM_NAME_LENGTH = 20,
    8. WIDTH = 219,
    9. HEIGHT = 141,
    10. };
    11. //method
    12. protected:
    13. bool init();
    14. //static
    15. public:
    16. static wmUISellItem* Create( const char* pszItemName, int iItemPriceNormal, int iItemPriceVIP, int iItemAmount );
    17. };
    class wmUISellItem : public wmLayerDescendant
    {
    //macro
    public:
    enum
    {
    ITEM_NAME_LENGTH = 20,
    WIDTH = 219,
    HEIGHT = 141,
    };
    //method
    protected:
    bool init();
    //static
    public:
    static wmUISellItem* Create( const char* pszItemName, int iItemPriceNormal, int iItemPriceVIP, int iItemAmount );
    };
  • 对多触摸点消息感兴趣的控件,需自行注册standardTouchDelegate。我们没分将多触摸点消息转化成单触摸点消息传播,因为多触摸点消息更适合一次性接受处理,而不是分批传递。
最后,上述代码在写文章时做过手动修改,所以可能存在书写错误。如若使用需自行改正。这里面一定存在一些不足,还请指出改正,感激不尽。

cocos2d-x 建立自己的层级窗口消息机制 .相关推荐

  1. 非阻塞模式WinSock编程入门(Socket关联窗口消息机制)

    本文版权归 CSDN trcj 所有,转载请自觉按如下方式于明显位置标明原作者及出处,以示尊重! 作者:trcj 原文:http://blog.csdn.net/trcj1/archive/2010/ ...

  2. 窗口消息——Windows核心编程学习手札之二十六

    窗口消息 --Windows核心编程学习手札之二十六 Windows允许一个进程至多建立10000个不同类型的用户对象(user object):图符.光标.窗口类.菜单.加速键表等,当一个线程调用一 ...

  3. 《MFC游戏开发》笔记二 建立工程、调整窗口

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9300383 作者:七十一雾央 新浪微博:http:// ...

  4. 窗口封装类与Windows窗口实例的关系-3、CWnd如何处理窗口消息

    在窗口消息处理方面,CWnd使用了窗口子类化和消息映射机制,关于消息映射的知识将在第9章详述,下面着重阐述CWnd是如何应用子类化处理窗口消息的.其实,在6.2节的示例中,CBaseWnd已经使用了与 ...

  5. VC++ Hook截取鼠标点击窗口消息的问题!全局钩子

    VC++ Hook截取鼠标点击窗口消息的问题!全局钩子,高手进! 悬赏分:0 - 提问时间: 2010年05月19日 06时20分 我在做毕业设计,现在遇到一个问题,像求教高手: 我想实现如下功能,当 ...

  6. C#通过Windows API捕获窗,获取窗口文本(FindWindow、GetWindowText),附录:Windows窗口消息大全、Windows API大全

    文章目录 一.前言 二.使用Spy++工具分析窗口 三.C#通过Windows API捕获窗口,获取窗口文本 四.附录:Windows窗口消息 五.Windows API大全 1.API之网络函数 2 ...

  7. Win32窗口机制和消息机制整体流程

    [ 摘要] 本节课通过一张图,详细的介绍了Windows的窗口机制和消息机制整体流程,让你一目了然. Windows系统,是窗口作为基础的系统,以消息机制作为运转机制的系统.我们学习Windows编程 ...

  8. VC6系统托盘类|崩溃自动重建图标|HOOK窗口消息|气泡效果更新SDK

    解决制作托盘气泡提示中遇到的'NIF_INFO' : undeclared identifier VC6下实现托盘气泡提示的关键是要更新SDK,实际上应该就是要更新SHELLAPI.h,SHELL32 ...

  9. WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口

    原文:WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/powertoolsteam/ar ...

最新文章

  1. Qt编译PX4源码,参考如下进行配置
  2. 特斯拉上海超级工厂开工 预计今夏完成初期建设...
  3. 3 上传分段_32式太极拳教材分段教学:【3】32式太极拳背向演示 .3/25.
  4. html鼠标滑过带音效,HTML5带音效的交互式日食动画
  5. 用 IIS 进行ASP.NET 成员/角色管理(1):安全和配置概述
  6. bs4.BeautifulSoup获取outerHTML和innerHTML
  7. 洛谷P5706 【深基2.例8】再分肥宅水__C++描述
  8. 明安图(卡特兰)数(及其扩展 折线法)
  9. 随身WiFi刷百度直连
  10. 页面性能优化办法有哪些?
  11. WiFi模块种类一:单WiFi功能单频WiFi模块
  12. python 进阶案例_Python 进阶内容整理
  13. 螃蟹WiFi驱动中的Band Steering功能
  14. python测试开发实战_自动化平台测试开发:Python测试开发实战
  15. Java程序员应该如何提升自己呢
  16. Sencha的Eclipse插件提示和技巧
  17. AXI_Lite 总线详解
  18. 计算机控制课程设计pid控制,自动化计算机控制课程设计参考题目
  19. win10安装chocolatey及使用
  20. 数组按照中文名称排序

热门文章

  1. 打开keil工程遇到了Loading PDSC Debug Description failed for
  2. ai智能写诗你了解吗?
  3. VM虚拟机开防检测 [ 游戏不能登录处理详解 ]
  4. uni-app获取位置信息(经纬度转换地址信息)
  5. 单片机 rs232 c语言,单片机与RS232串口通信C51程序
  6. 急性子的人,不妨来点“佛系”的心态
  7. 案例:倒计时 js小案例
  8. 原生js实现带左右箭头可滑动的tab效果
  9. java ca校验服务器证书,添加 CA 签名服务器证书和可信 CA 证书
  10. HTML5期末大作业:榆林子州网站设计——榆林子州-含论文(6页) 榆林子州旅游网页HTML代码 学生网页设计与制作期末作业下载 大学生网页设计与制作成品下载 DW旅行网页作业