cocos2dx-3.0(23) ScrollView 缩放 及 touch新用法

~~~~我的生活,我的点点滴滴!!

有参照star特530的blog,大家可以移步过去学习~~~~~


ScrollView在3.0里面依然存在于extensions下面,看他的继承关系,他最终也是继承node,我们在来看看他的头文件应该怎么包含

1. 在头文件中添加 #include "cocos-ext.h" 在添加使用域  USING_NS_CC_EXT;

2. 添加多继承 public ScrollViewDelegate,如下:

class HelloWorld : public LayerColor, public ScrollViewDelegate

我们转到ScrollViewDelegate里面,发现这是一个抽象基类,里面有两个纯虚函数,我们需要自己实现

   
 /**
     * @js NA
     * @lua NA
     */
    virtual void scrollViewDidScroll(ScrollView* view) = 0;
    /**
     * @js NA
     * @lua NA
     */
    virtual void scrollViewDidZoom(ScrollView* view) = 0;

所以我们还需要在自己的类中添加这两行代码

//scrollview滚动的时候会调用
	void scrollViewDidScroll(extension::ScrollView *view);
	//scrollview缩放的时候会调用
	void scrollViewDidZoom(extension::ScrollView *view);

我们为了达到用鼠标点击图片移动时,出现缩小放大效果且能像相册一样切换图片,那么我们还需要添加触摸事件

    
	bool onTouchBegan(Touch *touch, Event *pEvent);
	void onTouchMoved(Touch *touch, Event *pEvent);
	void onTouchEnded(Touch *touch, Event *pEvent);

下面直接看看在头文件中代码

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "extensions/cocos-ext.h"
#else
#include "cocos-ext.h"
#endif

#include "cocostudio/CocoStudio.h"
#include "CocosGUI.h"

USING_NS_CC;
USING_NS_CC_EXT;
using namespace cocostudio;
using namespace ui;

#define PHOTO_COUNT 11

class HelloWorld : public LayerColor, public ScrollViewDelegate
{
public:
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();  
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
	bool onTouchBegan(Touch *touch, Event *pEvent);
	void onTouchMoved(Touch *touch, Event *pEvent);
	void onTouchEnded(Touch *touch, Event *pEvent);

	//scrollview滚动的时候会调用
	void scrollViewDidScroll(extension::ScrollView *view);
	//scrollview缩放的时候会调用
	void scrollViewDidZoom(extension::ScrollView *view);

	//添加触摸
	void addListener();

	//添加粒子系统test
	void addParticle();

    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
	
	//骨骼动画
	Armature *m_armature;

	//添加UI
	void addUI();

	//添加场景(区别是我自定义的函数)
	void addMyScene();

private:
	//根据手势滑动的距离和方向滚动图层
	void adjustScrollView(float offset);

	//void setback();

private:	
	//存放所有图片
	Vector< Sprite* > m_spVec;
	
	//当前的ScrollView
	extension::ScrollView *m_scrollView;

	//touchBegen时的触摸位置
	Point m_touchPoint;

	//当前是第几张图片
	int m_currentPage;

};

#endif // __HELLOWORLD_SCENE_H__

上面有很多不是我们这相例子里面的,不过不影响

在来看看源文件里面

