cocos2dx之TableView和ScrollView的混合使用

**************************************************************************

时间:2015-01-09

作者:Sharing_Li

转载出处:http://www.jb51.cc/article/p-cjqisawf-rx.html

***************************************************************************

玩过《开心消消乐》这款游戏的人,应该知道里面有这样一处设计,如下图:

我们可以左右滑动界面,也可以上下滑动界面,左右滑动的时候不能上下滑动,上下滑动的时候不能左右滑动。这种效果可以用TableView和ScrollView来组合实现,即先弄一个ScrollView,然后把2个TableView当作内容放入这个ScrollView中就可以了,这种UI设计也应用在《开心消消乐》其好友信件中,只不过多了一个TableView。

接下来将进行代码讲解,cocos2dx的版本是3.2,先展示一下实现之后的效果图:

看完效果图,再看正文,定义一个类:CombineView

头文件:CombineView.h

#ifndef __COMBINE_VIEW_H__
#define __COMBINE_VIEW_H__

#include "cocos2d.h"
#include "extensions/cocos-ext.h"

USING_NS_CC;
USING_NS_CC_EXT;

enum Table
{
	Table_Left = 0,Table_Center,Table_Right
};

class CombineView : public Layer,TableViewDataSource,TableViewDelegate
{
public:
	CombineView();
	~CombineView();

	virtual bool init();
	static cocos2d::Scene * create();

	virtual Size tableCellSizeForIndex(TableView *table,ssize_t idx);
	virtual TableViewCell* tableCellAtIndex(TableView *table,ssize_t idx);
	virtual ssize_t numberOfCellsInTableView(TableView *table);
	virtual void tableCellTouched(TableView* table,TableViewCell* cell);

	virtual void scrollViewDidScroll(ScrollView* view);
	virtual void scrollViewDidZoom(ScrollView* view);

public:
	void SetTouch(bool isTouched);
	//对scrollview的调整
	void adjustScrollView(float offset);
private:
	ScrollView * m_scrollView;
	TableView * m_leftTable;
	TableView * m_centerTable;
	TableView * m_rightTable;
	//scrollview当前显示的页数
	int m_curPage;
	//第一个触摸点
	Vec2 m_firstPoint;
	//scrollview的偏移
	Vec2 m_offset;
	//判断第一次滑动方向
	bool m_horizontal;
	bool m_vertical;
	//View的大小
	Size m_viewSize;
};

#endif // !__COMBINE_VIEW_H__


再看看cpp文件的实现,这里对主要的代码进行讲解,想要完整代码和资源,请到文章末尾点击下载(0下载积分)。

我们写代码,要养成初始化成员变量的习惯,这样可以避免一些意想不到的错误。同时记住不用的资源要记得释放。

CombineView::CombineView()
{
	m_scrollView = NULL;
	m_leftTable = NULL;
	m_centerTable = NULL;
	m_rightTable = NULL;
	m_curPage = 0;
	m_firstPoint = Vec2(0,0);
	m_offset = Vec2(0,0);
	m_vertical = false;
	m_horizontal = false;
	m_viewSize = Size(0,0);
}

