[cocos2dx]让CCScrollView支持分页做过IOS开发的朋友, 肯定知道UIScrollView有一个isPaged属性. 当设置其为true的时候, 滑动会自动分页. 即, 每次滑动之后, 会停止在整页的位置. 当开始介入cocos2dx开发的时候, 却发现跟UIScrollView接口十分相似的CCScrollView却没有这个分页属性. 于是手动实现了一个.
博客:http://www.cnblogs.com/jhzhu邮箱:jhzhuustc@Gmail.com作者: 知明所以时间: 2014-03-29关键字cocos2dx,CCScrollView,page,UIScrollView
基础知识在常见的图形引擎中, 滑动组件的定义里有两个重要的概念
viewSize: 这个大小值得是组件占用屏幕的大小. 即实际大小.contentSize: 这个大小是一个虚拟的大小. 我们之所以要滚动, 必然是因为需要展示的内容比现实的屏幕空间大. 我们需要滚动屏幕, 才能浏览到所有的显示内容. 这个contentSize即是我们虚拟出来的, 需要展示的所有内容加起来的大小.分页: 最典型的例子就是iphone的主界面. 我们不能任意指定一个位置, 让滑动固定在那里. 它要么停留在第一页, 要么停留在第二页, 不会是第0.5页. 每一页的大小是viewSize决定的. 那么总页数就是total_page_count = ceil(viewSize / contentSize )CCScrollView源码查看我们知道,cocos2dx的触摸都是通过CCTouchDelegate来实现的. 如果对cocos2dx的touch机制不熟悉的, 可以参考博客.
简单介绍ccTouchBegan方法的功能:ccTouchBegan是cocos2dxtouch机制的第一个方法. 这个方法的接口如下:
boolCCScrollView::ccTouchBegan(CCTouch*touch,CCEvent*event)
返回的bool值, 告诉cocos2dx中touch事件的管理者CCTouchDispatcher, 当前组件是否处理这一次触摸:
如果返回true, 则CCTouchDispatcher会根据用户的touch动作, 在后续调用本组件的ccTouchMoved,ccTouchEnded,ccTouchCancelled方法.如果返回false, 则表示当前组件不处理此次touch事件. 后续的三个方法不会被调用.CCScrollView::ccTouchBegan()解析无码无真相, 先贴一张代码图:
boolCCScrollView::ccTouchBegan(CCTouch*touch,CCEvent*event){//tag-1if(!this->isVisible()){returnfalse;}//tag-2CCRectframe=getViewRect();//dispatcher does not know about clipping. reject touches outside visible bounds.if(m_pTouches->count()>2||m_bTouchMoved||!frame.containsPoint(m_pContainer->convertToWorldSpace(m_pContainer->convertTouchToNodeSpace(touch)))){returnfalse;}...//tag-3if(m_pTouches->count()==1){// scrollingm_tTouchPoint=this->convertTouchToNodeSpace(touch);m_bTouchMoved=false;m_bDragging=true;//dragging startedm_tScrollDistance=ccp(0.0f,0.0f);m_fTouchLength=0.0f;}//tag-4elseif(m_pTouches->count()==2){...m_bDragging=false;}returntrue;}
我把跟本博文主题关系不大的代码用省略号(…)代替了. 下面是相关代码解释:
tag-1 当此组件隐藏的时候, 不予处理.tag-2 当此触摸点数大于2或者触摸点不在当前组件的显示范围内的时候, 不予处理.tag-3 这是我们的重点. 当触摸点数等于1的时候, 处理滑动操作.tag-4 触摸点等于2的时候, 响应缩放处理.源代码的修改变量申明及方法增加在CCScrollView.h文件中增加以下成员变量和方法:
//CCScrollView.hpublic:boolisPaged(){returnm_bPaged;};voidsetPaged(boolvalue){m_bPaged=value;};PRotected:clock_tm_touchBeganTime;intm_touchBeganOffset;intm_targetPage;intm_currPage;floatm_pageAccSpeed;floatm_distanceRatioOfTurn;boolm_bPaged;void__pageTouchBegan();//在CCScrollView的滑动被触发的时候调用bool__pageTouchEnd();//在ScrollView的滑动停止的时候调用void__pageTouchCancel();//在滑动被取消的时候调用void__pageClearTouch();//在一次滑动结束的时候调用
方法的实现:四个方法的代码可能要占用一些篇幅, 所以在这里, 先简要讲一下原理:
在touch开始的时候, 记录一下当时时间m_touchBeganTime和开始滑动的位置m_touchBeganOffset.在touch结束的时候, 获取结束时刻的时间和位置. 我们有两个标准来判断应该翻页还是停留在上一页:
如果滑动距离超过一页距离的一半(或者是其他阈值),那么判断为用户希望翻到下一页(或上一页)如果滑动速度超过一个阈值, 那么判断为用户希望翻到下一页(或上一页)当做了判断之后, 即可滑动到对应的位置.下面是四个方法的实现, 实现了上述原理.
voidCCScrollView::__pageTouchBegan(){//仅在设置了分页属性, 并且只有一个滑动方向的时候, 才支持分页.if(!m_bPaged||(m_eDirection!=kCCScrollViewDirectionHorizontal&&m_eDirection!=kCCScrollViewDirectionVertical))return;//记录初试时间和位置m_touchBeganTime=clock();m_touchBeganOffset=m_eDirection==kCCScrollViewDirectionHorizontal?getContentOffset().x:getContentOffset().y;}boolCCScrollView::__pageTouchEnd(){if(!m_bPaged||(m_eDirection!=kCCScrollViewDirectionHorizontal&&m_eDirection!=kCCScrollViewDirectionVertical))returnfalse;//constantconstfloatPAGE_DISTENCE=m_eDirection==kCCScrollViewDirectionHorizontal?getViewSize().width:getViewSize().height;if(PAGE_DISTENCE<=0)returnfalse;constfloatMAX_PAGE=(m_eDirection==kCCScrollViewDirectionHorizontal?getContentSize().width:getContentSize().height)/PAGE_DISTENCE;constfloatMIN_PAGE=0;floatcurrOffset=m_eDirection==kCCScrollViewDirectionHorizontal?getContentOffset().x:getContentOffset().y;floatdeltaOffset=-(currOffset-m_touchBeganOffset);clock_tcurrTime=clock();floatspeed=currTime!=m_touchBeganTime?deltaOffset/(currTime-m_touchBeganTime):0;m_targetPage=m_currPage;if(abs(deltaOffset)>=TURN_PAGE_MIN_OFFSET_RATIO*PAGE_DISTENCE){//滑动距离大于某一阈值.if(deltaOffset>0){m_targetPage=m_currPage+1;}elseif(deltaOffset<0){m_targetPage=m_currPage-1;}}elseif(abs(speed)>=TURN_PAGE_SPEED){//速度大于某一阈值.if(speed>0){m_targetPage=m_currPage+1;}elseif(speed<0){m_targetPage=m_currPage-1;}}if(m_targetPage>MAX_PAGE)m_targetPage=MAX_PAGE;elseif(m_targetPage<MIN_PAGE)m_targetPage=MIN_PAGE;floattargetOffset=-m_targetPage*(m_eDirection==kCCScrollViewDirectionHorizontal?getViewSize().width:getViewSize().height);floatpageDurateion=0.5;CCPointtargetPointOffset=m_eDirection==kCCScrollViewDirectionHorizontal?ccp(targetOffset,getContentOffset().y):ccp(getContentOffset().x,targetOffset);setContentOffsetInDuration(targetPointOffset,pageDurateion);m_currPage=m_targetPage;returntrue;}voidCCScrollView::__pageTouchCancel(){if(!m_bPaged||(m_eDirection!=kCCScrollViewDirectionHorizontal&&m_eDirection!=kCCScrollViewDirectionVertical))return;__pageClearTouch();}voidCCScrollView::__pageClearTouch(){//clear所有状态m_touchBeganOffset=0;m_touchBeganTime=0;m_targetPage=m_currPage;}
修改后的CCScrollView.h, CCScrollView.cpp这里是修改后的文件, 可以直接下载覆盖.
CCScrollView.zip
注意:上述代码仅在cocos2dx-2.2.2和cocos2dx-2.2.1版本上验证通过. 其他版本请根据上述原理做适当的修改~~~
Written withStackEdit.