原来在公司被迫加班加点赶工,用lua实现的版本:http://www.cnblogs.com/mmc1206x/p/4146911.html

后来因我个人的需要, 用C++实现了一个版本.

蓦然回首, lua那版不忍直视, 设计拙劣, 代码拙劣, 当然, 这都归咎于那时的我太年轻.

效果图

ScrollView.h

 #pragma once

 #include "Base.h"

 class ScrollView : public ccNode {
 public:
     struct Param {
         float scale;
         float width;
         float height;
         float itemWidth;
         float itemHeight;
         float anchorY;
         std::function<void(ccNode *)> enterHandler;
         std::function<void(ccNode *)> leaveHandler;
         Param()
             : scale()
             , width()
             , height()
             , itemWidth()
             , itemHeight()
             , anchorY(0.5f)
             , enterHandler([](ccNode *){})
             , leaveHandler([](ccNode *){})
         {

         }
     };
 public:
     virtual void addChild(ccNode *pNode) override;
     virtual void removeChild(ccNode *pNode, bool cleanup = true) override;

     void gotoIndex(int index)
     {
         _touchIdx = index;
         runUpdate();
     }

     ccNode *getActiveNode()
     {
         return getNode(_activeIdx);
     }

     ccNode *getTouchNode()
     {
         return getNode(_touchIdx);
     }

     ccNode *getNode(u_int index)
     {
         auto &childs = getChildren();
         return index < (u_int)childs.size()
             ? childs.at(index) : nullptr;
     }

     float getNodeOffset(u_int index)
     {
         auto offset = getNodePos(index);
         auto max = getRight();
         auto min = getLeft();
         if (offset <= max && offset >= min)
         {
             offset -= (max - offset) * (_param.scale - );
         }
         else if (offset < min)
         {
             offset -= (max - min) * (_param.scale - );
         }
         return offset;
     }
 private:
     bool onTouchBegan(ccTouch *pTouch, ccEvent *pEvent);
     void onTouchMoved(ccTouch *pTouch, ccEvent *pEvent);
     void onTouchEnded(ccTouch *pTouch, ccEvent *pEvent);
     void onTouchCancelled(ccTouch *pTouch, ccEvent *pEvent);
     bool moveChilds(float diff);
     u_int getNodeIdx(const ccVec2 &worldPoint);
     virtual void update(float dt) override;

     void setActive(u_int index)
     {
         auto pOldNode = getActiveNode();
         auto pNewNode = getNode(index);
         MMC_ASSERT(pNewNode != nullptr);
         if (pOldNode != pNewNode)
         {
             _activeIdx = index;
             if (pOldNode != nullptr)
             {
                 _param.leaveHandler(pOldNode);
             }
             _param.enterHandler(pNewNode);
         }
     }

 private:
     float getNodePos(u_int index)
     {
         return _param.itemWidth * index + _offset;
     }

     float getRight()
     {
         ;
     }

     float getLeft()
     {
         return getRight() - _param.itemWidth;
     }

     bool isActive(u_int index)
     {
         auto offset = getNodePos(index);
         return offset <= getRight() && offset >= getLeft();
     }

     void stopUpdate()
     {
         unscheduleUpdate();
     }

     void runUpdate()
     {
         scheduleUpdate();
     }

 private:
     ScrollView();
     ~ScrollView();
     void mmcInit(const Param &param);

     friend ScrollView *utils::createCocos<ScrollView>(const Param &);

 private:
     ccEventListenerTouchOneByOne *_listener;
     Param _param;
     float _offset;
     int _activeIdx;
     int _touchIdx;
     int _tick;
 };

