1、概述
2、四种点击
1、函数回调
- // a selector callback
- void menuCloseCallback(Object* pSender);
- auto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png",
- CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
- void HelloWorld::menuCloseCallback(Object* pSender)
- {
- Director::getInstance()->end();
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
- exit(0);
- #endif
- }
// a selector callback void menuCloseCallback(Object* pSender); auto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this)); void HelloWorld::menuCloseCallback(Object* pSender) { Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif }
其中CC_CALLBACK_1宏是将函数与对象绑定在一起,1表示这个函数有一个参数。当点击这个按钮时,会调用这个回调函数。
除了基于c++11的这个形式的改变,使用方法与先前相同。
2、Layer的touch消息响应
虽然重写了底层的dispatch,但对这层的使用影响并不大。我们同样需要重写:
- //单点响应
- virtual bool onTouchBegan(Touch* touch, Event *event) override;
- virtual void onTouchMoved(Touch* touch, Event *event) override;
- virtual void onTouchEnded(Touch* touch, Event *event) override;
- virtual void onTouchCancelled(Touch *touch, Event *event) override;
- //多点响应
- virtual bool onTouchesBegan(Touch* touch, Event *event) override;
- virtual void onTouchesMoved(Touch* touch, Event *event) override;
- virtual void onTouchesEnded(Touch* touch, Event *event) override;
- virtual void onTouchesCancelled(Touch *touch, Event *event) override;
//单点响应 virtual bool onTouchBegan(Touch* touch, Event *event) override; virtual void onTouchMoved(Touch* touch, Event *event) override; virtual void onTouchEnded(Touch* touch, Event *event) override; virtual void onTouchCancelled(Touch *touch, Event *event) override; //多点响应 virtual bool onTouchesBegan(Touch* touch, Event *event) override; virtual void onTouchesMoved(Touch* touch, Event *event) override; virtual void onTouchesEnded(Touch* touch, Event *event) override; virtual void onTouchesCancelled(Touch *touch, Event *event) override;
重写这些函数来对layer的点击做处理。当然,我们需要:
- setTouchEnabled(true)。
setTouchEnabled(true)。
此外有个小改动。对于单点触控响应,可以调用:
- //设置为单点响应
- setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
- //设置为多点响应(默认)
- setTouchMode(Touch::DispatchMode::ALL_AT_ONCE);
//设置为单点响应 setTouchMode(Touch::DispatchMode::ONE_BY_ONE); //设置为多点响应(默认) setTouchMode(Touch::DispatchMode::ALL_AT_ONCE);
进行设置,而不需要再用设置Delegate的方式来做了。
3、TouchEvent响应
- //声明
- void touchButton(Object* object,TouchEventType type);
- //挂接到控件上
- uiButton->addTouchEventListener(this,toucheventselector(HelloWorld::touchButton));
- //实现
- void HelloWorld::touchButton(Object* object,TouchEventType type)
- {
- LabelTTF* label;
- switch (type)
- {
- case TouchEventType::TOUCH_EVENT_BEGAN:
- label = static_cast<LabelTTF*>(getChildByTag(11));
- label->setString("按下按钮");
- break;
- case TouchEventType::TOUCH_EVENT_MOVED:
- label = static_cast<LabelTTF*>(getChildByTag(11));
- label->setString("按下按钮移动");
- break;
- case TouchEventType::TOUCH_EVENT_ENDED:
- label = static_cast<LabelTTF*>(getChildByTag(11));
- label->setString("放开按钮");
- break;
- case TouchEventType::TOUCH_EVENT_CANCELED:
- label = static_cast<LabelTTF*>(getChildByTag(11));
- label->setString("取消点击");
- break;
- default:
- break;
- }
- }
//声明 void touchButton(Object* object,TouchEventType type); //挂接到控件上 uiButton->addTouchEventListener(this,toucheventselector(HelloWorld::touchButton)); //实现 void HelloWorld::touchButton(Object* object,TouchEventType type) { LabelTTF* label; switch (type) { case TouchEventType::TOUCH_EVENT_BEGAN: label = static_cast<LabelTTF*>(getChildByTag(11)); label->setString("按下按钮"); break; case TouchEventType::TOUCH_EVENT_MOVED: label = static_cast<LabelTTF*>(getChildByTag(11)); label->setString("按下按钮移动"); break; case TouchEventType::TOUCH_EVENT_ENDED: label = static_cast<LabelTTF*>(getChildByTag(11)); label->setString("放开按钮"); break; case TouchEventType::TOUCH_EVENT_CANCELED: label = static_cast<LabelTTF*>(getChildByTag(11)); label->setString("取消点击"); break; default: break; } }
因为所有的UIWidget都要添加到UILayer上,而UILayer通常都会在最上层,所以可以“基本上”认为这种使用方式会优先于其他方式处理点击消息。因为UILayer也会有层级的改变,比如它和MenuItem之间的关系。所以说“基本上”。
4、Listener消息响应方式
这种实现也是新加入的。它更像是点击的一个层次过滤器。点击时,在listener队里中进行过滤。每一个listener检查自己保存的touch消息响应是否会被触发。一层一层过滤,最后在到Layer的touch消息响应。
我觉得它的设计的初衷是为任意sprite提供一套自己制定的点击响应。但这样的实现仍然要写很多条件判断,没有能够控件化。可能我的理解有些偏差,欢迎讨论。
具体的用法大致是这样:
- auto dispatcher = EventDispatcher::getInstance();
- auto myListener = EventListenerTouch::create(Touch::DispatchMode::ONE_BY_ONE);
- //如果不加入此句消息依旧会向下传递
- myListener->setSwallowTouches(true);
- myListener->onTouchBegan = [=](Touch* touch,Event* event)
- {
- //some check
- if (pass)
- {
- return true;
- }
- return false;
- };
- myListener->onTouchMoved = [=](Touch* touch,Event* event)
- {
- //do something
- };
- myListener->onTouchEnded = [=](Touch* touch,Event* event)
- {
- //do something
- };
- dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite1);
- dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite2);
auto dispatcher = EventDispatcher::getInstance(); auto myListener = EventListenerTouch::create(Touch::DispatchMode::ONE_BY_ONE); //如果不加入此句消息依旧会向下传递 myListener->setSwallowTouches(true); myListener->onTouchBegan = [=](Touch* touch,Event* event) { //some check if (pass) { return true; } return false; }; myListener->onTouchMoved = [=](Touch* touch,Event* event) { //do something }; myListener->onTouchEnded = [=](Touch* touch,Event* event) { //do something }; dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite1); dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite2);
其原理是在dispatcher中检查listener列表,例如myListener或加进来的其他listener。然后每个listener检查自己中的Item看能否达到检查条件,例如:mySprite1,mySprite2。然后执行相应的操作。但这样的话,当控件很多的时候,每一次事件都进行这种双链表的检查操作不知会不会影响些性能?
3、实例
光说不练假把式,于是就动手写了一个上面的Demo:
点击背景区域可以移动整个场景,点击蓝色小方块可以半透明移动它,点击蓝色按钮可以更改文字,显示状态。点击右下角按钮退出程序。
4、总结
根据不同的交互需要,选择不同的实现方式,会更有利于我们的维护和扩展,相应例子可以在下面下载。
Demo下载:http://download.csdn.net/detail/fansongy/6399291 不要资源分,觉得好劳烦点下 “顶” ~
本篇博客出自阿修罗道,转载请注明出处,禁止用于商业用途:http://blog.csdn.net/fansongy/article/details/12716671