做過IOS開發(fā)的朋友, 肯定知道UIScrollView有一個isPaged屬性. 當設置其為true的時候, 滑動會自動分頁. 即, 每次滑動之后, 會停止在整頁的位置. 當開始介入cocos2dx開發(fā)的時候, 卻發(fā)現(xiàn)跟UIScrollView接口十分相似的CCScrollView卻沒有這個分頁屬性. 于是手動實現(xiàn)了一個.
cocos2dx, CCScrollView, page, UIScrollView
在常見的圖形引擎中, 滑動組件的定義里有兩個重要的概念
viewSize: 這個大小值得是組件占用屏幕的大小. 即實際大小.contentSize: 這個大小是一個虛擬的大小. 我們之所以要滾動, 必然是因為需要展示的內(nèi)容比現(xiàn)實的屏幕空間大. 我們需要滾動屏幕, 才能瀏覽到所有的顯示內(nèi)容. 這個contentSize即是我們虛擬出來的, 需要展示的所有內(nèi)容加起來的大小.viewSize決定的. 那么總頁數(shù)就是total_page_count = ceil(viewSize / contentSize )我們知道, cocos2dx的觸摸都是通過CCTouchDelegate來實現(xiàn)的. 如果對cocos2dx的touch機制不熟悉的, 可以參考博客.
ccTouchBegan是cocos2dxtouch機制的第一個方法. 這個方法的接口如下:
bool CCScrollView::ccTouchBegan(CCTouch* touch, CCEvent* event)返回的bool值, 告訴cocos2dx中touch事件的管理者CCTouchDispatcher, 當前組件是否處理這一次觸摸:
true, 則CCTouchDispatcher會根據(jù)用戶的touch動作, 在后續(xù)調(diào)用本組件的ccTouchMoved, ccTouchEnded, ccTouchCancelled方法.false, 則表示當前組件不處理此次touch事件. 后續(xù)的三個方法不會被調(diào)用.無碼無真相, 先貼一張代碼圖:
bool CCScrollView::ccTouchBegan(CCTouch* touch, CCEvent* event){ //tag-1 if (!this->isVisible()) { return false; } //tag-2 CCRect frame = 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)))) { return false; } ... //tag-3 if (m_pTouches->count() == 1) { // scrolling m_tTouchPoint = this->convertTouchToNodeSpace(touch); m_bTouchMoved = false; m_bDragging = true; //dragging started m_tScrollDistance = ccp(0.0f, 0.0f); m_fTouchLength = 0.0f; } //tag-4 else if (m_pTouches->count() == 2) { ... m_bDragging = false; } return true;}我把跟本博文主題關系不大的代碼用省略號(…)代替了. 下面是相關代碼解釋:
在CCScrollView.h文件中增加以下成員變量和方法:
//CCScrollView.hpublic: bool isPaged(){ return m_bPaged; }; void setPaged( bool value ){ m_bPaged = value; };PRotected: clock_t m_touchBeganTime; int m_touchBeganOffset; int m_targetPage; int m_currPage; float m_pageAccSpeed; float m_distanceRatioOfTurn; bool m_bPaged; void __pageTouchBegan(); //在CCScrollView的滑動被觸發(fā)的時候調(diào)用 bool __pageTouchEnd(); //在ScrollView的滑動停止的時候調(diào)用 void __pageTouchCancel(); //在滑動被取消的時候調(diào)用 void __pageClearTouch(); //在一次滑動結(jié)束的時候調(diào)用四個方法的代碼可能要占用一些篇幅, 所以在這里, 先簡要講一下原理:
m_touchBeganTime和開始滑動的位置m_touchBeganOffset.下面是四個方法的實現(xiàn), 實現(xiàn)了上述原理.
void CCScrollView::__pageTouchBegan(){ //僅在設置了分頁屬性, 并且只有一個滑動方向的時候, 才支持分頁. if( !m_bPaged || ( m_eDirection != kCCScrollViewDirectionHorizontal && m_eDirection != kCCScrollViewDirectionVertical )) return ; //記錄初試時間和位置 m_touchBeganTime = clock(); m_touchBeganOffset = m_eDirection == kCCScrollViewDirectionHorizontal ? getContentOffset().x : getContentOffset().y;}bool CCScrollView::__pageTouchEnd(){ if( !m_bPaged || ( m_eDirection != kCCScrollViewDirectionHorizontal && m_eDirection != kCCScrollViewDirectionVertical )) return false ; //constant const float PAGE_DISTENCE = m_eDirection == kCCScrollViewDirectionHorizontal ? getViewSize().width : getViewSize().height ; if( PAGE_DISTENCE <= 0 ) return false; const float MAX_PAGE = ( m_eDirection == kCCScrollViewDirectionHorizontal ? getContentSize().width : getContentSize().height ) / PAGE_DISTENCE; const float MIN_PAGE = 0; float currOffset = m_eDirection == kCCScrollViewDirectionHorizontal ? getContentOffset().x : getContentOffset().y; float deltaOffset = -(currOffset - m_touchBeganOffset); clock_t currTime = clock(); float speed = 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; } else if( deltaOffset < 0 ) { m_targetPage = m_currPage - 1; } } else if( abs(speed) >= TURN_PAGE_SPEED ) {//速度大于某一閾值. if( speed > 0 ) { m_targetPage = m_currPage + 1; } else if( speed < 0 ) { m_targetPage = m_currPage - 1; } } if( m_targetPage > MAX_PAGE ) m_targetPage = MAX_PAGE; else if( m_targetPage < MIN_PAGE ) m_targetPage = MIN_PAGE; float targetOffset = -m_targetPage*( m_eDirection == kCCScrollViewDirectionHorizontal ? getViewSize().width : getViewSize().height ); float pageDurateion = 0.5; CCPoint targetPointOffset = m_eDirection == kCCScrollViewDirectionHorizontal ? ccp( targetOffset, getContentOffset().y ) : ccp(getContentOffset().x, targetOffset ); setContentOffsetInDuration(targetPointOffset, pageDurateion); m_currPage = m_targetPage; return true;}void CCScrollView::__pageTouchCancel(){ if( !m_bPaged || ( m_eDirection != kCCScrollViewDirectionHorizontal && m_eDirection != kCCScrollViewDirectionVertical )) return ; __pageClearTouch();}void CCScrollView::__pageClearTouch(){ //clear所有狀態(tài) m_touchBeganOffset = 0; m_touchBeganTime = 0; m_targetPage = m_currPage;}這里是修改后的文件, 可以直接下載覆蓋.
CCScrollView.zip
注意:上述代碼僅在cocos2dx-2.2.2和cocos2dx-2.2.1版本上驗證通過. 其他版本請根據(jù)上述原理做適當?shù)男薷膥~~
Written with StackEdit.
新聞熱點
疑難解答