cocos2dx 实现不一样的ScrollView
原来在公司被迫加班加点赶工,用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 ¶m);
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 ¶m)
{
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处理, 节能减排;
可以直接锁定某个子节点;
两者最大的区别应该在于惯性的处理,
老板的根据滑动速度, 计算惯性速度, 然后等待惯性消失, 之后再调整位置, 这个过程很**.
新版的处理很巧妙, 通过滑动速度, 计算出将被锁定的节点, 之后直接锁定, 对接口的重用要好很多, 并且思路, 实现都很简洁.
转载请注明出处!
cocos2dx 实现不一样的ScrollView的更多相关文章
- Cocos2d-x 学习笔记(21.1) ScrollView “甩出”效果与 deaccelerateScrolling 方法
1. 简介 “甩出”效果是当我们快速拖动container并松开后,container继续朝原方向运动,但是渐渐减速直到停止的效果. ScrollView的onTouchEnded方法会设置Timer ...
- 从零开始のcocos2dx生活(十)ScrollView
目录 简介 基础变量 ScrollViewDelegate Direction _dragging _container _touchMoved _bounceable _touchLength 方法 ...
- 【Cocos2d-x】学习笔记目录
从2019年7月开始学习游戏引擎Cocos2dx,版本3.17. 学习笔记尽量以白话的形式表达自己对源码的理解,而不是大篇幅复制粘贴源码. 本人水平有限,欢迎批评指正! Cocos2d-x 学习笔记 ...
- cocos2d-x ScrollView、TableView
转自:http://codingnow.cn/cocos2d-x/1024.html 在游戏和应用中经常要实现左右滑动展示游戏帮助.以列表显示内容的UI效果,就像android中的Gallery和Li ...
- cocos2dx中的ScrollView
ScrollView由视窗区域(裁剪区域)和内容区域组成,内容区域叫innerContainer. 视窗区域范围:get/setContentSize 内容区域:get/setInnerContain ...
- [cocos2dx]计算scrollview元素的index
scrollview的原生代码没有提供元素对齐功能 通过下面介绍的index计算方法以及scrollview自带的设置位置方法 void setContentOffsetInDuration(CCPo ...
- Cocos2d-x 3.2 大富翁游戏项目开发-第五部分 单机游戏-级别选择ScrollView
于MenuScene.cpp 点击单机游戏后会调用 Director::getInstance()->pushScene(MapChooseScene::createScene()); 进入到关 ...
- cocosStudio制作ScrollView并在cocos2dx 3.0中使用。
使用cocosStudio制作界面基本已成为基础了,之前都是拖动一些 Image.Button的小控件,再用到层容器和滚动层的时候,习惯性的用拖动来改变控件的大小.但是你在把其他的控件拖动到上面的时候 ...
- cocos2dx 3.0 scrollview 在android下面背景變綠色了
在windows上面跑的是OK的, 在android下面跑的時候就變成這樣子了:
随机推荐
- ambari的重新安装
ambari是什么呢? 这里我简单说一下ambari的目的,他的目的就是简化hadoop集群的安装和管理.对于安装简化到什么地步呢?只需要几个命令,在页面上配置几个参数,几百几千个节点的集群就能安装成 ...
- 宁波Uber优步司机奖励政策(2月1日~2月7日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- kafka consumer 分区reblance算法
转载请注明原创地址 http://www.cnblogs.com/dongxiao-yang/p/6238029.html 最近需要详细研究下kafka reblance过程中分区计算的算法细节,网上 ...
- iOS 更好用的打Log方式-显示文件名、行数
单纯的NSLog方式打出的Log没有显示打印语句所在的文件名和行数,下面这种做法会很实用: #ifdef DEBUG # define DLog(fmt, ...) NSLog((@"%s ...
- 在C#中如何确定一个文件是不是文本文件,以及如何确定一个文件的类型
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:在C#中如何确定一个文件是不是文本文件,以及如何确定一个文件的类型.
- WDCP是什么 关于WDCP的详细介绍
WDCP是WDlinux Control Panel的简称,是一套用PHP开发的Linux服务器管理系统以及虚拟主机管理系统,,旨在易于使用Linux系统做为我们的网站服务器,以及平时对Linux服务 ...
- java获得项目绝对路径
在jsp和class文件中调用的相对路径不同. 在jsp里,根目录是WebRoot 在class文件中,根目录是WebRoot/WEB-INF/classes 当然你也可以用System.getPro ...
- PAT---1001. A+B Format (20)
#include<stdio.h> int main(){ //定义要读入的两个整数 int a,b; //定义好放拆项用的数组 ]; //读入两个整数 scanf("%d%d& ...
- C++访问sqlite3实践
Sqlite确实是一个比较好的本地数据库,从接触它的时候就喜欢上了它,它可以在很多情况下简化应用.不过以前都是在Java里面使用,或者Linux C下使用的,现在有个项目(C++)可能我会用到sqli ...
- 对MYSQL IFNULL函数的使用进行了具体的叙述
下文对MYSQL IFNULL函数的使用进行了具体的叙述.供您參考学习.假设您在MYSQL IFNULL函数使用方面遇到过类似的问题,最好还是一看. MYSQL IFNULL(expr1,expr2) ...