bool HelloWorld::init()
{
    //
    // 1. super init first
    if ( !LayerColor::initWithColor(Color4B(120,120,120,255)) )
    {
        return false;
    }
	auto visibleSize = Director::getInstance()->getVisibleSize();
	auto origin = Director::getInstance()->getVisibleOrigin();

	/*auto scrollLayer = LayerColor::create(Color4B(0,0,0,255));
	scrollLayer->setContentSize(Size(640,480));
	scrollLayer->setAnchorPoint(Point(0.5,0.5));
	scrollLayer->setPosition(Point(visibleSize.width/2-320, visibleSize.height/2-240));
	this->addChild(scrollLayer);*/


	m_scrollView = extension::ScrollView::create(Size(visibleSize.width, visibleSize.height));

	char spriteName[15];
	Layer *photoLayer = Layer::create();
	for(int i = 1; i <= PHOTO_COUNT; ++ i)
	{
		memset(spriteName, 0, sizeof(spriteName));
		sprintf(spriteName, "XJ%d.jpg", i);
		auto sprite = Sprite::create(spriteName);
		sprite->setPosition(Point(visibleSize.width*(i*1.0-0.5), visibleSize.height*0.5));
		//缩小一倍
		//sprite->setScale(0.5);
		photoLayer->addChild(sprite);
		m_spVec.pushBack(sprite);
	}
	//设置layer到滚动层容器中
	m_scrollView->setContainer(photoLayer);
	//重新设置滚动层的大小,这点很重要。
	m_scrollView->setContentSize(Size(PHOTO_COUNT*visibleSize.width, visibleSize.height));
	//设置代理
	m_scrollView->setDelegate(this);
	m_scrollView->setPosition(Point::ZERO);
	//我们只在水平方向上滚动。
	m_scrollView->setDirection(extension::ScrollView::Direction::HORIZONTAL);
	m_scrollView->setContentOffset(Point(0,0));
	this->addChild(m_scrollView);

	m_currentPage = 0;

	//注册触摸,和2.x的不同了, 还有一种lambda表达式的写法
	/************************************************************************
	* lambda表达式的写法 
	* auto listener1 = EventListenerTouchOneByOne::create();
	* listener1->onTouchBegan = [](Touch* touch, Event* event)
	* {
	*	//dosomething
	* };
	* listener1->onTouchMoved = [](Touch* touch, Event* event)
	* {
	*	//dosomething
	* };
	* listener1->onTouchEnded = [=](Touch* touch, Event* event)
	* { 
	*	//dosomething
	* };
	* _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
	************************************************************************/
	auto listener = EventListenerTouchOneByOne::create();
	//设置是否想下传递触摸
	listener->setSwallowTouches(true);
	listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
	listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
	listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
	//将触摸监听添加到eventDispacher中去
	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
	//注册触摸结束

    return true;
}
我们在也不需要使用setTouchEnabled这个函数了,你会发现你用了以后,编译时会提示这个函数已经被否决了,可以用setEnable();代替看看如何。

对于上面的触摸我们要重点说说,说完了在继续分析 ScrollView!!!

一、触摸监听listener的创建方式有两种,

1、EventListenerTouchOneByOne
2、EventListenerTouchAllAtOnce,
顾名思义EventListenerTouchOneByOne的意思单点触摸,EventListenerTouchAllAtOnce是多点触摸
而不需要再用设置Delegate的方式来做了。
3.0触摸机制还有个不同的地方,只要是放在最上面的那个精灵,那它的触摸优先级就最高。
我们用的按钮Menu 就是用这种方式设置触摸优先级的。

二、listener的调试优先级
我们进入addEventListenerWithSceneGraphPriority的定义中看一下,有下面这一行代码:

 listener->setFixedPriority(0);


它将精灵的触摸优先级设置成0,从这里我们可以引申出两个问题,一个就是当我们要给精灵设置触摸优先级时,

因为0已经被“官府”征用了,另一个问题就是:如果自己想设置精灵的触摸优先级,那应该怎么做呢?
下面是提供的另外一种添加listener的方法:

_eventDispatcher->addEventListenerWithFixedPriority(listener1 ,fixedPriority);
在第二个参数里设置触摸优先级,这样就可以了。

三、多个目标如何添加触摸监听
如果多个精灵都想实现拖动的功能,那么这些精灵都可以使用listener1这一个触摸监听,
例如我们有三个精灵,sprite,sprite2,sprite3,他们调用listener1的方式:

 _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);
 _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);

当我们进入到clone函数里面,你会发现他实现了复制触摸函数,也就是clone是专门用来这么干这的

EventListenerTouchOneByOne* EventListenerTouchOneByOne::clone()
{
    auto ret = new EventListenerTouchOneByOne();
    if (ret && ret->init())
    {
        ret->autorelease();
        
        ret->onTouchBegan = onTouchBegan;
        ret->onTouchMoved = onTouchMoved;
        ret->onTouchEnded = onTouchEnded;
        ret->onTouchCancelled = onTouchCancelled;
        
        ret->_claimedTouches = _claimedTouches;
        ret->_needSwallow = _needSwallow;
    }
    else
    {
        CC_SAFE_DELETE(ret);
    }
    return ret;
}

四、 删除触摸监听

如果想移除sprite的触摸移动,可以这么做:

_eventDispatcher->removeEventListeners(EventListener::Type::TOUCH_ONE_BY_ONE);