如效果图所示,我们要搞一个scrollview,这家伙呢,怀了5个月的三胞胎,分别是三个tableview。为了区别这三个儿子(喂,你怎么知道都是男的而不是女的),我们要给他们取名字,因为他们仨要共用一个函数即tableCellAtIndex,如果不取名,怎么知道谁是老二老三呢, 如头文件中定义的枚举类。

        m_scrollView = ScrollView::create();
	m_scrollView->setViewSize(m_viewSize);
	m_scrollView->setContentOffset(Point::ZERO);
	m_scrollView->setDelegate(this);
	m_scrollView->setDirection(ScrollView::Direction::HORIZONTAL);
	m_scrollView->setAnchorPoint(Point::ZERO);
	m_scrollView->setPosition(Vec2::ZERO);
	m_scrollView->setTouchEnabled(false);//因为我们不需要scrollview的触摸,因为太糟糕~
	pView->addChild(m_scrollView);
	
	//添加内容
	auto pContainer = Layer::create();
	pContainer->setContentSize(Size(m_viewSize.width * 3,m_viewSize.height));
	pContainer->setAnchorPoint(Point::ZERO);
	pContainer->setPosition(Vec2::ZERO);
	m_scrollView->setContainer(pContainer);

	//添加tabelview
	auto containerSize = pContainer->getContentSize();
	m_leftTable = TableView::create(this,ViewSize);
	m_leftTable->setTag(Table_Left);
	m_leftTable->ignoreAnchorPointForPosition(false);
	m_leftTable->setAnchorPoint(Vec2(0.5,0.5));
	m_leftTable->setPosition(Vec2(containerSize.width / 6,containerSize.height / 2));
	m_leftTable->setDirection(ScrollView::Direction::VERTICAL);
	m_leftTable->setDelegate(this);
	m_leftTable->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);
	m_leftTable->reloadData();
	pContainer->addChild(m_leftTable);

	m_centerTable = TableView::create(this,ViewSize);	
	m_centerTable->setTag(Table_Center);
	m_centerTable->ignoreAnchorPointForPosition(false);
	m_centerTable->setAnchorPoint(Vec2(0.5,0.5));
	m_centerTable->setPosition(Vec2(containerSize.width / 2,containerSize.height / 2));
	m_centerTable->setDirection(ScrollView::Direction::VERTICAL);
	m_centerTable->setDelegate(this);
	m_centerTable->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);
	m_centerTable->reloadData();
	pContainer->addChild(m_centerTable);

	m_rightTable = TableView::create(this,ViewSize);
	m_rightTable->setTag(Table_Right);
	m_rightTable->ignoreAnchorPointForPosition(false);
	m_rightTable->setAnchorPoint(Vec2(0.5,0.5));
	m_rightTable->setPosition(Vec2(containerSize.width / 6 * 5,containerSize.height / 2));
	m_rightTable->setDirection(ScrollView::Direction::VERTICAL);
	m_rightTable->setDelegate(this);
	m_rightTable->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);
	m_rightTable->reloadData();
	pContainer->addChild(m_rightTable);


然后我们再来看看触摸函数的实现,首先是touchbegan:

        auto listenerT = EventListenerTouchOneByOne::create();

	listenerT->onTouchBegan = [=](Touch * touch,Event * pEvent){
		m_firstPoint = touch->getLocation();
		m_offset = m_scrollView->getContentOffset();
		if (!m_scrollView->getBoundingBox().containsPoint(m_firstPoint))
		{
			return false;
		}
		return true;
	};


简洁明了(.......),然后再看touchmoved:

        listenerT->onTouchMoved = [=](Touch * touch,Event * pEvent){
		auto movePoint = touch->getLocation();
		auto distance = movePoint.x - m_firstPoint.x;

		if ((distance > 0 && this->m_curPage == 0) || (distance < 0 && this->m_curPage == 2))
		{
			return;
		}

		//限制滑动方向,避免scorll和table同时滑动
		if (fabs(movePoint.y - m_firstPoint.y) / fabs(distance) > 0.7 || m_vertical)
		{
			if (!m_horizontal)
			{
				m_vertical = true;
			}
			return;
		}
		else //水平
		{
			if (!m_vertical)
			{
				m_horizontal = true;
			}
		}
		if (m_horizontal)
		{
			this->SetTouch(false);
		}
		m_scrollView->setContentOffset(Vec2(distance + m_offset.x,0));
	};


