[cocos2dx]让CCScrollView支持分页

做过IOS开发的朋友, 肯定知道UIScrollView有一个isPaged属性. 当设置其为true的时候, 滑动会自动分页. 即, 每次滑动之后, 会停止在整页的位置. 当开始介入cocos2dx开发的时候, 却发现跟UIScrollView接口十分相似的CCScrollView却没有这个分页属性. 于是手动实现了一个.



关键字

cocos2dx, CCScrollView, page, UIScrollView

基础知识

在常见的图形引擎中, 滑动组件的定义里有两个重要的概念

  • viewSize: 这个大小值得是组件占用屏幕的大小. 即实际大小.
  • contentSize: 这个大小是一个虚拟的大小. 我们之所以要滚动, 必然是因为需要展示的内容比现实的屏幕空间大. 我们需要滚动屏幕, 才能浏览到所有的显示内容. 这个contentSize即是我们虚拟出来的, 需要展示的所有内容加起来的大小.
  • 分页: 最典型的例子就是iPhone的主界面. 我们不能任意指定一个位置, 让滑动固定在那里. 它要么停留在第一页, 要么停留在第二页, 不会是第0.5页. 每一页的大小是viewSize决定的. 那么总页数就是total_page_count = ceil(viewSize / contentSize )

CCScrollView源码查看

我们知道, cocos2dx的触摸都是通过CCTouchDelegate来实现的. 如果对cocos2dx的touch机制不熟悉的, 可以参考博客.

简单介绍ccTouchBegan方法的功能:

ccTouchBegancocos2dxtouch机制的第一个方法. 这个方法的接口如下:

bool CCScrollView::ccTouchBegan(CCTouch* touch, CCEvent* event)

返回的bool值, 告诉cocos2dx中touch事件的管理者CCTouchDispatcher, 当前组件是否处理这一次触摸:

  • 如果返回true, 则CCTouchDispatcher会根据用户的touch动作, 在后续调用本组件的ccTouchMoved, ccTouchEnded, ccTouchCancelled方法.
  • 如果返回false, 则表示当前组件不处理此次touch事件. 后续的三个方法不会被调用.

CCScrollView::ccTouchBegan()解析

无码无真相, 先贴一张代码图:

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;
}

我把跟本博文主题关系不大的代码用省略号(…)代替了. 下面是相关代码解释:

  • tag-1 当此组件隐藏的时候, 不予处理.
  • tag-2 当此触摸点数大于2或者触摸点不在当前组件的显示范围内的时候, 不予处理.
  • tag-3 这是我们的重点. 当触摸点数等于1的时候, 处理滑动操作.
  • tag-4 触摸点等于2的时候, 响应缩放处理.

源代码的修改

变量申明及方法增加

CCScrollView.h文件中增加以下成员变量和方法:

//CCScrollView.h
public:
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的滑动被触发的时候调用
bool __pageTouchEnd(); //在ScrollView的滑动停止的时候调用
void __pageTouchCancel(); //在滑动被取消的时候调用
void __pageClearTouch(); //在一次滑动结束的时候调用

方法的实现:

四个方法的代码可能要占用一些篇幅, 所以在这里, 先简要讲一下原理:

  1. 在touch开始的时候, 记录一下当时时间m_touchBeganTime和开始滑动的位置m_touchBeganOffset.
  2. 在touch结束的时候, 获取结束时刻的时间和位置. 我们有两个标准来判断应该翻页还是停留在上一页:
    • 如果滑动距离超过一页距离的一半(或者是其他阈值),那么判断为用户希望翻到下一页(或上一页)
    • 如果滑动速度超过一个阈值, 那么判断为用户希望翻到下一页(或上一页)
  3. 当做了判断之后, 即可滑动到对应的位置.

下面是四个方法的实现, 实现了上述原理.

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所有状态
m_touchBeganOffset = 0;
m_touchBeganTime = 0;
m_targetPage = m_currPage;
}

修改后的CCScrollView.h, CCScrollView.cpp

这里是修改后的文件, 可以直接下载覆盖.

CCScrollView.zip

注意:上述代码仅在cocos2dx-2.2.2cocos2dx-2.2.1版本上验证通过. 其他版本请根据上述原理做适当的修改~~~

Written with StackEdit.

