1. 程式人生 > >cocos2dx學習之路----第九篇(深入理解單點觸控的事件機制)

cocos2dx學習之路----第九篇(深入理解單點觸控的事件機制)

上一篇我們簡單接觸了關於單點觸控的實現。

這一篇我們繼續進一步的理解單點觸控的事件分發的優先順序問題。

我們來回顧一下實現觸控的過程:

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型別的監聽器。

好了,基本上就是這些了,其它的也沒什麼可以說的了。對於移動端的觸控機制,在這裡就暫時告一段落了。可能你更想聽聽關於多點觸控的。由於裝置有限,就不做實驗演示了。大家也可以去看看它的原始碼。如果理解了單點觸控,學習多點觸控也是很容易的哈~單點觸控是對傳入的點單獨處理,而多點觸控是用到了一個容器把你觸控的點儲存起來,分別處理。

嗯~希望以上的例項可以讓你明白對於單點觸控機制中不管是步驟也好,事件分發的優先性問題也罷,都能夠讓你對它理解得更深入點。下一篇我決定來說說關於這個節點的生命週期問題,因為在很多時候需要去考慮到使用它,特別是聲音播放問題,當然聲音播放就留在下下篇再說啦~大笑

相關推薦

cocos2dx學習----深入理解觸控事件機制

上一篇我們簡單接觸了關於單點觸控的實現。 這一篇我們繼續進一步的理解單點觸控的事件分發的優先順序問題。 我們來回顧一下實現觸控的過程: 1.建立新的監聽器; 2.為新的監聽器分配回撥函式(而我們在上一篇中用到了Lamda表示式來實現); 3.註冊分發監聽器。 好,這一篇就是

cocos2dx學習----座標系統中本地座標與世界座標的轉換詳解

這一篇我們來談談關於座標系統中本地座標與世界座標的轉換問題。 在上一篇中我們知道了標準的螢幕座標系、本地座標與世界座標的區別,還了解了關於cocos2dx的座標系問題。 其實關於OpenGL的座標,如果我們做2d程式設計,是可以暫時忽略Z軸座標的。但是卻需要記住的是渲染的深

cocos2dx學習----十章Node節點生命週期詳解

這一篇我們就來看看Node節點的生命週期問題。 對於Node節點的生命週期,也稱回撥事件的回撥。當對節點進行操作時,比如被新增或者移除,它預設都會呼叫自己的一些事件方法。現在就讓我們看看屬於它事件的回撥方法有哪些,如下所示: virtual void onEnter();

python學習——三彈 作業第一題

image 操作 啟動程序 代碼 color 鎖定文件 文件 文件內容 數據 作業一:編寫登錄接口1.輸入用戶名密碼2.認證成功後顯示歡迎信息3.輸錯三次後鎖定。 所需知識點 文件基本讀寫操作,循環,列表,字典 上面的作業題是在學習完數據類型和簡單的文件操作之後布置的,

Pthon學習 Python基礎

pri bsp programs -s alt 如果 lex class 算數運算 1.運算符:+ - *(乘法) /(除法) %(求余) //(求商) **(求冪) 2.成員運算:in not in:判斷單個字符或者子序列在不在字符串中。(n