ScrollView.cpp

 #include "ScrollView.h"

 ScrollView::ScrollView()
     : _listener(nullptr)
     , _activeIdx(INVALID_VALUE)
     , _touchIdx(INVALID_VALUE)
     , _offset()
 {
 }

 ScrollView::~ScrollView()
 {
 }

 void ScrollView::mmcInit(const Param &param)
 {
     if (!Node::init())
     {
         MMC_ASSERT(false);
     }
     _listener = ccEventListenerTouchOneByOne::create();
     _listener->onTouchBegan = CC_CALLBACK_2(ScrollView::onTouchBegan, this);
     _listener->onTouchMoved = CC_CALLBACK_2(ScrollView::onTouchMoved, this);
     _listener->onTouchEnded = CC_CALLBACK_2(ScrollView::onTouchEnded, this);
     _listener->onTouchCancelled = CC_CALLBACK_2(ScrollView::onTouchCancelled, this);
     _listener->setSwallowTouches(true);
     getEventDispatcher()->addEventListenerWithSceneGraphPriority(_listener, this);
     _offset = ;
     _param = param;
     runUpdate();
 }

 void ScrollView::addChild(ccNode *pNode)
 {
     auto childCount = getChildrenCount();
     auto offset = getNodeOffset(childCount);
     pNode->setPositionX(offset);
     pNode->setAnchorPoint(ccVec2(0.5f, _param.anchorY));
     if (_activeIdx == INVALID_VALUE && _touchIdx == INVALID_VALUE)
     {
         _touchIdx = childCount;
         runUpdate();
     }
     Node::addChild(pNode);
 }

 void ScrollView::removeChild(Node *pNode, bool cleanup)
 {
     auto pActive = getActiveNode();
     if (pActive == pNode)
     {
         _activeIdx = INVALID_VALUE;
     }
     auto pTouch = getTouchNode();
     if (pTouch == pNode)
     {
         _touchIdx = INVALID_VALUE;
     }
     Node::removeChild(pNode, cleanup);
 }

 bool ScrollView::onTouchBegan(ccTouch *pTouch, ccEvent *pEvent)
 {
     const auto &size = getContentSize();
     const auto &touchRect = ccRect(
         _param.width * -0.5f,
         _param.height * -_param.anchorY,
         _param.width, _param.height);
     const auto &worldPoint = pTouch->getLocation();
     const auto &localPoint = convertToNodeSpace(worldPoint);
     auto isTouch = touchRect.containsPoint(localPoint);
     if (isTouch)
     {
         _tick = clock();
         stopUpdate();
     }
     return isTouch;
 }

 void ScrollView::onTouchMoved(ccTouch *pTouch, ccEvent *pEvent)
 {
     auto diffOffset = pTouch->getDelta().x;
     auto nowtick = clock();
     auto difftick = nowtick - _tick;
 #ifdef WIN32
     ;
 #else
      / ;
 #endif
     _tick = nowtick;
     _touchIdx = _activeIdx - offsetIndex;
     if (_touchIdx >= getChildrenCount())
     {
         _touchIdx = getChildrenCount() - ;
     }
     )
     {
         _touchIdx = ;
     }
     moveChilds(diffOffset);
 }

 void ScrollView::onTouchEnded(ccTouch *pTouch, ccEvent *pEvent)
 {
     const auto &beg = pTouch->getStartLocation();
     const auto &end = pTouch->getLocation();
     const auto &delta = beg.getDistance(end);
     )
     {
         _touchIdx = getNodeIdx(end);
     }
     runUpdate();
 }

 void ScrollView::onTouchCancelled(ccTouch *pTouch, ccEvent *pEvent)
 {

 }

 void ScrollView::update(float dt)
 {
     auto pMoveNode = getTouchNode();
     if (pMoveNode == nullptr)
     {
         pMoveNode = getActiveNode();
     }

     if (pMoveNode != nullptr)
     {
         auto nowOffset = pMoveNode->getPositionX();
         auto newOffset = nowOffset * 0.1f;
         if (!moveChilds(-newOffset))
         {
             pMoveNode = nullptr;
         }
     }

     if (pMoveNode == nullptr)
     {
         stopUpdate();
     }
 }

 bool ScrollView::moveChilds(float diff)
 {
     _offset += diff;
     auto scaleLength = _param.itemWidth * _param.scale / ;
     auto &childs = getChildren();
     ; i != childs.size(); ++i)
     {
         auto pChild = childs.at(i);
         auto offset = getNodeOffset(i);
          > _param.width)
         {
             pChild->setVisible(false);
         }
         else
         {
             auto newScale = ( - std::abs(offset) / getRight()) * _param.scale;
             ) newScale = ;
             if (newScale > _param.scale) newScale = _param.scale;
             pChild->setScale(newScale);
             pChild->setVisible(true);
             pChild->setPositionX(offset);
             if (isActive(i))
             {
                 setActive(i);
             }
         }
     }
     return std::abs(diff) >= 0.1f;
 }

 u_int ScrollView::getNodeIdx(const ccVec2 &worldPoint)
 {
     const auto &localPoint = convertToNodeSpace(worldPoint);
     const auto &childs = getChildren();
     ccRect rect;
     ; i != childs.size(); ++i)
     {
         rect.origin.x = getNodeOffset(i) - _param.itemWidth / ;
         rect.origin.y = -_param.anchorY * _param.itemHeight;
         rect.size.width = _param.itemWidth;
         rect.size.height = _param.itemHeight;
         if (rect.containsPoint(localPoint))
         {
             return i;
         }
     }
     return INVALID_VALUE;
 }

这么简洁且通俗易懂的代码, 我想就不用填注释了吧.

这个版本的实现思路要比原来的更简洁.

去掉了所谓的惯性控制对象,

去掉了N多变量,

优化了一些接口.

原来,

快速滑动会激发惯性, 当惯性停止时, 再调整位置, 这里用到2个定时器, 一个用于控制惯性, 一个用于调整位置;

单击锁定放在子节点的Touch里响应, 因此要注册很多EventListener;

没有直接锁定功能, 貌似因为设计的问题, 无法加上这个功能, 年代久远, 懒得考察;

以及一堆让现在的我无法忍受的代码.

现在,

取消单独定时器, 所有计时操作都在Node::update里处理, 节能减排;

单击锁定由一个EventListener处理, 节能减排;

可以直接锁定某个子节点;

两者最大的区别应该在于惯性的处理,

