Cocos2d-x 3.x封装PageView(1)
2015年03月23日 10:20
0 点赞
0 评论
更新于 2025-11-21 18:12
显示效果
先来看一下显示效果。由于是在模拟器上录像的,所以看上去会有些卡顿,不过在真机上测试就不会出现这种情况了。接下来,我们直接看代码。
代码实现
PageView.h
// XKPageView.h
// XKPageView
// Created by Joueu on 14 - 11 - 26.
#ifndef __XKPageView__XKPageView__
#define __XKPageView__XKPageView__
#include "cocos2d.h"
#include "cocos-ext.h"
USING_NS_CC;
USING_NS_CC_EXT;
class XKPageView;
// 定义XKPageView的委托类
class XKPageViewDelegate {
public:
virtual ~XKPageViewDelegate() {};
XKPageViewDelegate() {};
// 纯虚函数,用于获取每页的大小
virtual Size sizeForPerPage() = 0;
// 虚函数,当PageView滚动时调用
virtual void pageViewDidScroll(XKPageView *pageView) {};
};
// XKPageView类,继承自ScrollView
class XKPageView : public ScrollView {
public:
// 创建XKPageView实例的静态方法
static XKPageView *create(Size size, XKPageViewDelegate *delegate);
// 初始化方法
virtual bool init(Size size, XKPageViewDelegate *delegate);
// 设置每页的大小
void setPageSize(Size size);
// 在指定时间内设置内容偏移量
virtual void setContentOffsetInDuration(Vec2 offset, float dt);
// 设置内容偏移量
virtual void setContentOffset(Vec2 offset);
private:
// 执行动画滚动
void performedAnimatedScroll(float dt);
// 当前页的索引
int current_index;
// 当前的偏移量
float current_offset;
// 调整偏移量的函数
void adjust(float offset);
// 每页的大小
Size pageSize;
// 委托对象
CC_SYNTHESIZE(XKPageViewDelegate *, _delegate, Delegate);
public:
// 页面数量
int pageCount;
// 添加页面
void addPage(Node *node);
// 根据索引获取页面
Node *getPageAtIndex(int index);
};
#endif /* defined(__XKPageView__XKPageView__) */
XKPageView.cpp
// XKPageView.cpp
// XKPageView
// Created by Joueu on 14 - 11 - 26.
#include "XKPageView.h"
#define XKPAGEVIEW_TAG 10086
// 创建XKPageView实例
XKPageView *XKPageView::create(Size size, XKPageViewDelegate *delegate) {
XKPageView *page = new XKPageView();
if (page && page->init(size, delegate)) {
page->autorelease();
} else {
CC_SAFE_RELEASE(page);
}
return page;
}
// 初始化XKPageView
bool XKPageView::init(Size size, XKPageViewDelegate *delegate) {
if (!ScrollView::initWithViewSize(size)) {
return false;
}
// 必须有委托对象,否则抛出断言错误
CCASSERT(delegate, "delegate should not be NULL!");
setDelegate(delegate);
if (_delegate) {
// 获取每页的大小
pageSize = _delegate->sizeForPerPage();
}
// 初始化数据
pageCount = 0;
current_index = 0;
this->setTouchEnabled(false);
// 创建触摸事件监听器
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = [&](Touch *touch, Event *event) {
_dragging = false;
if (_direction == ScrollView::Direction::HORIZONTAL) {
current_offset = this->getContentOffset().x;
} else {
current_offset = this->getContentOffset().y;
}
return true;
};
listener->onTouchMoved = [&](Touch *touch, Event *event) {
float start, end;
if (_direction == ScrollView::Direction::HORIZONTAL) {
start = touch->getStartLocation().x;
end = touch->getLocation().x;
} else {
start = touch->getStartLocation().y;
end = touch->getLocation().y;
}
float offset = end - start;
// * 2的作用是调节滚动速度,需要调滑动速度的可以改这个值
if (_direction == ScrollView::Direction::HORIZONTAL)
this->setContentOffset(Vec2(current_offset + offset * 2, 0));
else
this->setContentOffset(Vec2(0, current_offset + offset * 2));
};
listener->onTouchEnded = [&](Touch *touch, Event *event) {
float start = current_offset, end;
if (_direction == ScrollView::Direction::HORIZONTAL) {
end = this->getContentOffset().x;
} else {
end = this->getContentOffset().y;
}
float offset = end - start;
this->adjust(offset);
_dragging = true;
};
// 添加触摸事件监听器
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
return true;
}
// 调整偏移量
void XKPageView::adjust(float offset) {
Vec2 vec;
float xOrY;
if (_direction == ScrollView::Direction::HORIZONTAL) {
vec = Vec2(-(current_index * (pageSize.width)), 0);
xOrY = pageSize.width;
} else {
vec = Vec2(0, -(current_index * (pageSize.height)));
xOrY = pageSize.height;
}
// 小于50回到原来位置
if (abs(offset) < 50) {
this->setContentOffsetInDuration(vec, 0.1f);
return;
}
int i = abs(offset / (xOrY)) + 1;
if (offset < 0) {
current_index += i;
} else {
current_index -= i;
}
if (current_index < 0) {
current_index = 0;
} else if (current_index > 10) {
current_index = 10;
}
if (_direction == ScrollView::Direction::HORIZONTAL) {
vec = Vec2(-(current_index * (pageSize.width)), 0);
} else {
vec = Vec2(0, -(current_index * (pageSize.height)));
}
this->setContentOffsetInDuration(vec, 0.15f);
}
// 设置内容偏移量
void XKPageView::setContentOffset(Vec2 offset) {
ScrollView::setContentOffset(offset);
if (_delegate != nullptr) {
_delegate->pageViewDidScroll(this);
}
}
// 在指定时间内设置内容偏移量
void XKPageView::setContentOffsetInDuration(Vec2 offset, float dt) {
ScrollView::setContentOffsetInDuration(offset, dt);
this->schedule(CC_SCHEDULE_SELECTOR(XKPageView::performedAnimatedScroll));
}
// 执行动画滚动
void XKPageView::performedAnimatedScroll(float dt) {
if (_dragging) {
this->unschedule(CC_SCHEDULE_SELECTOR(XKPageView::performedAnimatedScroll));
return;
}
if (_delegate != nullptr) {
_delegate->pageViewDidScroll(this);
}
}
// 添加页面
void XKPageView::addPage(Node *node) {
if (_direction == ScrollView::Direction::HORIZONTAL) {
node->setPosition(Point(pageCount * pageSize.width + node->getPositionX(), node->getPositionY()));
this->setContentSize(Size((pageCount + 1) * pageSize.width, pageSize.height));
} else {
node->setPosition(Point(node->getPositionX(), pageCount * pageSize.height + node->getPositionY()));
this->setContentSize(Size(pageSize.width, (pageCount + 1) * pageSize.height));
}
node->setTag(pageCount + XKPAGEVIEW_TAG);
_container->addChild(node);
pageCount++;
}
// 根据索引获取页面
Node *XKPageView::getPageAtIndex(int index) {
if (index < pageCount && index >= 0) {
return _container->getChildByTag(index + XKPAGEVIEW_TAG);
}
return NULL;
}
测试用的HelloWorld.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "cocos-ext.h"
#include "XKPageView.h"
USING_NS_CC_EXT;
// HelloWorld类,继承自Layer和XKPageViewDelegate
class HelloWorld : public cocos2d::Layer, public XKPageViewDelegate {
public:
// 创建场景的静态方法
static cocos2d::Scene* createScene();
// 初始化方法
virtual bool init();
// 手动实现“static create()”方法
CREATE_FUNC(HelloWorld);
// 获取容器层
Layer *getContainer();
// 实现XKPageViewDelegate的纯虚函数,获取每页的大小
virtual Size sizeForPerPage();
// 实现XKPageViewDelegate的虚函数,当PageView滚动时调用
virtual void pageViewDidScroll(XKPageView *pageView);
private:
// XKPageView实例
XKPageView *pageView;
// 添加页面的方法
void addPages();
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorld.cpp
#include "HelloWorldScene.h"
USING_NS_CC;
#define COIN_WIDTH 212
#define COIN_GAP 30
#define COIN_COUNT 11
// 创建场景
Scene* HelloWorld::createScene() {
// 创建场景对象
auto scene = Scene::create();
// 创建HelloWorld层对象
auto layer = HelloWorld::create();
// 将层添加到场景中
scene->addChild(layer);
return scene;
}
// 初始化HelloWorld层
bool HelloWorld::init() {
// 调用父类的初始化方法
if (!Layer::init()) {
return false;
}
// 获取可见区域的大小
Size visibleSize = Director::getInstance()->getVisibleSize();
// 创建XKPageView实例
pageView = XKPageView::create(Size(visibleSize.width, COIN_WIDTH), this);
// 设置滚动方向为水平
pageView->setDirection(ScrollView::Direction::HORIZONTAL);
// 设置XKPageView的位置
pageView->setPosition(Point((visibleSize.width - COIN_WIDTH) * 0.5, (visibleSize.height - COIN_WIDTH) * 0.5));
// 添加页面
addPages();
// 设置裁切为false,这样layer溢出pageView的Size还能显示,只是为了演示效果而已
pageView->setClippingToBounds(false);
// 将XKPageView添加到当前层
this->addChild(pageView);
// 测试功能
Node *node = pageView->getPageAtIndex(5);
log("tag = %d", node->getTag());
node->setScale(1.3f);
return true;
}
// 添加页面
void HelloWorld::addPages() {
// 获取硬币精灵的大小
Size coinSize = Sprite::create("coin.png")->getContentSize();
// 循环添加11个页面
for (int i = 0; i < COIN_COUNT; i++) {
// 创建硬币精灵
auto sprite = Sprite::create("coin.png");
// 设置精灵的位置
sprite->setPosition(coinSize.width * 0.5, coinSize.height * 0.5);
// 创建标签
std::string str = StringUtils::format("%d", i);
Label *label = Label::createWithSystemFont(str, "Arial", 60);
// 设置标签的颜色
label->setTextColor(Color4B(0, 0, 0, 255));
// 设置标签的位置
Size size = sprite->getContentSize();
label->setPosition(size.width * 0.5, size.height * 0.5);
// 将标签添加到精灵上
sprite->addChild(label);
// 将精灵添加到XKPageView中
pageView->addPage(sprite);
}
}
// 获取容器层
Layer *HelloWorld::getContainer() {
// 创建层对象
auto layer = Layer::create();
// 获取硬币精灵的大小
Size coinSize = Sprite::create("coin.png")->getContentSize();
// 循环添加11个精灵到容器层
for (int i = 0; i < COIN_COUNT; i++) {
// 创建硬币精灵
auto sprite = Sprite::create("coin.png");
// 设置精灵的位置
sprite->setPosition(coinSize.width * 0.5 + i * (coinSize.width + COIN_GAP), coinSize.height * 0.5);
// 将精灵添加到容器层
layer->addChild(sprite);
// 创建标签
std::string str = StringUtils::format("%d", i);
Label *label = Label::createWithSystemFont(str, "Arial", 60);
// 设置标签的颜色
label->setTextColor(Color4B(0, 0, 0, 255));
// 设置标签的位置
Size size = sprite->getContentSize();
label->setPosition(size.width * 0.5, size.height * 0.5);
// 将标签添加到精灵上
sprite->addChild(label);
}
return layer;
}
// 获取每页的大小
Size HelloWorld::sizeForPerPage() {
// 返回每个Page的Size
return Size(COIN_WIDTH + COIN_GAP, COIN_WIDTH);
}
// 当PageView滚动时调用
void HelloWorld::pageViewDidScroll(XKPageView *pageView) {
// 监听滚动事件,可以在这里写滚动时候要添加的代码,比如缩放
log("pageViewDidScroll");
}
以上代码实现了Cocos2d-x 3.x中PageView的封装,并提供了测试代码。通过这些代码,你可以在项目中使用自定义的PageView组件。