这一段代码的意思是:如果你先垂直滑动,那么就将m_vertical设置为true,这样你就不能水平滑动了;如果你先水平滑动,就将m_horizontal设置为true,因而调用函数SetTouch,对着三个孩子tableview唱摇篮曲,要他们乖乖睡觉不要乱动。然后再来看看touchended:

       listenerT->onTouchEnded = [=](Touch * touch,Event * pEvent){
		auto endPoint = touch->getLocation();
		auto distance = endPoint.x - m_firstPoint.x;
		//优化滑动效果
		bool flag = false;
		if (fabsf(distance) < 60)
		{
			flag = true;
			if (distance < 0)
			{
				m_curPage--;
			}
			else if (distance > 0)
			{
				m_curPage++;
			}
		}

		//限制滑动方向,避免scroll和table同时滑动
		if (m_vertical)
		{
			m_vertical = false;
			if (flag)
			{
				if (distance > 0)
				{
					m_curPage--;
				}
				else if (distance < 0)
				{
					m_curPage++;
				}
			}
			
			return ;
		}
		else
		{
			this->SetTouch(true);
		}

		this->adjustScrollView(distance);
		m_horizontal = false;
	};

这一段代码的意思是:if (fabsf(distance) < 60)这个if语句是对滑动效果的优化,如果滑动很小距离,那么就忽视这次滑动,视图还是老样子,效果图如下:

这下应该一目了然了吧,接下来的代码是判断是先垂直滑动还是水平滑动,如果是先垂直,则直接return,return之前呢要还原m_curPage的值。如果是先水平,则要把三个熟睡的孩子搞醒。然后是对scrollview最终显示界面的调整:

void CombineView::adjustScrollView(float offset)
{
	if (offset < 0)
	{
		m_curPage++;
	}
	else if (offset > 0)
	{
		m_curPage--;
	}

	if (m_curPage < 0)
	{
		m_curPage = 0;
	}
	else if (m_curPage > 2)
	{
		m_curPage = 2;
	}

	auto adjustPoint = Vec2(-m_viewSize.width * m_curPage,0);
	m_scrollView->setContentOffsetInDuration(adjustPoint,0.1f);
}

未列出的部分代码如下:

TableViewCell* CombineView::tableCellAtIndex(TableView *table,ssize_t idx)
{
	auto cell = table->dequeueCell();
	auto cellSize = this->tableCellSizeForIndex(table,idx);
	auto tag = table->getTag();

	if (!cell)
	{
		cell = new TableViewCell();
		cell->autorelease();
		Sprite * pCellBg = NULL;
		Label * pNum = NULL;
		Sprite * pIcon = NULL;
		switch (tag)
		{
		case Table_Left:
			{
				pCellBg = Sprite::create("combineview/cell.png");
				pNum = Label::createWithTTF("1","fonts/Marker Felt.ttf",20);
				pIcon = Sprite::create("combineview/book.png");
			}
			break;
		case Table_Center:
			{
				pCellBg = Sprite::create("combineview/cell2.png");
				pNum = Label::createWithTTF("2",20);
				pIcon = Sprite::create("combineview/plane.png");
			}
			break;
		case Table_Right:
			{
				pCellBg = Sprite::create("combineview/cell3.png");
				pNum = Label::createWithTTF("3",20);
				pIcon = Sprite::create("combineview/setting.png");
			}
		default:
			break;
		}
		pCellBg->setPosition(Vec2(cellSize.width / 2,cellSize.height / 2));
		cell->addChild(pCellBg);
		pNum->setColor(Color3B(255,0));
		pNum->setPosition(Vec2(cellSize.width * 0.1,cellSize.height / 2));
		cell->addChild(pNum);
		pIcon->setPosition(Vec2(cellSize.width * 0.85,cellSize.height / 2));
		pIcon->setScale(0.2);
		cell->addChild(pIcon);
	}
	return cell;
}
void CombineView::SetTouch(bool isTouched)
{
	m_leftTable->setTouchEnabled(isTouched);
	m_centerTable->setTouchEnabled(isTouched);
	m_rightTable->setTouchEnabled(isTouched);
}

