原文请猛戳:
http://galoisplusplus.coding....
承接上一篇,这篇主要谈谈本渣在quickx用的一些脚本或自己折腾的一些定制,本文也将不时更新。
如无特殊说明,相关函数放在一个MyPackage
的lua global table中:
MyPackage = MyPackage or {}
UI组件
滚动列表相关
--[[--
Refresh UIListView at the current postion.
NOTE: only needed in async mode
]]
function MyPackage.refreshUIListView(listView)
if not listView.bAsyncLoad then
listView:reload()
return
end
if #listView.items_ <= 0 then
listView:reload()
return
end
local originPos = MyPackage.getOriginPosOfUIListView(listView)
-- index of the previous beginning item
local beginIdx = listView.items_[1].idx_
listView:removeAllItems()
listView.container:setPosition(0, 0)
listView.container:setContentSize(cc.size(0, 0))
MyPackage.drawUIListViewFromIdx(listView, beginIdx, originPos.x, originPos.y)
end
--[[--
NOTE: only needed in async mode
]]
function MyPackage.getOriginPosOfUIListView(listView)
if not listView.bAsyncLoad then
return
end
local getContainerCascadeBoundingBox = function (listView)
local boundingBox
for i, item in ipairs(listView.items_) do
local w,h = item:getItemSize()
local x,y = item:getPosition()
local anchor = item:getAnchorPoint()
x = x - anchor.x * w
y = y - anchor.y * h
if boundingBox then
boundingBox = cc.rectUnion(boundingBox, cc.rect(x, y, w, h))
else
boundingBox = cc.rect(x, y, w, h)
end
end
local point = listView.container:convertToWorldSpace(cc.p(boundingBox.x, boundingBox.y))
boundingBox.x = point.x
boundingBox.y = point.y
return boundingBox
end
local cascadeBound = getContainerCascadeBoundingBox(listView)
-- local cascadeBound = listView.scrollNode:getCascadeBoundingBox()
local localPos = listView:convertToNodeSpace(cc.p(cascadeBound.x, cascadeBound.y))
local originPosX = 0
local originPosY = 0
if cc.ui.UIScrollView.DIRECTION_VERTICAL == listView.direction then
-- ahead part of view
originPosY = localPos.y + cascadeBound.height - listView.viewRect_.y - listView.viewRect_.height
else
-- left part of view
originPosX = - listView.viewRect_.x + localPos.x
end
return cc.p(originPosX, originPosY)
end
--[[--
Draw UIListView from the `beginIdx`th item at position (`originPosX`, `originPosY`).
NOTE: only needed in async mode
]]
function MyPackage.drawUIListViewFromIdx(listView, beginIdx, originPosX, originPosY)
if not listView.bAsyncLoad then
listView:reload()
return
end
listView:removeAllItems()
listView.container:setPosition(0, 0)
listView.container:setContentSize(cc.size(0, 0))
local beginIdx = beginIdx or 1
local originPosX = originPosX or 0
local originPosY = originPosY or 0
local count = listView.delegate_[cc.ui.UIListView.DELEGATE](listView, cc.ui.UIListView.COUNT_TAG)
listView.items_ = {}
local itemW, itemH = 0, 0
local item
local containerW, containerH = 0, 0
for i = beginIdx, count do
item, itemW, itemH = listView:loadOneItem_(cc.p(originPosX, originPosY), i)
if cc.ui.UIScrollView.DIRECTION_VERTICAL == listView.direction then
originPosY = originPosY - itemH
containerH = containerH + itemH
else
originPosX = originPosX + itemW
containerW = containerW + itemW
end
if containerW > listView.viewRect_.width + listView.redundancyViewVal
or containerH > listView.viewRect_.height + listView.redundancyViewVal then
break
end
end
if cc.ui.UIScrollView.DIRECTION_VERTICAL == listView.direction then
listView.container:setPosition(listView.viewRect_.x,
listView.viewRect_.y + listView.viewRect_.height)
else
listView.container:setPosition(listView.viewRect_.x, listView.viewRect_.y)
end
listView:increaseOrReduceItem_()
end
function MyPackage.elasticMoveUIScrollView(scrollView, scrollToBottom, scrollToRight)
local cascadeBound = scrollView:getScrollNodeRect()
local disX, disY = 0, 0
local viewRect = scrollView:getViewRectInWorldSpace()
if cascadeBound.width < viewRect.width then
if scrollToRight then
disX = viewRect.x + viewRect.width - cascadeBound.x - cascadeBound.width
else
disX = viewRect.x - cascadeBound.x
end
else
if cascadeBound.x > viewRect.x then
disX = viewRect.x - cascadeBound.x
elseif cascadeBound.x + cascadeBound.width < viewRect.x + viewRect.width then
disX = viewRect.x + viewRect.width - cascadeBound.x - cascadeBound.width
end
end
if cascadeBound.height < viewRect.height then
if scrollToBottom then
disY = viewRect.y - cascadeBound.y
else
disY = viewRect.y + viewRect.height - cascadeBound.y - cascadeBound.height
end
else
if cascadeBound.y > viewRect.y then
disY = viewRect.y - cascadeBound.y
elseif cascadeBound.y + cascadeBound.height < viewRect.y + viewRect.height then
disY = viewRect.y + viewRect.height - cascadeBound.y - cascadeBound.height
end
end
if 0 == disX and 0 == disY then
return
end
local posX, posY = scrollView.scrollNode:getPosition()
scrollView.position_ = cc.p(posX + disX, posY + disY)
scrollView.scrollNode:setPosition(scrollView.position_)
end
更新:以上改动已挪到yszheda/quickx-extensions的UIScrollView或UIListView中。
lua语言相关
bool转数字
function MyPackage.bool2number(bool)
return bool and 1 or 0
end
table相关
function MyPackage.removeValueFromArray(array, value)
local idx
for i, v in ipairs(array) do
if v == value then
idx = i
break
end
end
if idx then
table.remove(array, idx)
end
end
function MyPackage.hasValueInArray(array, value)
local hasValue = false
for i, v in ipairs(array) do
if v == value then
hasValue = true
break
end
end
return hasValue
end
UTF8字符串
cocos2d-x的label默认为UTF8编码,一般场景下主要需要以下两个功能:
字符串长度
截取子串
原先本渣用cocos2d-x时写了个C++函数来求长度:
long long utf8StringSize(const std::string& str)
{
char* charArray = new char[str.length() + 1];
strcpy(charArray, str.c_str());
char* s = charArray;
/*-----------------------------------------------------------------------------
* References: http://stackoverflow.com/questions/4063146/getting-the-actual-length-of-a-utf-8-encoded-stdstring
*-----------------------------------------------------------------------------*/
long long len = 0;
while (*s) len += (*s++ & 0xc0) != 0x80;
delete [] charArray;
return len;
}
cocos2d-x Helper
也提供了接口来做字符串截取:
static std::string getSubStringOfUTF8String(const std::string& str,
std::string::size_type start,
std::string::size_type length);
在lua方面,quickx已经提供string.utf8len
来求字符串长度,本渣仿照其实现写了个截取子串的函数:
function MyPackage.utf8str(str, start, num)
local function utf8CharSize(char)
local size = 0
local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}
local size = #arr
while arr[size] do
if char >= arr[size] then
break
end
size = size - 1
end
return size
end
local startIdx = 1
while start > 1 do
local char = string.byte(str, startIdx)
startIdx = startIdx + utf8CharSize(char)
start = start - 1
end
local endIdx = startIdx
while num > 0 do
if endIdx > #str then
endIdx = #str
break
end
local char = string.byte(str, endIdx)
endIdx = endIdx + utf8CharSize(char)
num = num - 1
end
return str:sub(startIdx, endIdx - 1)
end
不过目前lua5.3已经有UTF8库,可以不用自行造轮子了。另外,关于其他UTF8相关的lua问题可以参考Lua Unicode。
其他Helper Functions
更新view的callbackWrapper
我们经常碰到如下的情景:
游戏向后端请求数据,在拿到数据之后执行某个callback去更新某个view。
这种网络请求通常是异步的,如果所请求的数据回来时相关的view被释放,则执行操作该view的callback会导致问题(例如访问非法内存地址)。
这时候我们可以用tolua.isnull
来判断相关的view对象是否被释放。
由于每个这种类型的callback都有必要加上这样的guard code,所以本渣干脆做了如下的接口:
function MyPackage.callbackWrapper(views, callback)
return function(...)
for _, view in pairs(views) do
if tolua.isnull(view) then
return
end
end
if callback ~= nil then
callback(...)
end
end
end
拿到一个node九个端点的坐标
--[[--
get the nine positions of a node (the following variables are defined in display.lua of quickx):
display.CENTER
display.LEFT_TOP
display.CENTER_TOP
display.RIGHT_TOP
display.CENTER_LEFT
display.CENTER_RIGHT
display.BOTTOM_LEFT
display.BOTTOM_RIGHT
display.BOTTOM_CENTER
]]
function MyPackage.getPositionOfNode(node, alignType)
if not node or tolua.isnull(node) then
return
end
local size = node:getContentSize()
if size.width == 0 and size.height == 0 then
size = node:getCascadeBoundingBox()
end
local pos = cc.p(node:getPosition())
local anchorPoint = cc.p(node:getAnchorPoint())
if alignType == display.LEFT_TOP or
alignType == display.LEFT_CENTER or
alignType == display.LEFT_BOTTOM then
pos.x = pos.x - size.width * anchorPoint.x
elseif alignType == display.CENTER_TOP or
alignType == display.CENTER or
alignType == display.CENTER_BOTTOM then
pos.x = pos.x - size.width * anchorPoint.x + size.width * 0.5
elseif alignType == display.RIGHT_TOP or
alignType == display.RIGHT_CENTER or
alignType == display.RIGHT_BOTTOM then
pos.x = pos.x - size.width * anchorPoint.x + size.width
end
if alignType == display.BOTTOM_LEFT or
alignType == display.BOTTOM_CENTER or
alignType == display.BOTTOM_RIGHT then
pos.y = pos.y - size.height * anchorPoint.y
elseif alignType == display.CENTER_LEFT or
alignType == display.CENTER or
alignType == display.CENTER_RIGHT then
pos.y = pos.y - size.height * anchorPoint.y + size.height * 0.5
elseif alignType == display.TOP_LEFT or
alignType == display.TOP_CENTER or
alignType == display.TOP_RIGHT then
pos.y = pos.y - size.height * anchorPoint.y + size.height
end
return pos
end
在某个container中加入sprite,可指定根据container大小进行缩放及对齐方式
function MyPackage.displaySpriteOnContainer(sprite, container, scaleToFit, alignType)
if tolua.isnull(container) then
return
end
-- default settings
local scaleToFit = (scaleToFit ~= false)
local alignType = alignType or display.CENTER
if not tolua.isnull(sprite) then
local originSize = sprite:getContentSize()
if originSize.width == 0 or originSize.height == 0 then
originSize = sprite:getCascadeBoundingBox()
end
local targetSize = container:getContentSize()
if targetSize.width == 0 or targetSize.height == 0 then
targetSize = container:getCascadeBoundingBox()
end
if scaleToFit then
sprite:setScale(targetSize.width / originSize.width, targetSize.height / originSize.height)
end
-- NOTE: ignore container's anchor point
local pos = MyPackage.getPositionOfNode(container, alignType)
local leftBottomPos = MyPackage.getPositionOfNode(container, display.LEFT_BOTTOM)
local posX = pos.x - leftBottomPos.x
local posY = pos.y - leftBottomPos.y
display.align(sprite, alignType, posX, posY)
container:addChild(sprite)
end
end