cocos2dx3.12 CCTableView优化和使用

前言:公司项目原先所有UI都用的是UIListView,优化过很多遍了,仍然有很多地方不尽人意。想改掉这一块,本想重写一个tableView,新项目开发很急,因此先拿cocos自带的CCTableView来使用,改掉其原生的一些使用bug即可正常商用了。

cocos2dx自带的scrollView有两种。
1,cocos2dx/extensions/GUI/CCScrollView(自带) 和cocos2dx/cocos/ui/UIScrollView。(ios ui控件)
2,GUI部分里面就带有一个CCTableView就是即将使用的这个,继承CCScrollView。
3,CCTableView实现了界面item个数+1的显示,所以使用起来效率还是可以的。

但是在使用过程中发现缺少了几个需求,因此对c++方面做了以下3点更改:
1,缺少refreshData函数

// 添加刷新函数,刷新相同个数item的view,不用每次都重载

// CCTableView.h文件,添加以下函数:
void refreshData();

// CCTableView.cpp文件, 添加以下函数:
void TableView::refreshData()
{
    if (_dataSource->numberOfCellsInTableView(this) == 0)
        return;

    for (const auto &cell: _cellsUsed)
    {
        _dataSource->tableCellAtIndex(this, cell->getIdx());
    }
}

2,缺少跳转到第几个item的函数,同样修改CCTableView.cpp和CCTableView.h

// CCTableView.h文件,添加以下函数:
// 从上往下数,跳到第几个
bool setItemTop(int nIndex, bool animated=false);
// 跳到第几个为中心
bool setItemMiddle(int nIndex, bool animated=false);


// CCTableView.cpp文件, 添加以下函数:
bool TableView::setItemTop(int nIndex,  bool animated)
{
    if (_dataSource == NULL)
    {
        return false;
    }

    if (nIndex <= 0)
    {
        nIndex = 1;
    }
    else if ( nIndex > _vCellsPositions.size())
    {
        nIndex = _vCellsPositions.size();
    }

    this->unschedule(CC_SCHEDULE_SELECTOR(TableView::deaccelerateScrolling));

    if (_direction == Direction::VERTICAL)
    {
        return this->setItemTopV(nIndex, animated);
    }
    else
    {
        return this->setItemTopH(nIndex, animated);
    }
}

bool TableView::setItemTopV(int nIndex,  bool animated)
{
    if (_viewSize.height >=  this->getContentSize().height)
    {
        this->setContentOffset(Vec2(this->getContentOffset().x, this->minContainerOffset().y),  animated);
        return true;
    }

    Vec2 offset = __offsetFromIndex(nIndex - 1);
    if (_viewSize.height >= this->getContentSize().height - offset.y)
    {
        this->setContentOffset(Vec2(this->getContentOffset().x, this->maxContainerOffset().y),  animated);
        return true;
    }

    float fOffsetX = this->getContentOffset().x;
    float fOffsetY      = -(this->getContentSize().height - offset.y) + _viewSize.height;

    this->setContentOffset(Vec2(fOffsetX, fOffsetY),  animated);

    return true;
}

bool TableView::setItemTopH(int nIndex,  bool animated)
{
    if (_viewSize.width >=  this->getContentSize().width)
    {
        this->setContentOffset(Vec2(this->maxContainerOffset().x, this->getContentOffset().y ),  animated);
        return true;
    }

    Vec2 offset = __offsetFromIndex(nIndex - 1);
    if (_viewSize.width >= this->getContentSize().width - offset.x)
    {
        this->setContentOffset(Vec2(this->minContainerOffset().x, this->getContentOffset().y),  animated);
        return true;
    }

    float fOffsetX = -offset.x;
    float fOffsetY = this->getContentOffset().y;

    this->setContentOffset(Vec2(fOffsetX, fOffsetY),  animated);
    return true;
}