看完了新版 touch 我们继续看scrollView中的委托方法, 实现效果是对象在某个坐标范围内移动时会有缩放效果。

void HelloWorld::scrollViewDidScroll(extension::ScrollView *view)
{
	//往右移动x坐标为正,往左移为负
	auto offPos = view->getContentOffset();
	//log("offset pos %f %f", offPos.x, offPos.y);

	for(auto sp : m_spVec)
	{
		//获得当前对象的X坐标(不管怎么滚动,这个坐标都是不变的)
		auto pointX = sp->getPositionX();
		
		//将精灵的 X坐标 + 偏移X坐标
		float offX = pointX + offPos.x;

		//我的屏幕大小设置为了480*640,我的图片为320*480,我让图片居中显示(Point(160,320) ---> Point(320,320))
		//图片与图片之间的间隔是一个屏幕(480),我移动图片时不想让他缩放的太小,即只在80-240之间缩放,由于要对称,所以
		//相应的240-400也会产生和80-240之间相同的缩放比例,这样后一种需要用480-offx
		if( offX > 80 && offX <= 240 )
		{
			float scaleX = offX / 240;
			sp->setScale(scaleX);
			//log("scaleX1 %f",scaleX);
		}
		else if( offX > 240 && offX <= 400 )
		{
			float scaleX = (480 - offX) / 240;
			sp->setScale(scaleX);
			//log("scaleX2 %f",scaleX);
		}
		else
		{
			//对于在80-400之外的,我们保持同一缩放比例
			sp->setScale(80*1.0/240);
		}
	}
}

我们应该知道,对象放到滚动层上,那么不管对象在scrollView上如何移动,它获得的坐标都是不会变的(如sp->getPosition()是不变的数值),这种情况下,如果我们想实现对象在某个坐标范围内会有缩放效果,那么只是去获取对象的坐标肯定是行不通的,所以肯定要找一个时刻在变化的”参照物”来利用下,该找什么呢?没错,就是scrollView的偏移坐标(scrollView->getContentOffset())!只要scrollView移动一下,那么它的 偏移量也随之改变。我这里就是利用对象的坐标与scrollView的偏移坐标之间不可告人的秘密,从而实现当前的目的。

实现了缩放,怎么去实现图片切换了,我们需要在触摸结束后,来切换ScrollView的偏移来达到切换效果

bool HelloWorld::onTouchBegan(Touch *touch, Event *pEvent)
{
	m_touchPoint = Director::getInstance()->convertToGL(touch->getLocationInView());
	log("begin x = %f, y = %f", m_touchPoint.x, m_touchPoint.y);

	return true;

}

获得一个触摸初始值,将来与结束值对比来做出一些约束

void HelloWorld::onTouchEnded(Touch *touch, Event *pEvent)
{
	Point endPoint = Director::getInstance()->convertToGL(touch->getLocationInView());
	float distance = endPoint.x - m_touchPoint.x;
	log("end x = %f, y = %f", endPoint.x, endPoint.y);

	//当我们移动的距离太小了,默认为不移动,也可以防止不小心碰了一下就切换了
	if( fabs(distance) <= 240 )
	{
		log("this is 1");
		adjustScrollView(0);
	}
	if(fabs(distance) > 240)
	{
		log("this is 2");
		adjustScrollView(distance);
	}
}
去看看adjustScrollView里面实现了什么?

<span style="font-family: Arial; font-size: 14px;">void HelloWorld::adjustScrollView(float offset)
{
	if( offset < 0 )
	{
		++m_currentPage;
	}
	else if( offset > 0 )
	{
		--m_currentPage;
	}

	if( m_currentPage < 0 )
	{
		m_currentPage = 0;
	}
	
	else if( m_currentPage >= PHOTO_COUNT )
	{
		m_currentPage = 1;
	}
	
	auto visibleSize = Director::getInstance()->getVisibleSize();
	//注意这里为 负的 visibleSize.width, 我开始写成正的,一直不出效果
	Point curPoint(-visibleSize.width * m_currentPage, 0);
	this->m_scrollView->setContentOffset(curPoint);

	//setback

}</span><span style="font-family:SimSun;font-size:14px;">
</span>

此程序依然存在一些bug,比如一下子滑动了很远的距离,那样图片的切换就会变得杂乱无章,这个依然可以在 触摸结束里面使用 distance 来限制,留给以后去完善吧!!

下面看看效果:









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值