cocos2dx之TableView和ScrollView的混合使用

前端之家收集整理的这篇文章主要介绍了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

  1. #ifndef __COMBINE_VIEW_H__
  2. #define __COMBINE_VIEW_H__
  3.  
  4. #include "cocos2d.h"
  5. #include "extensions/cocos-ext.h"
  6.  
  7. USING_NS_CC;
  8. USING_NS_CC_EXT;
  9.  
  10. enum Table
  11. {
  12. Table_Left = 0,Table_Center,Table_Right
  13. };
  14.  
  15. class CombineView : public Layer,TableViewDataSource,TableViewDelegate
  16. {
  17. public:
  18. CombineView();
  19. ~CombineView();
  20.  
  21. virtual bool init();
  22. static cocos2d::Scene * create();
  23.  
  24. virtual Size tableCellSizeForIndex(TableView *table,ssize_t idx);
  25. virtual TableViewCell* tableCellAtIndex(TableView *table,ssize_t idx);
  26. virtual ssize_t numberOfCellsInTableView(TableView *table);
  27. virtual void tableCellTouched(TableView* table,TableViewCell* cell);
  28.  
  29. virtual void scrollViewDidScroll(ScrollView* view);
  30. virtual void scrollViewDidZoom(ScrollView* view);
  31.  
  32. public:
  33. void SetTouch(bool isTouched);
  34. //对scrollview的调整
  35. void adjustScrollView(float offset);
  36. private:
  37. ScrollView * m_scrollView;
  38. TableView * m_leftTable;
  39. TableView * m_centerTable;
  40. TableView * m_rightTable;
  41. //scrollview当前显示的页数
  42. int m_curPage;
  43. //第一个触摸点
  44. Vec2 m_firstPoint;
  45. //scrollview的偏移
  46. Vec2 m_offset;
  47. //判断第一次滑动方向
  48. bool m_horizontal;
  49. bool m_vertical;
  50. //View的大小
  51. Size m_viewSize;
  52. };
  53.  
  54. #endif // !__COMBINE_VIEW_H__


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

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

  1. CombineView::CombineView()
  2. {
  3. m_scrollView = NULL;
  4. m_leftTable = NULL;
  5. m_centerTable = NULL;
  6. m_rightTable = NULL;
  7. m_curPage = 0;
  8. m_firstPoint = Vec2(0,0);
  9. m_offset = Vec2(0,0);
  10. m_vertical = false;
  11. m_horizontal = false;
  12. m_viewSize = Size(0,0);
  13. }

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

  1. m_scrollView = ScrollView::create();
  2. m_scrollView->setViewSize(m_viewSize);
  3. m_scrollView->setContentOffset(Point::ZERO);
  4. m_scrollView->setDelegate(this);
  5. m_scrollView->setDirection(ScrollView::Direction::HORIZONTAL);
  6. m_scrollView->setAnchorPoint(Point::ZERO);
  7. m_scrollView->setPosition(Vec2::ZERO);
  8. m_scrollView->setTouchEnabled(false);//因为我们不需要scrollview的触摸,因为太糟糕~
  9. pView->addChild(m_scrollView);
  10. //添加内容
  11. auto pContainer = Layer::create();
  12. pContainer->setContentSize(Size(m_viewSize.width * 3,m_viewSize.height));
  13. pContainer->setAnchorPoint(Point::ZERO);
  14. pContainer->setPosition(Vec2::ZERO);
  15. m_scrollView->setContainer(pContainer);
  16.  
  17. //添加tabelview
  18. auto containerSize = pContainer->getContentSize();
  19. m_leftTable = TableView::create(this,ViewSize);
  20. m_leftTable->setTag(Table_Left);
  21. m_leftTable->ignoreAnchorPointForPosition(false);
  22. m_leftTable->setAnchorPoint(Vec2(0.5,0.5));
  23. m_leftTable->setPosition(Vec2(containerSize.width / 6,containerSize.height / 2));
  24. m_leftTable->setDirection(ScrollView::Direction::VERTICAL);
  25. m_leftTable->setDelegate(this);
  26. m_leftTable->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);
  27. m_leftTable->reloadData();
  28. pContainer->addChild(m_leftTable);
  29.  
  30. m_centerTable = TableView::create(this,ViewSize);
  31. m_centerTable->setTag(Table_Center);
  32. m_centerTable->ignoreAnchorPointForPosition(false);
  33. m_centerTable->setAnchorPoint(Vec2(0.5,0.5));
  34. m_centerTable->setPosition(Vec2(containerSize.width / 2,containerSize.height / 2));
  35. m_centerTable->setDirection(ScrollView::Direction::VERTICAL);
  36. m_centerTable->setDelegate(this);
  37. m_centerTable->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);
  38. m_centerTable->reloadData();
  39. pContainer->addChild(m_centerTable);
  40.  
  41. m_rightTable = TableView::create(this,ViewSize);
  42. m_rightTable->setTag(Table_Right);
  43. m_rightTable->ignoreAnchorPointForPosition(false);
  44. m_rightTable->setAnchorPoint(Vec2(0.5,0.5));
  45. m_rightTable->setPosition(Vec2(containerSize.width / 6 * 5,containerSize.height / 2));
  46. m_rightTable->setDirection(ScrollView::Direction::VERTICAL);
  47. m_rightTable->setDelegate(this);
  48. m_rightTable->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);
  49. m_rightTable->reloadData();
  50. pContainer->addChild(m_rightTable);


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

  1. auto listenerT = EventListenerTouchOneByOne::create();
  2.  
  3. listenerT->onTouchBegan = [=](Touch * touch,Event * pEvent){
  4. m_firstPoint = touch->getLocation();
  5. m_offset = m_scrollView->getContentOffset();
  6. if (!m_scrollView->getBoundingBox().containsPoint(m_firstPoint))
  7. {
  8. return false;
  9. }
  10. return true;
  11. };


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

  1. listenerT->onTouchMoved = [=](Touch * touch,Event * pEvent){
  2. auto movePoint = touch->getLocation();
  3. auto distance = movePoint.x - m_firstPoint.x;
  4.  
  5. if ((distance > 0 && this->m_curPage == 0) || (distance < 0 && this->m_curPage == 2))
  6. {
  7. return;
  8. }
  9.  
  10. //限制滑动方向,避免scorll和table同时滑动
  11. if (fabs(movePoint.y - m_firstPoint.y) / fabs(distance) > 0.7 || m_vertical)
  12. {
  13. if (!m_horizontal)
  14. {
  15. m_vertical = true;
  16. }
  17. return;
  18. }
  19. else //水平
  20. {
  21. if (!m_vertical)
  22. {
  23. m_horizontal = true;
  24. }
  25. }
  26. if (m_horizontal)
  27. {
  28. this->SetTouch(false);
  29. }
  30. m_scrollView->setContentOffset(Vec2(distance + m_offset.x,0));
  31. };


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

  1. listenerT->onTouchEnded = [=](Touch * touch,Event * pEvent){
  2. auto endPoint = touch->getLocation();
  3. auto distance = endPoint.x - m_firstPoint.x;
  4. //优化滑动效果
  5. bool flag = false;
  6. if (fabsf(distance) < 60)
  7. {
  8. flag = true;
  9. if (distance < 0)
  10. {
  11. m_curPage--;
  12. }
  13. else if (distance > 0)
  14. {
  15. m_curPage++;
  16. }
  17. }
  18.  
  19. //限制滑动方向,避免scroll和table同时滑动
  20. if (m_vertical)
  21. {
  22. m_vertical = false;
  23. if (flag)
  24. {
  25. if (distance > 0)
  26. {
  27. m_curPage--;
  28. }
  29. else if (distance < 0)
  30. {
  31. m_curPage++;
  32. }
  33. }
  34. return ;
  35. }
  36. else
  37. {
  38. this->SetTouch(true);
  39. }
  40.  
  41. this->adjustScrollView(distance);
  42. m_horizontal = false;
  43. };

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

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

  1. void CombineView::adjustScrollView(float offset)
  2. {
  3. if (offset < 0)
  4. {
  5. m_curPage++;
  6. }
  7. else if (offset > 0)
  8. {
  9. m_curPage--;
  10. }
  11.  
  12. if (m_curPage < 0)
  13. {
  14. m_curPage = 0;
  15. }
  16. else if (m_curPage > 2)
  17. {
  18. m_curPage = 2;
  19. }
  20.  
  21. auto adjustPoint = Vec2(-m_viewSize.width * m_curPage,0);
  22. m_scrollView->setContentOffsetInDuration(adjustPoint,0.1f);
  23. }

