上一篇我们简单接触了关于单点触摸的实现。
这一篇我们继续进一步的理解单点触摸的事件分发的优先级问题。
我们来回顾一下实现触摸的过程:
1.创建新的监听器;
2.为新的监听器分配回调函数(而我们在上一篇中用到了Lamda表达式来实现);
3.注册分发监听器。
好,这一篇就是来说说关于分发监听器这一步。
在这之前也需要明白一些在上一篇单点触摸没谈到的细节问题。
第一点,触摸事件的接受问题。
触摸事件的传递一般是穿透的,也就是我们点击了屏幕,那么所有被分发的监听器均会收到对应的触摸消息。我们会在触摸回调函数注意到,这个回调函数的返回值是Bool类型的,也就是说在接收到触摸消息之前会做一次判断,如果接受了消息我们会返回true,否则为false。
第二点,就是下面这句:
listener->setSwallowTouches(true);
这一句是在创建监听器之后加的,代表着当前最先接受的监听器事件对象,如果在接受到了触摸事件之后,会覆盖比其优先级低的所有监听器对象的监听事件。
覆盖也就意味着触摸消息将不会再传递下去。
第三点,就是分发监听器的这句:
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, layer1);
这是上一篇所使用的。在分发监听器时,有两种方式可以进行注册分发。第一种就是上面这种,第二种就是通过使用另外一个接口,addEventListenerWithFixedPriority()。也是通过_eventDipatcher调用。对于
这两个接口,它们的第一个参数均是传入一个我们自己定义的监听器指针,我们需要注意的是它们的第二个参数。Graph类型的接口传入的是节点Node*的指针,即一个节点;Fixed类型的接口传入的是一个整型值。
Graph类型:通过这种类型分发注册监听器后,监听器的优先级会根据节点在场景中的Zolder来决定,ZOlder越大,则该节点就在场景中越被渲染在越上面,优先级也就越大。
Fixed类型:通过这种类型分发注册监听器后,监听器的优先级根据整型值的大小来决定。整型值越大,优先级越小。(注意:整型的值传入不能为0)
那么,可能你会有这样的疑问,既然都可以设置优先级,一个是节点ZOlder决定优先级,一个是你指定的整数值决定。如果是混合来使用,哪个的优先级更大?
这是一个很不错的测试,没错,这个就属于我们下面要做的测试内容。
好,理解了这些,就准备可以来看看我们要做的例子了。
在做之前,我有点想说明的,那就是它的应用如何?关于做优先级方面的处理的时候,我们可能需求不一样,这个需求由就你自己来设定了。比如卡牌游戏,每张卡牌都可以点击拖动,但是当两张甚至多张卡牌重叠在一起时,你可以像上一篇一样点击哪张就让它浮现在表面;如果你就想固定它,哪张在上面,那么点击最上面的那张时将不会影响下面的卡牌等等。这些都是可以根据我们的需求来动态改变的,关键的一点是要理解触摸事件在触发时整个的传递过程。好了,话不多说,直接进入正题吧~
这一次我们需要做的还不算多,也不算少。直接看看我们做的效果先:
解释一下:
我们这里做了两个测试,第一个就是事件的分发和移除相关;第二个是测试通过Fixed类型来设置监听器的优先级。
第一个测试:我们可以看到有Block1、Block2和Block3三个方块。它们的监听器均是覆盖的。Block1和Block3是通过Fixed类型来设置优先级的,而Block2是通过Graph来设置优先级的。它们此时优先级从大到小依次是:Block1>Block2>Block3。在点击重叠部分时,优先级最高的会覆盖优先级低的。在场景中,我们还添加了移除事件的菜单条目,就在方块下方,点击后可以移除掉所有的Touch_One_By_One消息,包括先有的菜单条目,所以移除后将不能点击,因此为了能够跳转到第二个测试场景中,我们在点击完成之后又创建另外一个菜单条目。
第二个测试:这个测试是仅仅用Fixed类型来设置分发事件监听器的优先级的,通过这个接口,传入一个整型类型的值设置优先级。各颜色方块的优先级从大到小依次是:Red>Yellow>Blue。
通过上面测试一已经可以看出上面问题的结果了。当两种方法一起使用时,优先级的从大到小依次是:
(Fixed类型整型值<0) > (Graph类型) > (Fixed类型整型值 > 0)
解释完毕~
下面就直接贴代码了,为了方便阅读,我把这两个场景分成两个类。TouchEventTest类和AnotherTouchEventTest类。
TouchEventTest.h:
#ifndef __TOUCH_EVENT_TEST_H__
#define __TOUCH_EVENT_TEST_H__
#include "cocos2d.h"
USING_NS_CC;
class TouchEventTest : public Layer
{
public:
static Scene* createScene();
virtual bool init();
CREATE_FUNC(TouchEventTest);
};
#endif
TouchEventTest.cpp:
#include "TouchEventTest.h"
#include"AnotherTouchEventTest.h"
Scene* TouchEventTest::createScene()
{
auto scene = Scene::create();
auto layer = TouchEventTest::create();
scene->addChild(layer);
return scene;
}
bool TouchEventTest::init()
{
if ( !Layer::init() )
{
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
//当前测试标签描述
auto test_label = Label::createWithSystemFont("Event Dispatcher And Remove Test", "", 30);
test_label->setPosition(Vec2(visibleSize.width / 2, visibleSize.height - test_label->getContentSize().height));
this->addChild(test_label, 20);
auto label = Label::createWithSystemFont("Block1:Fixed:-1,Block2:Graph:layer2,Block3:Fixed:1", "", 23);
this->addChild(label);
label->setPosition(Vec2(test_label->getPositionX(), test_label->getPositionY() - 80));
//方块一
LayerColor *layer1 = LayerColor::create(Color4B::RED, 100, 100);
this->addChild(layer1,3);
layer1->ignoreAnchorPointForPosition(false);
layer1->setAnchorPoint(Vec2::ANCHOR_MIDDLE); //设置锚点为中心点
layer1->setPosition(visibleSize.width / 2 - layer1->getContentSize().width / 2 , visibleSize.height / 2);
//方块一标签
auto layer1_label = Label::createWithSystemFont("Block1", "", 30);
layer1_label->setPosition(Vec2(layer1->getContentSize() / 2));
layer1->addChild(layer1_label);
//方块二
LayerColor *layer2 = LayerColor::create(Color4B::GREEN, 100, 100);
this->addChild(layer2,2);
layer2->ignoreAnchorPointForPosition(false);
layer2->setAnchorPoint(Vec2::ANCHOR_MIDDLE); //设置锚点为中心点
layer2->setPosition(Vec2(layer1->getPositionX() + 30, layer1->getPositionY() + 50));
//方块二标签
auto layer2_label = Label::createWithSystemFont("Block2", "", 30);
layer2_label->setPosition(Vec2(layer2->getContentSize() / 2));
layer2->addChild(layer2_label);
//方块三
LayerColor *layer3 = LayerColor::create(Color4B::BLUE, 100, 100);
this->addChild(layer3,1);
layer3->ignoreAnchorPointForPosition(false);
layer3->setAnchorPoint(Vec2::ANCHOR_MIDDLE); //设置锚点为中心点
layer3->setPosition(Vec2(layer1->getPositionX() + 70, layer1->getPositionY() - 10));
//方块三标签
auto layer3_label = Label::createWithSystemFont("Block3", "", 30);
layer3_label->setPosition(Vec2(layer3->getContentSize() / 2));
layer3->addChild(layer3_label);
//创建监听器1
auto listener1 = EventListenerTouchOneByOne::create();
listener1->setSwallowTouches(true);
//为监听器分配回调函数
listener1->onTouchBegan = [=](Touch *t, Event *e){
CCLOG("listener1 touch Began!!!!");
auto pos = layer1->convertToNodeSpace(t->getLocation());
Rect rect = { 0, 0, layer1->getContentSize().width, layer1->getContentSize().height };
if (rect.containsPoint(pos)){
//设置透明度,范围是:0~255,显示程度逐渐递增
layer1->setOpacity(180);
return true;
}
return false;
};
listener1->onTouchEnded = [=](Touch *t, Event *e){
CCLOG("listener1 touch Ended!!!!");
layer1->setOpacity(255);
};
//创建监听器2
auto listener2 = EventListenerTouchOneByOne::create();
listener2->setSwallowTouches(true);
//为监听器分配回调函数
listener2->onTouchBegan = [=](Touch *t, Event *e){
CCLOG("listener2 touch Began!!!");
auto pos = layer2->convertToNodeSpace(t->getLocation());
Rect rect = { 0, 0, layer2->getContentSize().width, layer2->getContentSize().height };
if (rect.containsPoint(pos)){
//设置透明度,范围是:0~255,显示程度逐渐递增
layer2->setOpacity(180);
return true;
}
return false;
};
listener2->onTouchEnded = [=](Touch *t, Event *e){
CCLOG("listener2 touch Ended!!!!");
layer2->setOpacity(255);
};
//创建监听器3
auto listener3 = EventListenerTouchOneByOne::create();
listener3->setSwallowTouches(true);
//为监听器分配回调函数
listener3->onTouchBegan = [=](Touch *t, Event *e){
CCLOG("listener3 touch Began!!!");
auto pos = layer3->convertToNodeSpace(t->getLocation());
Rect rect = { 0, 0, layer2->getContentSize().width, layer2->getContentSize().height };
if (rect.containsPoint(pos)){
//设置透明度,范围是:0~255,显示程度逐渐递增
layer3->setOpacity(180);
return true;
}
return false;
};
listener3->onTouchEnded = [=](Touch *t, Event *e){
CCLOG("listener3 touch Ended!!!!");
layer3->setOpacity(255);
};
//分发监听事件
_eventDispatcher->addEventListenerWithFixedPriority(listener1,-1);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener2, layer2);
_eventDispatcher->addEventListenerWithFixedPriority(listener3, 1);
//添加事件移除菜单
auto menuLabel = MenuItemLabel::create(Label::createWithSystemFont("Click Here To RemoveAllListener", "", 30),
[this](Ref *pSender){
auto target = dynamic_cast<MenuItemLabel*>(pSender);
target->setString("TouchOneByOneEvent has removed!");
//移除当前所有的TOUCH_ONE_BY_ONE类型事件:包括已经添加的菜单条目
_eventDispatcher->removeEventListenersForType(EventListener::Type::TOUCH_ONE_BY_ONE);
//新增下一场景测试跳转菜单
auto nextItem = MenuItemFont::create("Click Here Enter Next Test", [=](Ref* sender){
CCLOG("Click!!!!!!!!!!!!!!!");
Director::getInstance()->replaceScene(AnotherTouchEventTest::createScene());
});
nextItem->setFontSizeObj(30);
nextItem->setPosition(Vec2(Director::getInstance()->getVisibleSize().width / 2, 50));
auto menu2 = Menu::create(nextItem, nullptr);
menu2->setPosition(Vec2(0, 0));
this->addChild(menu2);
});
menuLabel->setPosition(Vec2(0,-visibleSize.height/2 + 100));
auto menu = Menu::create(menuLabel, NULL);
this->addChild(menu);
return true;
}
这是第一个类,在.cpp文件中的头部添加了另外一个类的头文件只是用于切换到另外一个场景中。好,继续:
AnotherTouchEventTest.h:
#ifndef __ANOTHER_TOUCH_EVENT_TEST_H__
#define __ANOTHER_TOUCH_EVENT_TEST_H__
#include "cocos2d.h"
USING_NS_CC;
class TouchableLayer :public LayerColor{
public:
TouchableLayer(const Color4B &color,GLfloat width,GLfloat height,int priority)
{
this->_color = color;
this->_width = width;
this->_height = height;
this->_fixedPriority = priority;
this->_listener = nullptr;
}
static TouchableLayer *create(const Color4B &color,GLfloat width, GLfloat height, int priority = 0)
{
auto t = new (std::nothrow)TouchableLayer(color,width,height,priority);
if (t && t->init())
{
t->autorelease();
return t;
}
else{
delete t;
t = nullptr;
}
return t;
}
virtual bool init(){
if (!LayerColor::initWithColor(_color,_width,_height)){
return false;
}
//设置锚点为中心点
this->ignoreAnchorPointForPosition(false);
this->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
//初始化自身监听器
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = [=](Touch *t, Event *e){
auto point = this->convertToNodeSpace(t->getLocation());
Size s = this->getContentSize();
Rect rect = { 0, 0, s.width, s.height };
if (rect.containsPoint(point)){
this->setOpacity(180);
return true;
}
return false;
};
listener->onTouchEnded = [=](Touch *t, Event *e){
this->setOpacity(255);
};
if (_fixedPriority != 0){
_eventDispatcher->addEventListenerWithFixedPriority(listener, _fixedPriority);
}
else{
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}
return true;
}
private:
Color4B _color;
GLfloat _width, _height;
int _fixedPriority;
EventListener *_listener;
};
class AnotherTouchEventTest : public Layer
{
public:
static Scene* createScene();
virtual bool init();
CREATE_FUNC(AnotherTouchEventTest);
};
#endif
AnotherTouchEventTest.cpp:
#include"AnotherTouchEventTest.h"
Scene* AnotherTouchEventTest::createScene(){
auto scene = Scene::create();
auto layer = AnotherTouchEventTest::create();
scene->addChild(layer);
return scene;
}
bool AnotherTouchEventTest::init(){
if (!Layer::init()){
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
//当前测试标签描述
auto test_label = Label::createWithSystemFont("EventListenerWithFixedPriority Test", "", 30);
this->addChild(test_label);
test_label->setPosition(Vec2(visibleSize.width / 2, visibleSize.height - test_label->getContentSize().height));
auto label = Label::createWithSystemFont("Fixed Priority:Red:10,Blue:20,Yellow:30", "", 25);
this->addChild(label);
label->setPosition(Vec2(test_label->getPositionX(), test_label->getPositionY() - 80));
//方块三
LayerColor *layer3 = TouchableLayer::create(Color4B::BLUE,100,100,30);
layer3->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
this->addChild(layer3);
//方块四
LayerColor *layer4 = TouchableLayer::create(Color4B::YELLOW,100,100,20);
layer4->setPosition(Vec2(layer3->getPositionX() + 40, layer3->getPositionY() + 60));
this->addChild(layer4);
//方块五
LayerColor *layer5 = TouchableLayer::create(Color4B::RED, 100, 100, 10);
layer5->setPosition(Vec2(layer3->getPositionX() - 30, layer3->getPositionY() + 30));
this->addChild(layer5);
return true;
}
这第二个类我对LayerColor类进行了一个简单的封装,并命名为TouchableLayer类,当实例化出来时都有属于它们自己的监听器,并直接分发,通过传入整型值进行监听器优先级的设定,如果没有,就设定为Graph类型的监听器。
好了,基本上就是这些了,其它的也没什么可以说的了。对于移动端的触摸机制,在这里就暂时告一段落了。可能你更想听听关于多点触摸的。由于设备有限,就不做实验演示了。大家也可以去看看它的源码。如果理解了单点触摸,学习多点触摸也是很容易的哈~单点触摸是对传入的点单独处理,而多点触摸是用到了一个容器把你触摸的点保存起来,分别处理。
嗯~希望以上的实例可以让你明白对于单点触摸机制中不管是步骤也好,事件分发的优先性问题也罢,都能够让你对它理解得更深入点。下一篇我决定来说说关于这个节点的生命周期问题,因为在很多时候需要去考虑到使用它,特别是声音播放问题,当然声音播放就留在下下篇再说啦~