bool TableView::setItemMiddle(int nIndex, bool animated)
{
    if (_dataSource == NULL)
    {
        return false;
    }

    if (nIndex <= 0)
    {
        nIndex = 1;
    }
    else if ( nIndex > _vCellsPositions.size())
    {
        nIndex = _vCellsPositions.size();
    }

    this->unschedule(CC_SCHEDULE_SELECTOR(TableView::deaccelerateScrolling));

    if (_direction == Direction::VERTICAL)
    {
        return this->setItemMiddleV(nIndex, animated);
    }
    else
    {
        return this->setItemMiddleH(nIndex, animated);
    }
}

bool TableView::setItemMiddleV(int nIndex, bool animated)
{
    Vec2 offset = __offsetFromIndex(nIndex - 1);
    Size sizeCell = _dataSource->tableCellSizeForIndex(this, nIndex - 1);

    const Size& sizeContent = this->getContentSize();
    const Size& sizeView = _viewSize;

    if (sizeView.height >= sizeContent.height || ((offset.y + sizeCell.height / 2) <= sizeView.height / 2) )
    {
        this->setContentOffset(Vec2(this->getContentOffset().x, this->minContainerOffset().y),  animated);
        return true;
    }

    if ((sizeContent.height - (offset.y + sizeCell.height/2)) <= sizeView.height/2)
    {
        this->setContentOffset(Vec2(this->getContentOffset().x, this->maxContainerOffset().y),  animated);
        return true;
    }

    float fOffsetX = this->getContentOffset().x;
    float fOffsetY = sizeView.height - sizeContent.height + offset.y - (sizeView.height - sizeCell.height) / 2;

    this->setContentOffset(Vec2(fOffsetX, fOffsetY),  animated);

    return true;
}

bool TableView::setItemMiddleH(int nIndex, bool animated)
{
    Vec2 offset = __offsetFromIndex(nIndex - 1);
    const Size sizeCell = _dataSource->tableCellSizeForIndex(this, nIndex - 1);

    const Size& sizeContent = this->getContentSize();
    const Size& sizeView = _viewSize;

    if (sizeView.width >= sizeContent.width || ((offset.x + sizeCell.width/2) <= sizeView.width/2) )
    {
        this->setContentOffset(Vec2(this->maxContainerOffset().x, this->getContentOffset().y),  animated);
        return true;
    }

    if ((sizeContent.width - (offset.x + sizeCell.width/2)) <= sizeView.width/2)
    {
        this->setContentOffset(Vec2(this->minContainerOffset().x, this->getContentOffset().y),  animated);
        return true;
    }

    float fOffsetX = -offset.x + (sizeView.width - sizeCell.width) / 2;
    float fOffsetY = this->getContentOffset().y;

    this->setContentOffset(Vec2(fOffsetX, fOffsetY),  animated);

    return true;
}

3,将修改的函数导出接口到lua使用

