cocos2dx3.12 CCTableView优化和使用

    xiaoxiao2021-03-25  54

    前言:公司项目原先所有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可以提高使用效率,有效减卡顿。稍作修改即可,挺方便的- -. 同一帧内不要做太多操作,分帧操作会很大程度提高流畅性

    转载请注明原文地址: https://ju.6miu.com/read-39391.html

    最新回复(0)