未列出的部分代码如下:

  1. TableViewCell* CombineView::tableCellAtIndex(TableView *table,ssize_t idx)
  2. {
  3. auto cell = table->dequeueCell();
  4. auto cellSize = this->tableCellSizeForIndex(table,idx);
  5. auto tag = table->getTag();
  6.  
  7. if (!cell)
  8. {
  9. cell = new TableViewCell();
  10. cell->autorelease();
  11. Sprite * pCellBg = NULL;
  12. Label * pNum = NULL;
  13. Sprite * pIcon = NULL;
  14. switch (tag)
  15. {
  16. case Table_Left:
  17. {
  18. pCellBg = Sprite::create("combineview/cell.png");
  19. pNum = Label::createWithTTF("1","fonts/Marker Felt.ttf",20);
  20. pIcon = Sprite::create("combineview/book.png");
  21. }
  22. break;
  23. case Table_Center:
  24. {
  25. pCellBg = Sprite::create("combineview/cell2.png");
  26. pNum = Label::createWithTTF("2",20);
  27. pIcon = Sprite::create("combineview/plane.png");
  28. }
  29. break;
  30. case Table_Right:
  31. {
  32. pCellBg = Sprite::create("combineview/cell3.png");
  33. pNum = Label::createWithTTF("3",20);
  34. pIcon = Sprite::create("combineview/setting.png");
  35. }
  36. default:
  37. break;
  38. }
  39. pCellBg->setPosition(Vec2(cellSize.width / 2,cellSize.height / 2));
  40. cell->addChild(pCellBg);
  41. pNum->setColor(Color3B(255,0));
  42. pNum->setPosition(Vec2(cellSize.width * 0.1,cellSize.height / 2));
  43. cell->addChild(pNum);
  44. pIcon->setPosition(Vec2(cellSize.width * 0.85,cellSize.height / 2));
  45. pIcon->setScale(0.2);
  46. cell->addChild(pIcon);
  47. }
  48. return cell;
  49. }
  1. void CombineView::SetTouch(bool isTouched)
  2. {
  3. m_leftTable->setTouchEnabled(isTouched);
  4. m_centerTable->setTouchEnabled(isTouched);
  5. m_rightTable->setTouchEnabled(isTouched);
  6. }

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

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

  1. TableView* TableView::create(TableViewDataSource* dataSource,Size size,Node *container)
  2. {
  3. TableView *table = new TableView();
  4. table->initWithViewSize(size,container);
  5. table->autorelease();
  6. table->setDataSource(dataSource);
  7. table->_updateCellPositions();
  8. table->_updateContentSize();
  9.  
  10. return table;
  11. }


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

  1. //m_rightTable = TableView::create(this,ViewSize);
  2. m_rightTable = new TableView();
  3. m_rightTable->initWithViewSize(m_viewSize,NULL);
  4. m_rightTable->autorelease();
  5. m_rightTable->setDataSource(this);

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

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

猜你在找的Cocos2d-x相关文章