//修改lua_cocos2dx_extension_auto.cpp,添加以下函数
//add --
int lua_cocos2dx_extension_TableView_refreshData(lua_State* tolua_S)
{
    int argc = 0;
    cocos2d::extension::TableView* cobj = nullptr;
    bool ok  = true;

#if COCOS2D_DEBUG >= 1
    tolua_Error tolua_err;
#endif


#if COCOS2D_DEBUG >= 1
    if (!tolua_isusertype(tolua_S,1,"cc.TableView",0,&tolua_err)) goto tolua_lerror;
#endif

    cobj = (cocos2d::extension::TableView*)tolua_tousertype(tolua_S,1,0);

#if COCOS2D_DEBUG >= 1
    if (!cobj) 
    {
        tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_TableView_refreshData'", nullptr);
        return 0;
    }
#endif

    argc = lua_gettop(tolua_S)-1;
    if (argc == 0) 
    {
        if(!ok)
        {
            tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_TableView_refreshData'", nullptr);
            return 0;
        }
        cobj->refreshData();
        lua_settop(tolua_S, 1);
        return 1;
    }
    luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.TableView:refreshData",argc, 0);
    return 0;

#if COCOS2D_DEBUG >= 1
    tolua_lerror:
    tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_TableView_refreshData'.",&tolua_err);
#endif

    return 0;
}
int lua_cocos2dx_extension_TableView_setItemTop(lua_State* tolua_S)
{
    int argc = 0;
    cocos2d::extension::TableView* cobj = nullptr;
    bool ok  = true;
#if COCOS2D_DEBUG >= 1
    tolua_Error tolua_err;
#endif

#if COCOS2D_DEBUG >= 1
    if (!tolua_isusertype(tolua_S,1,"cc.TableView",0,&tolua_err)) goto tolua_lerror;
#endif
    cobj = (cocos2d::extension::TableView*)tolua_tousertype(tolua_S,1,0);
#if COCOS2D_DEBUG >= 1
    if (!cobj)
    {
        tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_TableView_setItemTop'", nullptr);
        return 0;
    }
#endif
    argc = lua_gettop(tolua_S)-1;
    do{
        if (argc == 2) {
            int arg0;
            ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "cc.TableView:setItemTop");

            if (!ok) { break; }
            bool arg1;
            ok &= luaval_to_boolean(tolua_S, 3,&arg1, "cc.TableView:setItemTop");

            if (!ok) { break; }
            bool ret = cobj->setItemTop(arg0, arg1);
            tolua_pushboolean(tolua_S,(bool)ret);
            return 1;
        }
    }while(0);
    ok  = true;
    do{
        if (argc == 1) {
            int arg0;
            ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "cc.TableView:setItemTop");

            if (!ok) { break; }
            bool ret = cobj->setItemTop(arg0);
            tolua_pushboolean(tolua_S,(bool)ret);
            return 1;
        }
    }while(0);
    ok  = true;
    luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n",  "cc.TableView:setItemTop",argc, 1);
    return 0;

#if COCOS2D_DEBUG >= 1
    tolua_lerror:
    tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_TableView_setItemTop'.",&tolua_err);
#endif

    return 0;
}
int lua_cocos2dx_extension_TableView_setItemMiddle(lua_State* tolua_S)
{
    int argc = 0;
    cocos2d::extension::TableView* cobj = nullptr;
    bool ok  = true;
#if COCOS2D_DEBUG >= 1
    tolua_Error tolua_err;
#endif

#if COCOS2D_DEBUG >= 1
    if (!tolua_isusertype(tolua_S,1,"cc.TableView",0,&tolua_err)) goto tolua_lerror;
#endif
    cobj = (cocos2d::extension::TableView*)tolua_tousertype(tolua_S,1,0);
#if COCOS2D_DEBUG >= 1
    if (!cobj)
    {
        tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_TableView_setItemMiddle'", nullptr);
        return 0;
    }
#endif
    argc = lua_gettop(tolua_S)-1;
    do{
        if (argc == 2) {
            int arg0;
            ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "cc.TableView:setItemMiddle");

            if (!ok) { break; }
            bool arg1;
            ok &= luaval_to_boolean(tolua_S, 3,&arg1, "cc.TableView:setItemMiddle");

            if (!ok) { break; }
            bool ret = cobj->setItemMiddle(arg0, arg1);
            tolua_pushboolean(tolua_S,(bool)ret);
            return 1;
        }
    }while(0);
    ok  = true;
    do{
        if (argc == 1) {
            int arg0;
            ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "cc.TableView:setItemMiddle");

            if (!ok) { break; }
            bool ret = cobj->setItemMiddle(arg0);
            tolua_pushboolean(tolua_S,(bool)ret);
            return 1;
        }
    }while(0);
    ok  = true;
    luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n",  "cc.TableView:setItemMiddle",argc, 1);
    return 0;

#if COCOS2D_DEBUG >= 1
    tolua_lerror:
    tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_TableView_setItemMiddle'.",&tolua_err);
#endif

    return 0;
}
//

