您的位置:首页 > 移动开发 > Cocos引擎

CocosCreator开发笔记(5)-ScrollView之动态更新的优化原理

2018-01-19 10:08 423 查看
ScrollView是比较常用的UI组件之一,游戏中的任务榜、排行榜都少不了它,实际使用中存在一个问题,例如:在排行榜中要显示前100名玩家,如果真的把这100名玩家的信息全部创建,并加载进ScrollView,对移动设备的宝贵内存会是巨大的浪费。其实玩家在屏幕上总能看到的最多只有7、8项而已,所以实际上只用创建比显示多一点的数量,再通过缓冲区实时动态更新数据,就可以给玩家呈现出尽可能多数量的列表(依内存而定,理论上无限),即节省内存,也不影响性能。

在CocosCreate官方提供的example中,有个ScrollView的例子,就使用了动态更新数据,我把它摘出来做为一个单独的工程,加上中文注释,并对代码稍做改动,运行截图如下所示:



  可以看到,列表中显示一共有100行,但一屏最多展示7.5行,而实际上在内存中只真正创建了15项,所有看到的这100行,都是在上下滚动事件中,通过动态更新这15项的坐标和内容来实现的。要了解它的运行原理,先看下图:



  如图把ScrollView分成三部分,按区域从小到大依次是:

1、屏幕可见区。指屏幕上玩家可看可操作的列表区域,在此demo中有7.5行;

2、缓冲区。指内存中真正创建了的列表所占的区域,在此demo中有15行;

3、content区。指整个ScrollView要显示的区域,在此demo中有100行;

  下面再分三种情况讲解:

1、刚初始化完成时:此时在右侧按钮上提示有100行,实际上只创建了第1-15行,而玩家能看到的是第1-7行。如果玩家想要看到更多,必然会向上或向下滚动屏幕;

2、向上滚动时:在移动设备上,如果玩家想要看到下面的行,所做的操作是触摸往上滑动,则整个content区往上移动,也带动content区的item往上移动,update函数会不断遍历所创建的15项item,如果检测到某item的y坐标超出了缓冲区的上边界(该item已经被玩家看过或不想再看),则把该item往下移动一个缓冲区的高度(移动该item到玩家即将看到的位置),并更新它的显示ID;

3、向下滚动时:同理,content区的item往下移动,update不断遍历所创建的15项item,如果检测到某item的y坐标越过了缓冲区的下边界(该item已经被玩家看过或不想再看),则把该item往上移动一个缓冲区的高度(移动该item到玩家即将看到的位置),并更新它的显示ID;

  关键代码如下所示:

  

// 返回item在ScrollView空间的坐标值
getPositionInView: function (item) {
let worldPos = item.parent.convertToWorldSpaceAR(item.position);
let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
return viewPos;
},

// 每帧调用一次。根据滚动位置动态更新item的坐标和显示(所以spawnCount可以比totalCount少很多)
update: function(dt) {
this.updateTimer += dt;
if (this.updateTimer < this.updateInterval) {
return; // we don't need to do the math every frame
}
this.updateTimer = 0;
let items = this.items;
// 如果当前content的y坐标小于上次记录值,则代表往下滚动,否则往上。
let isDown = this.scrollView.content.y < this.lastContentPosY;
// 实际创建项占了多高(即它们的高度累加)
let offset = (this.itemTemplate.height + this.spacing) * items.length;
let newY = 0;

// 遍历数组,更新item的位置和显示
for (let i = 0; i < items.length; ++i) {
let viewPos = this.getPositionInView(items[i]);
if (isDown) {
// 提前计算出该item的新的y坐标
newY = items[i].y + offset;
// 如果往下滚动时item已经超出缓冲矩形,且newY未超出content上边界,
// 则更新item的坐标(即上移了一个offset的位置),同时更新item的显示内容
if (viewPos.y < -this.bufferZone && newY < 0) {
items[i].setPositionY(newY);
let item = items[i].getComponent('Item');
let itemId = item.itemID - items.length; // update item id
item.updateItem(i, itemId);
}
} else {
// 提前计算出该item的新的y坐标
newY = items[i].y - offset;
// 如果往上滚动时item已经超出缓冲矩形,且newY未超出content下边界,
// 则更新item的坐标(即下移了一个offset的位置),同时更新item的显示内容
if (viewPos.y > this.bufferZone && newY > -this.content.height) {
items[i].setPositionY(newY);
let item = items[i].getComponent('Item');
let itemId = item.itemID + items.length;
item.updateItem(i, itemId);
}
}
}

// 更新lastContentPosY和总项数显示
this.lastContentPosY = this.scrollView.content.y;
this.lblTotalItems.string = "Total Items: " + this.totalCount;
},


  在此demo中,ScrollView列表显示的item其实是个按钮,而它是做为预制资源,其实可以在Creator中编辑成各种UI,并不局限于按钮形式。

  最后,附上该Creator工程完整源代码的github地址:https://github.com/foupwang/CocosCreatorScrollViewDemo.git
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: