火焰傀偶的挣扎旅程

纯爷们的一生就是不停地燃烧,keep on burning soul!!
随笔 - 6, 文章 - 5, 评论 - 4, 引用 - 0
数据加载中……

cocos2d-x 3.10 lua版本中,listview的优化(基于cocostudio3.10)

众所周知,cocos的ListView控件并不好用,他最明显的缺点有两个:
1.加载大量item时会有明显卡顿
2.不支持2维列表

·关于listview中item的加载


listview中的用法,大约看起来是这样的:
1.首先有一个item,并且调用setItemModel
2.调用pushBackDefaultItem插入列表,然后从列表取出item并初始化
(通过阅读代码可知,这个其实就是1中的item调用clone函数,生成插入item,然后addChild,是一样的)

相关代码:

 1 void ListView::addChild(cocos2d::Node *child, int zOrder, int tag)
 2 {
 3     ScrollView::addChild(child, zOrder, tag);
 4 
 5     Widget* widget = dynamic_cast<Widget*>(child);
 6     //这里可以看出,不是Widget 的话,并不会有布局的功能……
 7     if (nullptr != widget)
 8     {
 9         _items.pushBack(widget);
10         onItemListChanged();
11     }
12 }

 1 void ListView::pushBackDefaultItem()
 2 {
 3     if (nullptr == _model)
 4     {
 5         return;
 6     }
 7     Widget* newItem = _model->clone();
 8     remedyLayoutParameter(newItem);//布局排位
 9     addChild(newItem);
10     requestDoLayout();//设置重新渲染的标记
11 }

可以看出,listview的item只支持继承自widget的控件,或者说他的本意如此。

基类不是widget的类型,也没有clone方法,那么setItemModel和pushBackDefaultItem这套体系就形同虚设

然而……cocostudio的新建文件,根本就没有一个父节点是继承自widget的

开发过程中,一般一个item就是一个单独的csd,如果想要使用clone,则要每个item的父节点都必须使用一个的panel类型(即“基础容器”,实际上只是一个ccui.layout类型……)来替代node节点,这个panel的大小影响布局,而如果item中有嵌套csd的场合,情况则会变得更加麻烦,因为node是无法复制的。

综上所述,基本可以得出结论,为了节省工作量,clone这套机制,不如不用。直接create一个csd,再addChild的方案,更加可靠,当然,为了避免上述的布局问题,即使create了csd,还是要先加在一个panel上……

·关于clone方法的效率

那么直接使用create的效率比之clone又如何呢?
就直接用代码看看呗。

cocostudio是可以直接把csb文件导出成lua的,而lua本身又可以再一次压缩luac,这看起来效率还可以

试着测试这样一个简单结构的item,创建100个这东东:



local a = require("testItem").create().root:getChildByName("panel_base");
local osClock1 = os.clock();
for i = 1, 100 do
    a:clone();
end
print("time:" .. os.clock() - osClock1);

local osClock2 = os.clock();
for i = 1, 100 do
    local a = require("testItem").create().root;
end
print("time:" .. os.clock() - osClock2);

输出结果为:

好吧,虽然create慢了点,但还是可以接受的

如果item里面的东西复杂,比如我把btn_test(一个默认的按钮控件),在cocostudio中复制50个,则:


后来又测试了几个以往项目用的比较复杂的item,基本都是直接create更快了,可见这cocos的clone也是很挫的方法。

去瞧瞧c++里的代码,发现他各种属性都要遍历一遍赋值,即是说他没有方法找出重复的值,所以所有的值都要设置一次……上面的使用,想必是因为有很多重复赋值的缘故,才导致慢吧 - -

//clone方法的核心代码,一坨属性的赋值
void Widget::copyProperties(Widget *widget)
{
    setEnabled(widget->isEnabled());
    setVisible(widget->isVisible());
    setBright(widget->isBright());
    setTouchEnabled(widget->isTouchEnabled());
    setLocalZOrder(widget->getLocalZOrder());
    setTag(widget->getTag());
    setName(widget->getName());
    setActionTag(widget->getActionTag());
    _ignoreSize = widget->_ignoreSize;
    this->setContentSize(widget->_contentSize);
    _customSize = widget->_customSize;
    _sizeType = widget->getSizeType();
    _sizePercent = widget->_sizePercent;
    _positionType = widget->_positionType;
    _positionPercent = widget->_positionPercent;
    setPosition(widget->getPosition());
    setAnchorPoint(widget->getAnchorPoint());
    setScaleX(widget->getScaleX());
    setScaleY(widget->getScaleY());
    setRotation(widget->getRotation());
    setRotationSkewX(widget->getRotationSkewX());
    setRotationSkewY(widget->getRotationSkewY());
    setFlippedX(widget->isFlippedX());
    setFlippedY(widget->isFlippedY());
    setColor(widget->getColor());
    setOpacity(widget->getOpacity());
    _touchEventCallback = widget->_touchEventCallback;
    _touchEventListener = widget->_touchEventListener;
    _touchEventSelector = widget->_touchEventSelector;
    _clickEventListener = widget->_clickEventListener;
    _focused = widget->_focused;
    _focusEnabled = widget->_focusEnabled;
    _propagateTouchEvents = widget->_propagateTouchEvents;

    copySpecialProperties(widget);

    Map<int, LayoutParameter*>& layoutParameterDic = widget->_layoutParameterDictionary;
    for (auto iter = layoutParameterDic.begin(); iter != layoutParameterDic.end(); ++iter)
    {
        setLayoutParameter(iter->second->clone());
    }
}