//然后修改lua_register_cocos2dx_extension_TableView函数将刚才的方法导出到lua
int lua_register_cocos2dx_extension_TableView(lua_State* tolua_S)
{
    tolua_usertype(tolua_S,"cc.TableView");
    tolua_cclass(tolua_S,"TableView","cc.TableView","cc.ScrollView",nullptr);

    tolua_beginmodule(tolua_S,"TableView");
        tolua_function(tolua_S,"new",lua_cocos2dx_extension_TableView_constructor);
        tolua_function(tolua_S,"updateCellAtIndex",lua_cocos2dx_extension_TableView_updateCellAtIndex);
        tolua_function(tolua_S,"setVerticalFillOrder",lua_cocos2dx_extension_TableView_setVerticalFillOrder);
        tolua_function(tolua_S,"scrollViewDidZoom",lua_cocos2dx_extension_TableView_scrollViewDidZoom);
        tolua_function(tolua_S,"_updateContentSize",lua_cocos2dx_extension_TableView__updateContentSize);
        tolua_function(tolua_S,"getVerticalFillOrder",lua_cocos2dx_extension_TableView_getVerticalFillOrder);
        tolua_function(tolua_S,"removeCellAtIndex",lua_cocos2dx_extension_TableView_removeCellAtIndex);
        tolua_function(tolua_S,"initWithViewSize",lua_cocos2dx_extension_TableView_initWithViewSize);
        tolua_function(tolua_S,"scrollViewDidScroll",lua_cocos2dx_extension_TableView_scrollViewDidScroll);
        tolua_function(tolua_S,"reloadData",lua_cocos2dx_extension_TableView_reloadData);

        //add-- 此处是我添加的3个函数
        tolua_function(tolua_S,"refreshData",lua_cocos2dx_extension_TableView_refreshData);
        tolua_function(tolua_S,"setItemTop",lua_cocos2dx_extension_TableView_setItemTop);
        tolua_function(tolua_S,"setItemMiddle",lua_cocos2dx_extension_TableView_setItemMiddle);
        //end

        tolua_function(tolua_S,"insertCellAtIndex",lua_cocos2dx_extension_TableView_insertCellAtIndex);
        tolua_function(tolua_S,"cellAtIndex",lua_cocos2dx_extension_TableView_cellAtIndex);
        tolua_function(tolua_S,"dequeueCell",lua_cocos2dx_extension_TableView_dequeueCell);
    tolua_endmodule(tolua_S);
    std::string typeName = typeid(cocos2d::extension::TableView).name();
    g_luaType[typeName] = "cc.TableView";
    g_typeCast["TableView"] = "cc.TableView";
    return 1;
}

在lua使用过程发现了CCTableView有两个问题,因此在lua使用时候做了以下修改
1,tableView滑动出可视区域,仍然可以点击被隐藏的cell上面的button。即穿透问题。
解决思路就是:在按钮上创建一个layer监听点击事件,判断点击区域的坐标是否属于tableView即可。

2, tableView点击cell上的button进行滑动,cell不可滑动。即button为wiget的时候touch被吞噬。
解决思路就是:监听点击事件同时,在开始点击储存begionPos,在点击结束判断位移的offset是否满足可以点击,满足就点击,不满足就滑动tableView

3,解决方法的lua代码如下:

--因为我们项目所有的按钮都是用UIbutton来创建的。所以只需要改UIButton即可
local ButtonEx = class("ButtonEx")
ButtonEx.__index = ButtonEx
function ButtonEx.extend(target)
    local t = tolua.getpeer(target)
    if not t then
        t = {}
        tolua.setpeer(target, t)
    end
    setmetatable(t, ButtonEx)
    return target
end
function ButtonEx:setTableView(tableView)
    self.tableView = tableView
    if not self.tableView or self.touchLayer then
        return
    end
    self:initTouchLayer()
end
function ButtonEx:initTouchLayer()
    self:setSwallowTouches(false)
    self.touchLayer = cc.Layer:create()
    self.touchLayer:setContentSize(self:getContentSize())
    self:addChild(self.touchLayer)
    self.touchLayer:unregisterScriptTouchHandler()
    local onTouch = function (eventType, x, y, id)
        if eventType == "began"  then
            self.touchBeginPos = self:getWorldPos(self)

            if self.isNotInView then
                return true
            end

            if self.tableView then
                local viewPos = self:getWorldPos(self.tableView)
                local size = self.tableView:getViewSize()
                local viewSize = cc.rect(viewPos.x, viewPos.y, size.width, size.height)
                if not cc.rectContainsPoint(viewSize, cc.p(x, y)) then
                    self.isNotInView = true
                end
            end
            return true
        end
    end
    self.touchLayer:registerScriptTouchHandler(onTouch, false, 0, false)
    self.touchLayer:setTouchEnabled(true)