最后,完了。。。。。。。。。。才怪!

代码其实有问题,我故意留了一个bug,不知道大家发现没,这个bug不解决的话,程序跑起来会崩溃的。如果按照我之前的代码来运行的话,会在tableCellAtIndex函数中崩溃,这是为什么呢?因为我们在创建tableview的时候,给每个tableview设置tag并没有成功,那为什么没成功呢?因为我们还没设置好tag的时候,tableCellAtIndex这斯就跑起来了,我们通过table->getTag(),其实是取不到tag的,既然取不到,那么之后就不能创建图片文字,会调用空指针,所以程序就BOOM了。那么罪魁祸首就是TableView::create(this,ViewSize);这个家伙了,我们调试跟踪进源码,如下:

TableView* TableView::create(TableViewDataSource* dataSource,Size size,Node *container)
{
    TableView *table = new TableView();
    table->initWithViewSize(size,container);
    table->autorelease();
    table->setDataSource(dataSource);
    table->_updateCellPositions();
    table->_updateContentSize();

    return table;
}


倒数第二句table->_updateContentSize();里面会调用tableCellAtIndex这个函数。那么找到问题了该怎么解决呢,难懂要改源码?不用,我们可以这样创建tableview,如下:

        //m_rightTable = TableView::create(this,ViewSize);	
	m_rightTable = new TableView();
	m_rightTable->initWithViewSize(m_viewSize,NULL);
	m_rightTable->autorelease();
	m_rightTable->setDataSource(this);

那么为什么不把table->_updateCellPositions();也搞进来,因为这是保护成员函数,所以不能访问,而且也用不上,以后遇到类似的问题也可以这样解决。然后把三个tableview改过来就ok啦。

代码及资源下载处:http://download.csdn.net/detail/sharing_li/8345111

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


    本文实践自 RayWenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@163.com微信公众号:HopToad 欢迎转载,转载标注出处:http://blog.csdn.netotbaron/article/details/424343991.  软件准备 下载地址:http://cn.cocos2d-x.org/download 2.  简介2.1         引用C
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从Cocos2D-x官网上下载,进入网页http://www.cocos2d-x.org/download,点击Cocos2d-x以下的Download  v3.0,保存到自定义的文件夹2:从python官网上下载。进入网页https://www.python.org/downloads/,我当前下载的是3.4.0(当前最新
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发引擎,易学易用,支持多种智能移动平台。官网地址:http://cocos2d-x.org/当前版本:2.0    有很多的学习资料,在这里我只做为自己的笔记记录下来,错误之处还请指出。在VisualStudio2008平台的编译:1.下载当前稳
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《最强大脑》娱乐节目。将2048改造成一款挑战玩家对数字记忆的小游戏。邮箱:appdevzw@163.com微信公众号:HopToadAPK下载地址:http://download.csdn.net/detailotbaron/8446223源码下载地址:http://download.csdn.net/
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试以QtCreatorIDE来进行CMake构建。Cocos2d-x3.X地址:https://github.com/cocos2d/cocos2d-x1.打开QtCreator,菜单栏→"打开文件或项目...",打开cocos2d-x目录下的CMakeLists.txt文件;2.弹出CMake向导,如下图所示:设置
 下载地址:链接:https://pan.baidu.com/s/1IkQsMU6NoERAAQLcCUMcXQ提取码:p1pb下载完成后,解压进入build目录使用vs2013打开工程设置平台工具集,打开设置界面设置: 点击开始编译等待编译结束编译成功在build文件下会出现一个新文件夹Debug.win32,里面就是编译
