面试题: cocos2d-x中 ScrollView,TableView,ListView区别
TableView,ListView都继承自ScrollView
用法区别:
少量数据使用ListView,ScrollView比较好,大量的数据插入及其访问,则用TableView好
为什么?
because:
ListView,ScrollView每次都是加载的所有的item,所有数据量越大,卡顿越是明显
而TableView则是加载的你所看到的几条item,所以即使在多的数据,则只有几条,
所以大量的数据加载,则使用TableView
少量数据则使用ListView,ScrollView
————————————————————————————————————————————————————
Listview很多人都有使用过,当加载大量数据的时候会出现卡顿也是经常有的事情,最主要的问题在于2个地方,
1 一次性创建过多的item 内存分配、ui布局等底层代码的消耗(这个不算最主要)
2 貌似listview的pushback…之类的方法会调用widget的clone十分消耗效率(这个重要…我也是从网上看来的…没看listview的源码)
我们需要的效果是秒加载 无卡顿这个是最主要的目的,其次的目的 如果加载过多的item肯定会造成内存飙升,我记得看到文章说有兄弟踩坑的时候加载大量item导致直接闪退…我在网上看了一部分文章觉得比较好的处理办法就是在scrollview的可视(剪裁区域内)加载能铺满整个区域的item,然后在多增加10个item 也就是说你需要加载的item的数量是scrollview.list.height / (item.heigh + spacingY) + 10 这个数量的item 一般来说也就10多个item的样子 ,整个scrollview只需要用到这么多的item 保证了内存的开销很低并且加载速度会非常的快.接下来的思路就是计算一个顶部最大Y坐标和底部最大Y坐标。
如图:
在这里插入图片描述
红色区域为scrollview的可视剪裁区,上部黑框和下部黑框是用来做缓冲的,假设可视区域内部一共只需要3个item即可填充满,那么我们需要的总共item数量是13个,剩下的10个是预留给上部黑框和下部黑框做缓冲的item, 也就是说当我们滑动这个scrollview的时候实际上是在移动scrollview内部的list这个node.我们可以在update中去循环检测每一个节点的Y坐标 ,当然 我们需要先计算出这个最大Y和最小Y坐标,一旦发现某个item的Y坐标超出了最大Y或者最小Y的时候,如果是往上滑动则立即将这个item的内容重新刷新,然后将item移动到所有节点的最尾端,比如当前的item是0-13,一旦当item0的Y坐标大于最大Y的时候我们立马刷新该item的坐标到最底部,然后将item的内容刷新为第14个item的内容,反方向滑动同理解决. 这样我们即可实现在极少量的item数量下实现大量的item显示效果。既保证加载效率又保证内存开销低。下面是代码 我这个做法呢 写demo的时候并没有考虑多行多列的情况,仅仅是考虑了列表模式,用来做一些排行榜,任务列表是足够使用的。
scrollview.js
cc.Class({
extends: cc.Component,
properties: {
//scrollview的view剪裁节点
view:cc.Node,
//scrollview的具体装载item的node
list:cc.Node,
//item的prefab
itemPrefab:cc.Prefab,
//item的间隔
spacingY:0,
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
//预加载的item的数据
this.data = []
//当前可视区域内部填充满需要的item数量
this.rowItemCounts = 0
//创建的item节点的数组
this.items = []
//顶部最大Y
this.topMax = 0
//底部最小Y
this.bottomMax = 0
//上一次listnode的Y坐标
this.lastListY = 0
//itemprefab的高度
this.itemHeight = 0
},
//使用需调用init方法传递进data数组
init(data){
this.data = data
//保存高度
let height = 0
//创建item
let item = cc.instantiate(this.itemPrefab)
height = item.height
this.itemHeight = height
//计算可视区域内部填充满需要的item数量
this.rowItemCounts = Math.ceil(this.view.height / (height + this.spacingY))
//加载rowitemCounts + 10个item
for(let i =0 ; i < this.rowItemCounts + 10 ; ++ i){
//数据已经加载完毕了 说明需要加载的数据量很小
if(typeof data[i] == 'undefined')
break
//data[i]为了测试方便实际上只是一个1 2 3这样的数字 具体data和updateItem方法的实现
//你需要根据你自己的情况来实现
item.getComponent('ItemNode').updateItem(data[i])
//记录一下itemid
item.__itemID = i
//保存item到数组
this.items.push(item)
//加入item节点到scrollview的list里面
this.list.addChild(item)
//设置x坐标
item.x = 20
//设置y坐标 (根据自己设置的不同的锚点这些东西来调整能跑就完事了)
item.y = - (height / 2 + i * (height + this.spacingY ))
//继续创建
if(i < this.rowItemCounts + 9){
item = cc.instantiate(this.itemPrefab)
}
}
//设置list的高度 不设置无法滑动
this.list.height = 20 + (data.length) * height + (data.length) * this.spacingY
//计算顶部最大Y
this.topMax = (5 * height + 4 * this.spacingY)
//计算底部最小Y
this.bottomMax = -(this.view.height + this.topMax)
//保存list的当前Y坐标
this.lastListY = this.list.y
},
update(){
//判断是否往下滑动
let isDown = this.list.y > this.lastListY
//当前的item数量
let countOfItems = this.items.length
//预显示数据的总数量
let dataLen = this.data.length
//遍历所有item节点
for (let i in this.items){
let item = this.items[i]
//item坐标转换到对应view节点的坐标 y坐标需要减去一半item的高度...具体看你item的锚点设置
let itemPos = this.list.convertToWorldSpaceAR(item.position)
itemPos.y -= this.view.height / 2
itemPos = this.view.convertToNodeSpaceAR(itemPos)
//如果是往下滑动
if(isDown){
//判断当前item的坐标是否大于顶部最大Y
if(itemPos.y > this.topMax){
//计算新的itmeid
//比如一共13个item item的索引就是0-12 那么第0个item超过y坐标之后 就需要显示第13个item
//那么就是将当前id + 当前item的数量即可
let newId = item.__itemID + countOfItems
//如果item已经显示完毕了就不需要刷新了
if(newId >= dataLen) return
//保存itemid
item.__itemID = newId
//计算item的新的Y坐标 也就是当前y减去所有item加起来的高度
item.y = item.y - countOfItems * this.itemHeight - (countOfItems ) * this.spacingY
//刷新item内容
item.getComponent('ItemNode').updateItem(this.data[item.__itemID])
}
//如果是往上滑动
}else {
//如果超过底部最小Y 和上面的一样处理一下就完事了
if(itemPos.y < this.bottomMax){
let newId = item.__itemID - countOfItems
if (newId < 0) return
item.__itemID = newId
item.y = item.y + countOfItems * this.itemHeight + (countOfItems) * this.spacingY
item.getComponent('ItemNode').updateItem(this.data[item.__itemID])
}
}
}
//存储下当前listnode的Y坐标
this.lastListY = this.list.y
}
});
加载代码如下
let data = []
for (let i =0;i<10000;++i){
data.push(i)
}
this.scroll.getComponent(‘ScrollView’).init(data)
传递进入10000条数据 实际上也就加载可视区域的数量+10而已 动态不停的刷新就完事了…刷新频率可以降低点 这样消耗会更低.这个破问题经常会面试被问到哎…动手解决一番麻麻再也不担心我被面试官虐了。