****************************************************************************
时间:2015-01-13
作者:Sharing_li
转载出处:http://www.jb51.cc/article/p-asmvisks-rx.html
****************************************************************************
我们在使用cocos2dx的tableVIEw和ScrollVIEw的时候,如果要显示的内容非常多,我们不方便确认当前浏览的内容处于什么位置,也不方便快速浏览。这时我们需要一个滚动条来帮忙,但cocos2dx里面没有这个控件,所以呢,这里我给大家设计了一个滚动条控件Scrollbar,可以非常方便的使用。讲解之前,先看看效果图吧:
看了效果图之后,我们来确认下功能需求:
1、通过滑动tableVIEw或ScrollVIEw,右边的滑块也跟着滑动;
2、tableVIEw或ScrollVIEw滑到底时,右边的滑块也滑到底了;
3、当点击右边的滑块滑动时,左边的tableVIEw或ScrollVIEw也跟着滑动;
4、滑块滑到底时,tableVIEw或ScrollVIEw也滑到底了;
5、当点击右边的滑块背景时,即示例黄色部分,tableVIEw或ScrollVIEw和滑块都跟者滑动;
6、当tableVIEw或ScrollVIEw的内容动态增加时,滑块的大小也动态改变;
7、控件水平和垂直都可以使用,示例只展示了垂直效果,水平同理;
大致的功能就这么多啦,那么就来看看代码怎么写吧,我们定义一个类Scrollbar:
Scrollbar.h头文件
@H_502_48@#ifndef _SCRolL__bar__H_#define _SCRolL__bar__H_#include "cocos2d.h"#include "cocos-ext.h"USING_NS_CC;USING_NS_CC_EXT;enum SclbarDirection{ DIR_NODIR = 0,DIR_VERTICAL,DIR_HORIZenTAL,};class Scrollbar : public cocos2d::Layer{public: Scrollbar(); ~Scrollbar(); /** * 因为九宫图不能缩小到比实际图片要小,所以传入的图片的实际大小要足够小,否则slIDer的大小会有问题 */ static Scrollbar * create(Scale9Sprite * bar_bg,Scale9Sprite * bar_slIDer,tableVIEw * tableVIEw,SclbarDirection dir); static Scrollbar * create(const char * bar_bgfile,const char * bar_slIDerfile,SclbarDirection dir); bool myInit(Scale9Sprite * bar_bg,SclbarDirection dir);protected: virtual bool ontouchBegan(touch* touch,Event* pEvent); virtual voID ontouchmoved(touch *ptouch,Event *pEvent); virtual voID ontouchended(touch *ptouch,Event *pEvent); virtual voID update(float dt) overrIDe; /** * 动态改变slIDer的大小 */ voID updateSlIDer();private: tableVIEw * m_pTarget; Scale9Sprite * m_pBg; Scale9Sprite * m_pSlIDer; SclbarDirection m_direction; Size m_preContentSize; Size m_vIEwSize; bool m_slIDertouched; Vec2 m_firsttouch; Vec2 m_slIDerCurPos; Vec2 m_targetCurPos;};#endif
代码中已给出了部分注释,我们用了九宫图Scale9Sprite来显示滑块和滑块背景图片,因为Scale9Sprite在缩放时,图片效果很好,不会因为拉伸而使得图片效果变质。值得注意的是,如果你的图片的实际大小是size这么大,那么Scale9Sprite不能缩小到比size小,而相反的会放大。所以传入的图片要足够的小,下面再来看看具体的实现:
首先初始化数据:
/*** 初始化各个数据*/bool Scrollbar::myInit(Scale9Sprite * bar_bg,SclbarDirection dir){ if (!Layer::init()) { return false; } m_pBg = bar_bg; m_pSlIDer = bar_slIDer; m_pTarget = tableVIEw; m_direction = dir; m_preContentSize = m_pTarget->getContainer()->getContentSize(); m_vIEwSize = m_pTarget->getVIEwSize(); if (m_direction == DIR_VERTICAL) { m_pBg->setContentSize(Size(m_pBg->getContentSize().wIDth,m_vIEwSize.height)); m_pBg->setposition(Vec2(m_pBg->getContentSize().wIDth / 2,0)); m_pSlIDer->setpositionX(m_pBg->getContentSize().wIDth / 2); } else if (m_direction == DIR_HORIZenTAL) { m_pBg->setContentSize(Size(m_vIEwSize.wIDth,m_pBg->getContentSize().height)); m_pBg->setposition(Vec2(0,-m_pBg->getContentSize().height / 2)); m_pSlIDer->setpositionY(-m_pBg->getContentSize().height / 2); } this->addChild(m_pBg,0); this->updateSlIDer(); this->addChild(m_pSlIDer,1); this->scheduleUpdate(); auto ListenerT = EventListenertouchOneByOne::create(); ListenerT->ontouchBegan = CC_CALLBACK_2(Scrollbar::ontouchBegan,this); ListenerT->ontouchmoved = CC_CALLBACK_2(Scrollbar::ontouchmoved,this); ListenerT->ontouchended = CC_CALLBACK_2(Scrollbar::ontouchended,this); ListenerT->setSwallowtouches(false); Director::getInstance()->getEventdispatcher()->addEventListenerWithSceneGraPHPriority(ListenerT,this); return true;}
我们来看看updateSlIDer函数如何改变滑块slIDer的大小:
voID Scrollbar::updateSlIDer(){ float ratio = 0.0; if (m_direction == DIR_VERTICAL) { ratio = m_vIEwSize.height / m_preContentSize.height; m_pSlIDer->setContentSize(Size(m_pSlIDer->getContentSize().wIDth,m_vIEwSize.height * ratio)); } else if (m_direction == DIR_HORIZenTAL) { ratio = m_vIEwSize.wIDth / m_preContentSize.wIDth; m_pSlIDer->setContentSize(Size(m_vIEwSize.wIDth * ratio,m_pSlIDer->getContentSize().height)); } //如果要显示的内容的尺寸比视图大小小,则隐藏滑块slIDer this->setVisible( !(ratio >= 1) );}
我弄了一个定时器,来监听tableVIEw或ScrollVIEw的滑动,即偏移:
voID Scrollbar::update(float dt){ //判断当前内容是否有增减,因为内容的增减会影响ContenSize,从而修改slIDer的大小 auto curContentSize = m_pTarget->getContainer()->getContentSize(); if ( !(fabsf(curContentSize.height - m_preContentSize.height) <= 0.00001) || !(fabsf(curContentSize.wIDth - m_preContentSize.wIDth) <= 0.00001) ) { m_preContentSize = curContentSize; this->updateSlIDer(); } //设置slIDer的位置 if (m_direction == DIR_VERTICAL) { //调整滑块的位置 auto curOffset = m_pTarget->getContentOffset() + (m_preContentSize - m_vIEwSize) / 2; auto slIDerOffset = curOffset.y / (m_vIEwSize.height - curContentSize.height) * (m_vIEwSize.height - m_pSlIDer->getContentSize().height); //判断滑块是否滑出界限 if (fabsf(slIDerOffset) > (m_vIEwSize.height - m_pSlIDer->getContentSize().height) / 2) { return ; } m_pSlIDer->setpositionY(slIDerOffset); } else if (m_direction == DIR_HORIZenTAL) { auto curOffset = m_pTarget->getContentOffset() - (m_preContentSize - m_vIEwSize) / 2; auto slIDerOffset = -curOffset.x / (m_vIEwSize.wIDth - curContentSize.wIDth) * (m_vIEwSize.wIDth - m_pSlIDer->getContentSize().wIDth); if (fabsf(slIDerOffset) > (m_vIEwSize.wIDth - m_pSlIDer->getContentSize().wIDth) / 2) { return ; } m_pSlIDer->setpositionX(slIDerOffset); }}
注意的是:tableVIEw或ScrollVIEw的可滑动大小和滑块的可滑动大小不一样,所以二者要想同步的话,要成比例滑动。
再来看看滑块的滑动以及滑块背景点击这一块的实现:
先看看ontouchBegan:
bool Scrollbar::ontouchBegan(touch* touch,Event* pEvent){ m_slIDerCurPos = m_pSlIDer->getposition(); m_targetCurPos = m_pTarget->getContentOffset(); auto touchPoint = touch->getLocation(); m_firsttouch = touchPoint; //将触摸点转为在当前子层下的坐标 touchPoint = this->convertToNodeSpace(touchPoint); //只响应点击了滑块背景的触摸 if (!m_pBg->getBoundingBox().containsPoint(touchPoint)) { return false; } //如果先点击了滑块,则设置标志 if (m_pSlIDer->getBoundingBox().containsPoint(touchPoint)) { m_slIDertouched = true; } else//如果没有点击滑块,则点击的是滑块背景图 { if (m_direction == DIR_VERTICAL) { //通过调整m_pTarget的偏移,从而调整了滑块slIDer的位置,因为update函数会一直监听m_pTarget的偏移 auto offset = touchPoint.y - m_slIDerCurPos.y; if (touchPoint.y <= 0) { offset += m_pSlIDer->getContentSize().height / 2; } else { offset -= m_pSlIDer->getContentSize().height / 2; } auto newOff = m_targetCurPos.y + offset / (m_pSlIDer->getContentSize().height - m_vIEwSize.height) * (m_preContentSize.height - m_vIEwSize.height); m_pTarget->setContentOffset(Vec2(0,newOff)); } else if (m_direction == DIR_HORIZenTAL) { auto offset = touchPoint.x - m_slIDerCurPos.x; if (touchPoint.x <= 0) { offset += m_pSlIDer->getContentSize().wIDth / 2; } else { offset -= m_pSlIDer->getContentSize().wIDth / 2; } auto newOff = m_targetCurPos.x + offset / (m_vIEwSize.wIDth - m_pSlIDer->getContentSize().wIDth) * (m_preContentSize.wIDth - m_vIEwSize.wIDth); m_pTarget->setContentOffset(Vec2(newOff,0)); } } return true;}
这里有一点要注意的时,我么不需要在触摸函数中修改滑块的位置,因为我们通过修改ScrollVIEw或tableVIEw的偏移,从而间接地改变了滑块的位置,所以我们只需要正确的设置好ScrollVIEw或tableVIEw的位置就可以了,update函数会帮我们解决滑块的位置。
再来看看ontouchmoved:
voID Scrollbar::ontouchmoved(touch *ptouch,Event *pEvent){ //只响应点击了滑块的移动 if (m_slIDertouched) { auto offPos = ptouch->getLocation() - m_firsttouch; if (m_direction == DIR_VERTICAL) { //通过调整m_pTarget的偏移,从而调整了滑块slIDer的位置,因为update函数会一直监听m_pTarget的偏移 auto newOff = m_slIDerCurPos.y + offPos.y; //判断滑块是否滑出界限 if (fabsf(newOff) > (m_vIEwSize.height - m_pSlIDer->getContentSize().height) / 2) { (newOff < 0 ? (newOff = (m_pSlIDer->getContentSize().height - m_vIEwSize.height) / 2) : (newOff = (m_vIEwSize.height - m_pSlIDer->getContentSize().height) / 2)); } newOff -= m_slIDerCurPos.y; m_pTarget->setContentOffset(Vec2(0,m_targetCurPos.y + newOff / (m_pSlIDer->getContentSize().height - m_vIEwSize.height) * (m_preContentSize.height - m_vIEwSize.height))); } else if (m_direction == DIR_HORIZenTAL) { auto newOff = m_slIDerCurPos.x + offPos.x; if (fabsf(newOff) > (m_vIEwSize.wIDth - m_pSlIDer->getContentSize().wIDth) / 2) { (newOff < 0 ? (newOff = (m_pSlIDer->getContentSize().wIDth - m_vIEwSize.wIDth) / 2) : (newOff = (m_vIEwSize.wIDth - m_pSlIDer->getContentSize().wIDth) / 2)); } newOff -= m_slIDerCurPos.x; m_pTarget->setContentOffset(Vec2(m_targetCurPos.x + newOff / (m_vIEwSize.wIDth - m_pSlIDer->getContentSize().wIDth) * (m_preContentSize.wIDth - m_vIEwSize.wIDth),0)); } }}
最后,我们看看ontouchended:
voID Scrollbar::ontouchended(touch *ptouch,Event *pEvent){ m_slIDertouched = false;}
很简单,就一句,还原下滑块slIDer的触摸状态就可以了。到这里,自定义控件Scrollbar已经实现了。那么我们在来看看在代码中如何使用Scrollbar。同样也很简单,看下面的示例:
m_tableVIEw = tableVIEw::create(this,vIEwSize); m_tableVIEw->ignoreAnchorPointForposition(false); m_tableVIEw->setAnchorPoint(Vec2(0.5,0.5)); m_tableVIEw->setposition(Vec2(vIEwSize.wIDth / 2,vIEwSize.height / 2)); m_tableVIEw->setDirection(ScrollVIEw::Direction::VERTICAL); m_tableVIEw->setDelegate(this); m_tableVIEw->setVerticalFillOrder(tableVIEw::VerticalFillOrder::top_DOWN); m_tableVIEw->reloadData(); pVIEw->addChild(m_tableVIEw); auto scrollbar_vr = Scrollbar::create("scrollbar/vr_slIDer_bg.png","scrollbar/vr_slIDer.png",m_tableVIEw,DIR_VERTICAL); scrollbar_vr->setposition(Vec2(vIEwSize.wIDth,vIEwSize.height / 2)); pVIEw->addChild(scrollbar_vr,2);
创建你的tablewVIEw或ScrollVIEw后,只需要创建Scrollbar,设置位置,添加到父节点共三步就可以轻松完成。
这次的内容就讲完了,有疑惑的可以留言。
Demo资源下载出:http://download.csdn.net/detail/sharing_li/8359125
总结以上是内存溢出为你收集整理的cocos2dx之自定义控件ScrollBar的设计全部内容,希望文章能够帮你解决cocos2dx之自定义控件ScrollBar的设计所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)