cocos2dx學習----十四(記憶體管理機制

本來這一篇要繼續詳談一下動作類相關的應用,但是考慮到思路差不多,只是呼叫的類方法名引數不一樣而已。到時候再通過一個小的案例來進一步熟悉學習相關介面的使用。這一篇就來談談另外一個比較重要的類,引用計數類(Ref)。 關於cocos2dx這一套記憶體管理,它是引用了object

JAVA學習2-JAVA第一個程式hello world

java實現hello word 第1篇中我們瞭解了什麼是機器語言以及關於儲存單位的一些知識點,知道了能夠用編寫機器語言的二進位制值程式設計,但是二進位制面向機器挺友好機器能夠快速的識別和執行,但是面向編寫它的人就不太友好了。能否有一種語言對人比較友好而且機器還能識別執行的呢?我告訴你是

架構師的成長---1開篇

    本人java程式猿一枚,2012年參加工作。一步一步的由最初的程式設計師成長為獨立事業部經理,迷茫過,也興奮過。今天的我又迷茫了。     在人生的道路上,每個人都會遇到很多次選擇,每一次的選擇決定了今後一段時間的人生走向。今天的我又到了選擇的時候。 &nb

Vue學習:跑馬燈項目實現

let rip char eth sta 對象 app 清除 一個 前面六篇講解了Vue的一些基礎知識,正所謂:學以致用,今天我們將用前六篇的基礎知識,來實現類似跑馬燈的項目。 學前準備: 需要掌握定時器的兩個函數:setInterval和clearInterval以及作用

Vue學習:簡單計算器的實現

oct color 實現 mode 初始化 要掌握 原理 content method 前面九篇講解了vue的一些基礎知識,正所謂:學以致用,今天我們將用前九篇的基礎知識,來模擬實現計算器的簡單功能,項目價值不高,純粹是為了加深掌握所學知識。 學前準備: 需要掌握JavaS

Vue學習:按鍵修飾符的使用

定義 this 速度 捕獲 需求 序號 esc style color 1、我們工作中經常會有類似於這樣的需求:按下Enter鍵觸發某個事件、或者按下ESC退出頁面等各種各樣的場景。在Vue中,可以通過鍵盤修飾符來實現這樣的場景。 2、事例代碼: <body>

#Java學習——基礎階段二

string 類 結合 類型 絕對路徑 一起 java 對象 刪除文件夾 mkdir 我的學習階段是跟著CZBK黑馬的雙源課程,學習目標以及博客是為了審查自己的學習情況,畢竟看一遍,敲一遍,和自己歸納總結一遍有著很大的區別,在此期間我會參雜Java瘋狂講義(第四版)裏面的內

#Java學習——基礎階段二十四

out 出現 萬能 -c ack 分隔 status osi 版本 我的學習階段是跟著CZBK黑馬的雙源課程,學習目標以及博客是為了審查自己的學習情況,畢竟看一遍,敲一遍,和自己歸納總結一遍有著很大的區別,在此期間我會參雜Java瘋狂講義(第四版)裏面的內容。 前言:此隨

Java學習 五章 面向物件1

面向物件(1) 1、認識物件 (1)萬物皆物件。 (2)物件=特點或特徵(屬性)+行為或(方法)。 (3)物件由屬性和方法組成,一定要具體到個體上。 2、認識類 (1)類是一些具有共同屬性和方法的物件的一個抽象。 (2)類是一個概念,不是具體的一個物件。 (3)

配置檔案讀取架構師的成長---2

開篇後第一個知識點,本想寫資料庫連線池,仔細一想還是放棄了,改寫為《配置檔案讀取》。 畢竟專案中的基礎資訊,比如說資料庫連線資訊是需要配置在專案的配置檔案中的。 重點介紹 ResourceBundle.getBundle("配置檔名稱").getString("配置檔案內的key值"); &

架構師的責任架構師的成長---3

    作為架構師,首先要明確架構師的責任,要不然會再多的技術也是枉然。     簡單的說,帶領方向和難點攻克。     帶領方向是指架構師應不斷地多讀書,多學習,跟隨最新技術,不斷地昇華自己,並不停的為團隊傳輸最新知識,讓整個團隊不斷地進步。

映射:request-String-Object-Map之間相互轉化程序員的成長---5

exception types common validate runt eight methods 數據庫 英文 為什麽寫這一篇 問題一:jdbc連接數據庫返回的對象是ResultSet,如何把ResultSet對象中的值轉換為我們自建的各種實體類? 我估計,80%的程序

Vue學習十一:為頁面元素設置class類樣式

成了 簡化 htm viewport con ava 屬性綁定 進行 頁面元素 1、class為頁面元素的一個屬性,通過前面第五篇的內容可知,操作屬性需要使用到v-bind指定(也可簡寫為 :)。 2、先來看一個簡單的頁面樣式內容: <!DOCTYPE html&g

《C++primer(五版)》學習-章:特殊工具與技術

【宣告:版權所有,轉載請標明出處,請勿用於商業用途。聯絡信箱:[email protected]】 19.1 控制記憶體分配 1. 當我們使用一條new表示式時: string *sp

Vue學習十六:車型列表的添加與刪除項目

html set clas shee char name 寶馬 list 刪除按鈕 又到了大家最喜歡的項目練習階段,學以致用,今天我們要用前幾篇的學習內容實現列表的添加與刪除。 學前準備: ①:JavaScript中的splice(index,i)方法:從已知數組的inde