[cocos2dx]让CCScrollView支持分页的更多相关文章

  1. ASP.NET 为GridView添加序号列,且支持分页连续累计显示

    为GridView添加序号列,且支持分页连续累计显示,废话不多说,直接上代码: <%@ Page Language="C#" AutoEventWireup="tr ...

  2. 移动开发之浅析cocos2d-x的中文支持问题

    题记:这阵子一直在学习cocos2d-x,其跨平台的特性确实让人舒爽,引擎的框架概念也很成熟,虽然相应的第三方工具略显单薄,但也无愧是一件移动开发的利器啊,有兴趣的朋友有时间就多了解吧. 使用引擎的过 ...

  3. 【jsPDF】jsPDF插件实现将html页面转换成PDF,并下载,支持分页

    1.目的:在前段是 jQuery库 或者 VUE库 或者两者混合库,将html 页面和数据 转换成PDF格式并下载,支持分页 1.项目背景: 对客户报修记录进行分类统计,并生成各种饼图.柱状图.线性图 ...

  4. 页面直接导出为PDF文件,支持分页与页边距

    将WEB页面直接导出为pdf文件是经常会用到的一个功能,尤其是各种报表系统.总结了一下目前几种主流的做法: 在后端用代码生成pdf文件,比如iText一类: 在后端抓取页面并生成pdf文件,比如pha ...

  5. Cocos2d-x多语言支持解决方式

    很多其它相关内容请查看本人博客:http://www.bokeyi.com/ll/category/cocos2d-x/ 利用.plist文件让Cocos2d-x轻松支持多语言. .plist文件类似 ...

  6. cocos2dx旧版本支持arm64修改

    修改的版本是cocos2dx.2.2 1.在neon_matrix_impl.c中修改 #if defined(__ARM_NEON__)为 #if defined(_ARM_ARCH_7) 2.在m ...

  7. [cocos2d-x]OPENGL ES支持的像素格式

    OPENGL ES最多支持32位颜色值. 支持的像素格式有以下几种: 客户端格式 GL格式 GL数据类型 字节数 RGBA8888 GL_RGBA GL_UNSIGNED_BYTE 4 RGB888 ...

  8. [cocos2dx] 让UIButton支持disable状态

    摘要: 主要解决cocos2dx-2.2.2版本中, UIButton显示不了disable状态图的问题. 顺便, 理解了一下cocos2dx中UIWidget的渲染原理. 博客: http://ww ...

  9. 【转】让 cocos2d-x 的 CCHttpRequest 支持https

    肖锐(Cooki)个人原创,欢迎转载,转载请注明地址,肖锐(Cooki)的技术博客 http://blog.csdn.net/xiao0026  由于游戏用到了网络头像, 今天发现换成facebook ...

随机推荐

  1. kFreeBSD 7.0于2013/05/04发布 桌面环境 GNOME 3....

    kFreeBSD 7.0于2013/05/04发布 桌面环境 GNOME 3.4, KDE 4.8.4, Xfce 4.8, and LXDE

  2. [moka同学笔记]yii2.0表单的使用

    1.创建model   /biaodan.php <?php /** * Created by PhpStorm. * User: moka同学 * Date: 2016/08/05 * Tim ...

  3. es配置说明

    cluster代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的.es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来 ...

  4. C#知识点总结【1】

    值类型和引用类型 从概念上看,其区别是值类型直接存储其值,引用类型存储值的引用. 在内存当中的状态,值类型存储在堆栈(zhan)中,而引用类型存储在托管堆上. ; int j = i; 上面的例子中 ...

  5. C++引用笔记

    1.什么是引用: 百度百科里的解释:引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样.用&符号表示 举例: using namespace std; int _tmai ...

  6. SharePoint 2013 单一页面赋设计权限

    本文介绍SharePoint的使用中,断开单一页面权限,给用户编辑权限以及操作中遇到的问题,希望给相关需要的人一个参考. 1.首先进入页面库,找到我们的页面,进入共享,如下图: 2.在弹出的窗口中选择 ...

  7. System.Data.Dbtype转换为System.Data.SqlDbType

    最近在做一些OM Mapping的准备工作,新学了一招. 如果要将System.Data.Dbtype转换为System.Data.SqlDbType,以前以为要写Switch Case语句.其实有很 ...

  8. JSP Model模式

    用JSP开发的Web应用模型可以分为Model1和Model2 对于小型的Web应用,通常可以使用模型1来完成. 模型1可以分为两种方式: 一种是完全使用JSP页面来开发Web应用: 另一种是使用JS ...

  9. IOS UICollectionView基础+UICollectionViewFlowLayout基础

    UICollectionView在众多控件中也算是比较常用的了,像淘宝在浏览宝贝时采用的就是UICollectionView,对于UICollectionView->UICollectionVi ...

  10. iOS之 Mac下抓包工具使用wireshark

    主要是mac上面网卡的授权 分三个步骤:    1.wireshark安装        wireshark运行需要mac上安装X11,mac 10.8的系统上默认是没有X11的.先去http://x ...