原来在公司被迫加班加点赶工,用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. Good Numbers

    Problem Description If we sum up every digit of a number and the result can be exactly divided by 10 ...

  2. Java---俄罗斯方块小游戏

    去年就已经学了这个技术了,一直没去写,现在抽个时间写了个俄罗斯方块游戏. 只有简单的新游戏,暂停,继续,积分功能.简单的实现了俄罗斯的经典功能. 不介绍了,有兴趣的自己运行一下,后面贴出了图片. 代码 ...

  3. Keepass TAN 记录的使用

    之前一直不知道 Keepass 软件中这个“TAN”是做什么用的,今天看了帮助文档,然后通过维基百科,有了一些大致的了解. 简单来说,一个 TAN 记录用于某些网站增加安全操作使用的.常规下只要用户名 ...

  4. RHEL6彻底禁用ip6的方法

    一.vi  /etc/modprobe.d/disable-ipv6.conf(名字随便起)(RHEL6.0之后没有了/etc/modprobe.conf这个文件) 输入:install ipv6 / ...

  5. 微信开发 提示 Redirect_uri参数错误解决方法

    出现这个问题有多种原因: 1.没有配置网页授权 我们可以根据微信的开发者文档http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d ...

  6. java中如何使正在运行中的线程退出

    终止线程的三种方法      有三种方法可以使终止线程.      1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止.      2.  使用stop方法强行终止线程(这个方法不 ...

  7. 【我所认知的BIOS】—&gt; uEFI AHCI Driver(4) — 第一个Protocol真难搞

    [我所认知的BIOS]-> uEFI AHCI Driver(4) - 第一个Protocol真难搞 LightSeed 4/23/2014 文章对EFI_DRIVER_BINDING_PROT ...

  8. Java为何大行其道

    Java为何大行其道 --专訪传智播客冯威老师 冯威老师,多年来一直从事软件开发和教学工作.先后担任过项目经理.软件架构师.软件开发project师.系统架构师.讲师等.具备丰富的软件开发经验和教学经 ...

  9. [Node.js] Node.js Buffers

    >> node >>fs.readFile('finnish.txt', function(err,data){ console.log(data); }); // Outpu ...

  10. Java经典封装JDBC模板(充分体现面向对象思想)(转)

    程序清单一览 bean类 package com.software.usermanager.bean; public class Users { private String id; private ...