CocosCreator的ScrollView优化
有什么不明白的地方,扫描右方二维码加我微信交流。
ScrollView通常用来显示一些列表内容。例如任务列表,挑战列表,道具列表等等。当显示项目较多时,ScrollView的打开和滑动都会有卡顿。之前我的优化思路是这样的:列表滑动到哪里就创建并显示到哪里,未显示的部分通通移除。有一定的优化效果,但是会产生更多的创建和销毁过程。将其进一步优化,不再销毁和创建新的列表项,而是重复使用。
这篇博客的意图在于提供思路,方法,而不是封装好一个现成的组件给你拿来直接用,这样在遇到类似的列表卡顿问题时基本全都能解决。
简述一下优化思路
滑动列表只创建有限个item,能够充满列表就足够。当ScrollView向上滑动时,检测最上面的一个item位置是否已经滑出列表临界值,如果滑出,则把它移动到当前列表最下面,并更新item上的信息。ScrollView向下滑动时同理。当滑动到上下边缘时做一些特殊处理。
如果是和服务器有通信的列表,例如显示1000个item的排行榜,则可以分批请求数据。和服务器协议好,每次请求100条数据。ScrollView在每次滑动到列表底部的时候去请求新的数据,滑动到底部Creator有自带的方法监听,点击查看ScrollViewAPI文档。
点击连接获取Demo。
Demo是基于CocosCreator1.9.3版本,重在理解优化原理,任何游戏遇到列表卡顿问题都可以使用这个思路来解决。
Demo中,使用的item结构非常简单,图片也只有一张,所以相比我们游戏中的实际情况要好很多,我们使用1000条模拟数据来对比。
优化效果
通过实际测试,可以看到,未优化过的列表创建时卡顿感觉明显,优化过的列表秒开。
代码中主要的实现逻辑如下:
- 在update实时获取一下最上和最下item的位置;
- 将上一步骤中获取到的两个位置和上下临界位置做比较;
- 如果上item超过临界值,则上item移动到列表尾部,并更新信息;
- 如果下item超过临界值,则下item移动到列表首部,并更新信息;
- 当到达列表边缘时,不做任何处理。
下面对主要代码做下简要分析,在看分析前,一定要现看Demo,并运行起来
以下是几个比较重要的参数值,可以自行设置
this.itemHeight = 120;//设置每个item的高 this.topIndex = 0;//最上面的item索引id this.bottomIndex = 7;//最下面的item索引id this.offsetY = 80;//上下临界坐标补充,点击查看原理,值太小会出现item在边界闪动的情况,因为item可能此时在上下两边时均符合移动的情况,所以就会无限循环移动,此值建议自行调整 //如果觉得这种获取临界坐标的方式比较麻烦,可以多创建几个item,指定屏幕的上下边为边界 let scrollViewPos = this.scrollView.position;//scrollView以屏幕中心为原点的坐标,请自行计算出来 this.topExtremeDistance = scrollViewPos.y + this.scrollView.height / 2 + this.offsetY;//获取item能到达的屏幕上边界y坐标 this.bottomExtremeDistance = scrollViewPos.y - this.scrollView.height / 2 - this.offsetY;//获取item能到达的屏幕下边界y坐标
以下是实现item上下移动的主代码
updateItemsPos (dt) { if (!!this.itemsArr && !!this.itemsArr[this.bottomIndex]) { //获取上下item当前的坐标 let topPos = cc.pSub(this.itemsArr[this.topIndex].convertToWorldSpaceAR(cc.v2(0, 0)), cc.v2(cc.winSize.width / 2, cc.winSize.height / 2)); let bottomPos = cc.pSub(this.itemsArr[this.bottomIndex].convertToWorldSpaceAR(cc.v2(0, 0)), cc.v2(cc.winSize.width / 2, cc.winSize.height / 2)); //检测上item是否超过边界 if (topPos.y > this.topExtremeDistance) { if (this.bottomIndex >= this.data.length - 1) { return; } this.updateItem(this.itemsArr[this.topIndex], this.data[this.bottomIndex + 1], this.bottomIndex + 1); this.topIndex ++; this.bottomIndex ++; //检测下item是否超过边界 } else if (bottomPos.y < this.bottomExtremeDistance) { if (this.topIndex < 1) { return; } this.updateItem(this.itemsArr[this.bottomIndex], this.data[this.topIndex - 1], this.topIndex - 1); this.topIndex --; this.bottomIndex --; } } },
有问题的童鞋可以在下方留言。