·二维布局以及加载优化

这个单从原理上来说,很简单,大致说说我的思路
假设listview为竖着滑动的,则通过item的宽度,以及listview的宽度,计算出每一行可以摆的下多少个item,并且得到这个行的高度,以此宽高创建一个panel,并把item都算好坐标addChild到这个panel之中,
而这个panel则会被addChild到listview中,这样就实现了二维。

然而,加载的问题还是没有解决,listview就是这样的,你的列表有多少个,就给你加载多少个,一次搞定,cocos又没有做到很好的资源剪裁,于是item一多,就卡到爆炸。

能想到的解决方案:
1.分帧加载
2.随滚动加载
3.创建固定数量的item(即用户可见的最大数量item),监听滑动事件,根据滑动位置来改变各个item位置,并且根据需要重新初始化item中的内容

虽然已经没办法给出具体代码,但是方法1和方法2,甚至方法1和方法2的结合我都有实际在以前项目中使用过。

说实话体验比较一般,特别是item复杂的情况,还是会有很明显的卡顿

……总之多番试验之后,发现方法3才是最好的。

那么怎么用listview去实现呢?listview每添加一个widget类就会自动布局,自己设置滑动范围,对于3的想法来说,实在太不便利,而且我只想在lua中完成这个事情,于是pass。

再看看类似的控件怎么做吧,比如cocos2d-iphone时代就有的控件的tableView……

这个控件虽然使用起来比较麻烦,但是我认为其性能和扩展性都还可以……该说不愧是元祖鬼佬的cocos2d控件吗,反正比国人设计的这个根本不能商用的玩意要好

listview的item在tableview中的名字叫cell,tableview是动态创建item的,他只会创建冒头显示的cell,而且创建的只是一个空的cell,里面的实际内容可以自己定义。

tableview也是默认不支持二维数组的,如要需要用上述类似方法自己封装一下,至于动态加载方法,使用一个缓存item的对象池,配合滑动监听和cell初始化的回调方法来实现应该就可以了。

……好吧,说了这么多,我最终还是没有选择tableview,而是选择了更底层的scrollview,理由比较单纯,因为cocostudio里面的控件有滑动容器(scrollview),没有tableview。这样可以节省一定的ui工作量……

·实现细节

既然决定了,就创建一个叫CCListViewDynamic的class

首先来总结一下,一般初始化一个滑动列表需要的东东:
一个lua table,里面存储了一组数据结构(比如排行版的玩家列表)
一个scrollView控件,一个item的资源文件,用于重复create
一个初始化item的函数指针(比如 通过读取玩家数据,来设置 排名啊 玩家头像 玩家名字之类 )

这些都作为必要的初始化参数传入到CCListViewDynamic中

大致流程:(以下是假设滑动方向为垂直,如果是水平也是类似思路)

1.通过scrollView的宽高获取应初始化item的个数(可见的行数+1 * 每行的个数)
2.创建这些item,并且都addChild到list中,设置坐标为 -width,-height,让其不可见
3.遍历一遍lua table,算出每行由哪些item构成,并且算出每个item的坐标,每一行的显示范围并记录(即scrollview:getInnerContainer():getPositionY()在哪个区间时,这行的item依然显示),这个计算只是预计算数据,是没有做渲染相关工作的
4.根据上面的遍历,也算出了scrollview的滑动范围。
5.根据当前显示范围,通过3中的数据,计算要显示哪些行,如果没有变化,则不继续往下走,否则,和上次显示的范围做比较,计算出今次不显示和新显示的item行数,不显示的item要走一遍2中设置坐标为 -width,-height,让其不可见的流程,新显示的item则要跑一次初始化item的函数指针,并且传入对应的lua table数据
6.监听滑动事件,跑一次5的流程(需要注意的是,这个事件在c++中的枚举值是ScrollView::EventType.CONTAINER_MOVED,不知道什么时候添加的,cocos2d-x3.5的版本是没有这个事件的,如果没有就要自己整一个了……)


上面这里,我试着创建了1000个item,顶点数量和fps还算正常 总体都可以接受……就是初始化速度有点慢,要0.8s,这个还有一些细节有待优化吧。

目前只是粗略的做法,还有一些缺点:
1.目前还没实现插入自定义item,但是如果要做的话,其实思路也是类似的
2.数据和页面耦合太深,如果想做分离式的排序会变得不容易……(比如一个listview里面有两套排序规则的数据)……这个,如果真要有这种变态需求的话,还是有不少地方要改的,但是大体思路依然不变

最后附上代码:/Files/WhiteDummy/mycode/CCListViewDynamic.txt

posted on 2016-08-17 17:14 WhiteDummy 阅读(2496) 评论(1)  编辑 收藏 引用

评论

# re: cocos2d-x 3.10 lua版本中,listview的优化(基于cocostudio3.10)[未登录]  回复  更多评论   

666666
2016-08-17 17:19 | andy

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理