end
function ButtonEx:isCanNotClick()
    if not self.tableView then
        return false
    end

    -- 防止界面移出clipNode仍然可点击
    if self.isNotInView then
        self.isNotInView = false
        return true
    end

    -- 防止移动后仍触发点击事件
    local curPos = self:getWorldPos(self)
    local offset = 15
    local moveX = math.abs(curPos.x - self.touchBeginPos.x) > offset
    local moveY = math.abs(curPos.y - self.touchBeginPos.y) > offset
    if moveX or moveY then
        return true
    end
end
function ButtonEx:getWorldPos(node)
    local parent = node:getParent()
    return parent:convertToWorldSpace(cc.p(node:getPosition()))
end
function ButtonEx:setTouchEvent(touchEvent)
    local function _btnTouchEvent(sender, eventType)
        if eventType == ccui.TouchEventType.ended then     
            --tableView
            if self:isCanNotClick() then
                return
            end
        end
        if touchEvent then
            touchEvent(sender, eventType);
        end 
    end 
    self:addTouchEventListener(_btnTouchEvent)
end



--然后在使用的时候,只需要在cell上创建此按钮,
--然后在将此按钮上的tableView传入即可防止以上两个问题
function createButtonNormal(key, selKey, touchEvent, loadType )
    local btn = ButtonEx.extend(ccui.Button:create())
    btn:setTouchEnabled(true)
    btn:setTouchEvent(touchEvent)
    return btn
end
--使用方法
function use()
    //创建cell上的按钮
    local btn = createButtonNormal()
    //传入正在使用的tableView
    btn:setTableView(useTableView)
end

好了,做了如上修改,tableView就可以正常使用了,使用方法如下:

在panel里面可以调用如下方法,创建tableView

--@ tableView:refreshData()             刷新仅能刷新相同个数cell的view
--@ tableView:reloadData()              从组数据和view
--@ tableView:setDirection(Dir)         设置view显示横竖方向
--@ tableView:setVerticalFillOrder(Ver) 设置view数据正序或倒序
--@ tableView:initWithViewSize(size)    设置view可视区域大小
--@ tableView:setItemTop(num)           跳转到从顶部数下来第几个cell
--@ tableView:setItemMiddle(num)        同setItemTop,但以num为view中心位置
function view:createTableView(handleType, callBack)
    local tableView = cc.TableView:create(cc.size(708,500))  
    tableView:registerScriptHandler(function (tabView) 
        return self:numberOfCell()(tabView) 
    end, cc.NUMBER_OF_CELLS_IN_TABLEVIEW)  

    tableView:registerScriptHandler(function (tabView, idx) 
        return self:sizeForIndex(tabView, idx) 
    end, cc.TABLECELL_SIZE_FOR_INDEX)  

    tableView:registerScriptHandler(function (tabView, idx) 
        return self:cellAtIndex(tabView, idx) 
    end, cc.TABLECELL_SIZE_AT_INDEX)

    return tableView
end

function view:numberOfCell(tableView)
    return 100
end
function view:sizeForIndex(tableView, idx)
    return 705,150
end
function view:cellAtIndex(tableView, idx)  
    local cell = tableView:dequeueCell()     
    if not cell then
        cell = cc.TableViewCell:create()
        cell:refresh()
    end
    return cell
end

还有一处修改就是自带的滑动惯性有点慢,修改了CCScrollView.cpp里面的#define BOUNCE_BACK_FACTOR 0.05f ,减少这个值可以了

总结:使用tableView可以提高使用效率,有效减卡顿。稍作修改即可,挺方便的- -.
同一帧内不要做太多操作,分帧操作会很大程度提高流畅性

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值