分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net前言上次用象棋演示了cocos2dx的基本用法,但是对cocos2dx并没有作深入的讨论,这次以超级马里奥的源代码为线索,我们一起来学习超级马里奥的实
1. 圆形音量button事实上作者的本意应该是叫做“电位计button”。可是我觉得它和我们的圆形音量button非常像,所以就这么叫它吧~先看效果:好了,不多解释,本篇到此为止。(旁白: 噗。就这样结束了?)啊才怪~我们来看看代码:[cpp] viewplaincopyprint?CCContro
原文链接:http://www.cnblogs.com/physwf/archive/2013/04/26/3043912.html为了进一步深入学习贯彻Cocos2d,我们将自己写一个场景类,但我们不会走的太远,凡是都要循序渐进,哪怕只前进一点点,那也至少是前进了,总比贪多嚼不烂一头雾水的好。在上一节中我们建
2019独角兽企业重金招聘Python工程师标准>>>cocos2d2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观的形象就是ios里的短信气泡了),这就要求图
原文链接:http://www.cnblogs.com/linji/p/3599478.html1.环境和工具准备Win7VS2010/2012,至于2008v2版本之后似乎就不支持了。 2.安装pythonv.2.0版本之前是用vs模板创建工程的,到vs2.2之后就改用python创建了。到python官网下载版本2.7.5的,然后
环境:ubuntu14.04adt-bundle-linux-x86_64android-ndk-r9d-linux-x86_64cocos2d-x-3.0正式版apache-ant1.9.3python2.7(ubuntu自带)加入环境变量exportANDROID_SDK_ROOT=/home/yangming/adt-bundle-linux/sdkexportPATH=${PATH}:/$ANDROID_SDK_ROOTools/export
1开发背景游戏程序设计涉及了学科中的各个方面,鉴于目的在于学习与进步,本游戏《FlappyBird》采用了两个不同的开发方式来开发本款游戏,一类直接采用win32底层API来实现,另一类采用当前火热的cocos2d-x游戏引擎来开发本游戏。2需求分析2.1数据分析本项目要开发的是一款游
原文链接:http://www.cnblogs.com/linji/p/3599912.html//纯色色块控件(锚点默认左下角)CCLayerColor*ccc=CCLayerColor::create(ccc4(255,0,0,128),200,100);//渐变色块控件CCLayerGradient*ccc=CCLayerGradient::create(ccc4(255,0,0,
原文链接:http://www.cnblogs.com/linji/p/3599488.html//载入一张图片CCSprite*leftDoor=CCSprite::create("loading/door.png");leftDoor->setAnchorPoint(ccp(1,0.5));//设置锚点为右边中心点leftDoor->setPosition(ccp(240,160));/
为了答谢广大学员对智捷课堂以及关老师的支持,现购买51CTO学院关老师的Cocos2d-x课程之一可以送智捷课堂编写图书一本(专题可以送3本)。一、Cocos2d-x课程列表:1、Cocos2d-x入门与提高视频教程__Part22、Cocos2d-x数据持久化与网络通信__Part33、Cocos2d-x架构设计与性能优化内存优
Spawn让多个action同时执行。Spawn有多种不同的create方法,最终都调用了createWithTwoActions(FiniteTimeAction*action1,FiniteTimeAction*action2)方法。createWithTwoActions调用initWithTwoActions方法:对两个action变量初始化:_one=action1;_two=action2;如果两个a
需要环境:php,luajit.昨天在cygwin上安装php和luajit环境,这真特么是一个坑。建议不要用虚拟环境安装打包环境,否则可能会出现各种莫名问题。折腾了一下午,最终将环境转向linux。其中,luajit的安装脚本已经在quick-cocos2d-x-develop/bin/中,直接luajit_install.sh即可。我的lin
v3.0相对v2.2来说,最引人注意的。应该是对触摸层级的优化。和lambda回调函数的引入(嗯嗯,不枉我改了那么多类名。话说,每次cocos2dx大更新。总要改掉一堆类名函数名)。这些特性应该有不少人研究了,所以今天说点跟图片有关的东西。v3.0在载入图片方面也有了非常大改变,仅仅只是