cocos2d-X里提供的CCScrollView里并没有提供滚动条和滚动条背景,我对它进行了一些修改,所用的cocos2d-x 版本为3.0
使用方法
CCBarScrollView* scrollView = CCBarScrollView::create();
scrollView->initWithViewSize((Size(280.0f,150.0f)));
scrollView->setContentSize(Size(280,150*10.0f));
scrollView->setDirection(cocos2d::extension::ScrollView::Direction::VERTICAL);
scrollView->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
scrollView->setBounceable(true);
this->addChild(scrollView,0);
auto bgImage = Scale9Sprite::create("bg_pd_item.png");
auto barImage = Scale9Sprite::create("bg2.png");
scrollView->setBarImage(bgImage, barImage);
// scrollView->setAutoHidden(true);
for (int i = 0; i < 50; i++) {
auto sprite1 = Sprite::create("CloseNormal.png");
sprite1->setPosition(20, 20+i*40);
scrollView->addChild(sprite1);
}
控件代码: 点击打开链接
class CCBarScrollView:public ScrollView
{
public:
static CCBarScrollView* create(Size size, Node* container = NULL);
static CCBarScrollView* create();
using Node::addChild;
void setBarImage(Scale9Sprite* barBg,Scale9Sprite* barImage);
virtual void addChild(Node * child, int zOrder, int tag) override;
void setContentOffset(Point offset, bool animated = false);
void setAutoHidden(bool autoHidden); //设置是否自动隐藏
virtual bool onTouchBegan(Touch *touch, Event *event);
virtual void onTouchEnded(Touch *touch, Event *event);
protected:
void deaccelerateScrolling(float dt);
void updateBarPos();
Scale9Sprite* barBg; //滚动条背景
Scale9Sprite* barImage; //滚动条
bool isAutoHidden; //滚动条是否自动隐藏
};
设置滚动条背景和滚动条
void CCBarScrollView::setBarImage(Scale9Sprite* barBg, Scale9Sprite* barImage)
{
Size contentSize = this->getContentSize();
auto layer1 = LayerColor::create(Color4B(255, 0, 255, 255), contentSize.width, contentSize.height);
layer1->setCascadeColorEnabled(false);
layer1->setPosition( Point(0, 0));
this->addChild(layer1);
Size viewSize = this->getViewSize();
this->barBg = barBg;
this->barBg->setContentSize(Size(20,viewSize.height));
this->barBg->setPosition(Point(viewSize.width - 10,viewSize.height/2));
this->barImage = barImage;
Layer::addChild(this->barBg);
Layer::addChild(this->barImage);
this->barImage->setContentSize(Size(20,viewSize.height*(viewSize.height/contentSize.height)));
this->updateBarPos();
}
设置滚动条是否自动隐藏
void CCBarScrollView::setAutoHidden(bool autoHidden)
{
this->isAutoHidden = autoHidden;
if(this->barImage != nullptr && this->isAutoHidden == true)
{
this->barImage->setVisible(false);
}
}
在父类里,addChild会把子节点加入滚动container里面,但是滚动条和背景应该都在它的父节点上,所以要重载次方法
void CCBarScrollView::addChild(Node *child, int zOrder, int tag)
{
if (_container != child && barBg != child && barImage != child)
{
_container->addChild(child, zOrder, tag);
} else
{
Layer::addChild(child, zOrder, tag);
}
}
在contentOffset发生改变的时候,需要更新滚动条的位置
void CCBarScrollView::setContentOffset(Point offset, bool animated/* = false*/)
{
if (animated)
{ //animate scrolling
this->setContentOffsetInDuration(offset, BOUNCE_DURATION);
}
else
{ //set the container position directly
if (!_bounceable)
{
const Point minOffset = this->minContainerOffset();
const Point maxOffset = this->maxContainerOffset();
offset.x = MAX(minOffset.x, MIN(maxOffset.x, offset.x));
offset.y = MAX(minOffset.y, MIN(maxOffset.y, offset.y));
}
_container->setPosition(offset);
if (_delegate != NULL)
{
_delegate->scrollViewDidScroll(this);
}
}
this->updateBarPos();
// CCLOG("CCBarScrollView::setContentOffset");
}
更新滚动条的位置函数
void CCBarScrollView::updateBarPos()
{
// Point currentOffset = this->_container->getPosition();
Point currentOffset = this->getContentOffset();
Size contentSize = this->getContentSize();
Size viewSize = this->getViewSize();
Size barSize = this->barImage->getContentSize();
Point p =Point(viewSize.width-10,barSize.height/2 + viewSize.height*(-currentOffset.y/contentSize.height));
// CCLOG("barImage %f,%f,%f,%f",p.x,p.y,barSize.width,barSize.height);
// CCLOG("currentOffset %f",currentOffset.y);
if(p.y < barSize.height/2)
{
p.y = barSize.height/2;
}
else if(p.y + barSize.height/2 > viewSize.height)
{
p.y = viewSize.height- barSize.height/2;
}
this->barImage->setPosition(p);
}
滚动条在点击松开的时候,会做减速的运动,所以也要在减速函数做相应的处理
void CCBarScrollView::deaccelerateScrolling(float dt)
{
if (_dragging)
{
this->unschedule(schedule_selector(CCBarScrollView::deaccelerateScrolling));
return;
}
float newX, newY;
Point maxInset, minInset;
_container->setPosition(_container->getPosition() + _scrollDistance);
if (_bounceable)
{
maxInset = _maxInset;
minInset = _minInset;
}
else
{
maxInset = this->maxContainerOffset();
minInset = this->minContainerOffset();
}
newX = _container->getPosition().x;
newY = _container->getPosition().y;
_scrollDistance = _scrollDistance * SCROLL_DEACCEL_RATE;
this->setContentOffset(Point(newX,newY));
if ((fabsf(_scrollDistance.x) <= SCROLL_DEACCEL_DIST &&
fabsf(_scrollDistance.y) <= SCROLL_DEACCEL_DIST) ||
newY >= maxInset.y || newY <= minInset.y ||
newX >= maxInset.x || newX <= minInset.x)
{
if(this->isAutoHidden)
{
FiniteTimeAction *faseOut;
faseOut = FadeOut::create(0.2);
this->barImage->runAction(faseOut);
}
this->unschedule(schedule_selector(CCBarScrollView::deaccelerateScrolling));
this->relocateContainer(true);
}
this->updateBarPos();
}
点击事件开始和结束的处理
bool CCBarScrollView::onTouchBegan(Touch* touch, Event* event)
{
if (!this->isVisible())
{
return false;
}
Rect frame = getViewRect();
//dispatcher does not know about clipping. reject touches outside visible bounds.
if (_touches.size() > 2 ||
_touchMoved ||
!frame.containsPoint(touch->getLocation()))
{
CCLOG("outside visible bounds.");
return false;
}
if (std::find(_touches.begin(), _touches.end(), touch) == _touches.end())
{
_touches.push_back(touch);
// CCLOG("_touches.push_back");
}
if (_touches.size() == 1)
{ // scrolling
_touchPoint = this->convertTouchToNodeSpace(touch);
_touchMoved = false;
_dragging = true; //dragging started
_scrollDistance = Point(0.0f, 0.0f);
_touchLength = 0.0f;
if(isAutoHidden)
{
FiniteTimeAction *faseIn;
faseIn = FadeIn::create(0.2);
this->barImage->setVisible(true);
this->barImage->setOpacity(0);
this->barImage->runAction(faseIn);
}
// CCLOG("scrolling");
}
else if (_touches.size() == 2)
{
_touchPoint = (this->convertTouchToNodeSpace(_touches[0]).getMidpoint(
this->convertTouchToNodeSpace(_touches[1])));
_touchLength = _container->convertTouchToNodeSpace(_touches[0]).getDistance(
_container->convertTouchToNodeSpace(_touches[1]));
_dragging = false;
this->barImage->setVisible(false);
// CCLOG("_dragging");
}
return true;
}
void CCBarScrollView::onTouchEnded(Touch* touch, Event* event)
{
if (!this->isVisible())
{
return;
}
auto touchIter = std::find(_touches.begin(), _touches.end(), touch);
bool isDeaccelerateScrolling = false;
if (touchIter != _touches.end())
{
if (_touches.size() == 1 && _touchMoved)
{
isDeaccelerateScrolling = true;
this->schedule(schedule_selector(CCBarScrollView::deaccelerateScrolling));
}
_touches.erase(touchIter);
}
if (_touches.size() == 0)
{
if(this->isAutoHidden && isDeaccelerateScrolling == false)
{
FiniteTimeAction *faseOut;
faseOut = FadeOut::create(0.2);
this->barImage->runAction(faseOut);
}
_dragging = false;
_touchMoved = false;
}
}