老板的根据滑动速度, 计算惯性速度, 然后等待惯性消失, 之后再调整位置, 这个过程很**.

新版的处理很巧妙, 通过滑动速度, 计算出将被锁定的节点, 之后直接锁定, 对接口的重用要好很多, 并且思路, 实现都很简洁.

下载 DEMO, 猛戳这里!!!

转载请注明出处!

cocos2dx 实现不一样的ScrollView的更多相关文章

  1. ‎Cocos2d-x 学习笔记(21.1) ScrollView “甩出”效果与 deaccelerateScrolling 方法

    1. 简介 “甩出”效果是当我们快速拖动container并松开后,container继续朝原方向运动,但是渐渐减速直到停止的效果. ScrollView的onTouchEnded方法会设置Timer ...

  2. 从零开始のcocos2dx生活(十)ScrollView

    目录 简介 基础变量 ScrollViewDelegate Direction _dragging _container _touchMoved _bounceable _touchLength 方法 ...

  3. 【Cocos2d-x】学习笔记目录

    从2019年7月开始学习游戏引擎Cocos2dx,版本3.17. 学习笔记尽量以白话的形式表达自己对源码的理解,而不是大篇幅复制粘贴源码. 本人水平有限,欢迎批评指正! Cocos2d-x 学习笔记 ...

  4. cocos2d-x ScrollView、TableView

    转自:http://codingnow.cn/cocos2d-x/1024.html 在游戏和应用中经常要实现左右滑动展示游戏帮助.以列表显示内容的UI效果,就像android中的Gallery和Li ...

  5. cocos2dx中的ScrollView

    ScrollView由视窗区域(裁剪区域)和内容区域组成,内容区域叫innerContainer. 视窗区域范围:get/setContentSize 内容区域:get/setInnerContain ...

  6. [cocos2dx]计算scrollview元素的index

    scrollview的原生代码没有提供元素对齐功能 通过下面介绍的index计算方法以及scrollview自带的设置位置方法 void setContentOffsetInDuration(CCPo ...

  7. Cocos2d-x 3.2 大富翁游戏项目开发-第五部分 单机游戏-级别选择ScrollView

    于MenuScene.cpp 点击单机游戏后会调用 Director::getInstance()->pushScene(MapChooseScene::createScene()); 进入到关 ...

  8. cocosStudio制作ScrollView并在cocos2dx 3.0中使用。

    使用cocosStudio制作界面基本已成为基础了,之前都是拖动一些 Image.Button的小控件,再用到层容器和滚动层的时候,习惯性的用拖动来改变控件的大小.但是你在把其他的控件拖动到上面的时候 ...

  9. cocos2dx 3.0 scrollview 在android下面背景變綠色了

    在windows上面跑的是OK的,  在android下面跑的時候就變成這樣子了:

随机推荐

  1. Android项目开发全程(四)-- 将网络返回的json字符串轻松转换成listview列表

    前面几篇博文介绍了从项目搭建到获取网络字符串,对一个项目的前期整体工作进行了详细的介绍,本篇接着上篇介绍一下怎么样优雅将网络返回的json字符串轻松转换成listview列表. 先上图,看一下效果. ...

  2. ajax_demo:GET POST发送数据

    GET,通过url发送数据 <%@ page language="java" contentType="text/html; charset=UTF-8" ...

  3. hdoj 1787 GCD Again【欧拉函数】

    GCD Again Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  4. iOS全景

    一张鱼眼图或者6方图,展现出来的全景影像效果图 PanoramaGL PanoramaGL是世界上第一个开源的实现360度全景图像的iOS.Android类库.基于OpenGL 支持球,立方体,圆柱. ...

  5. DrawerLayout和toolbar的使用

    onPostCreate()是Activity完全启动后的调用:在完全启动后的回调设置toolbar 然后在使用 AppCompatActivity 时style要设置为何appCompat相关的样式 ...

  6. ios 利用Reveal来调试界面2--真机调试(步骤详解)

    使用真机调试我们的App界面,如果你的真机是没有越狱的设备,那么使用Reveal来调试UI的步骤是最麻烦的.

  7. 食品药检所LIMS需求分析说明书

    1.XX市食品药检所LIMS系统需求分析... 5 1.1检验业务流程... 5 1.1.1样品受理... 5 1.1.1.1选择受理任务... 5 1.1.1.2录入检品信息... 5 1.1.1. ...

  8. [翻译] GVUserDefaults

    GVUserDefaults Tired of writing all that code to get and set defaults in NSUserDefaults? Want to hav ...

  9. mysql查询随机几条数据(速度快)

    MySql查询随机几条数据 想到了 Max RAND 这几个函数 用以下2种办法都可以实现查询. 速度还行. 几十万数据左右, 没有什么问题. SELECT * FROM `news` WHERE i ...

  10. HTML里面Textarea换行总结

    近期碰到一个数据转来转去转到Textrea里面能否真正按行存放的问题,在这里总结一下:   问题描写叙述: 比方get数据到一个TextArea里面,如“AAA BBB”,想把这段文字在TextAre ...