我们现在有一个 角色属性类叫heroModel,内容如下,当heroModel中的等级发生变化的时候,我们需要刷新界面显示等级信息,通常我们是在收到等级升级成功的协议的时候,发送一个事件,UI界面接受到这个事件的时候,刷新一下等级信息,如果不采用全局事件的方式,该怎么处理呢
我们能不能监听HeroModel的level这个属性,给这个属性绑定一个监听方法,当这个属性发生变化的时候,自动执行这个监听方法执行一些刷新操作。
local heroModel = {
id = 1001,
star = 1,
level = 1,
life = 1000,
atk = 100,
def = 10,
}
我们知道,lua有两个比较常见的原方法 __index 和 __newindex,__index是 元表提供的表对象内部数据获取元方法,__newindex是当对表中不存在的元素进行赋值时会被调用。当我们从heroModel中获取某一个属性值的时候,会先从heroModel中取值,如果heroModel中不存在该值,则会从原表中取值,同样当我们给heroModel赋值的时候,会先判断heroModel是否存在该属性,如果存在,直接赋值,不存在则从原方法 __newindex中进行赋值。加入heroModel中不存在level这个属性,那么我们给它赋值的时候,肯定会调用原方法__newindex ,这个时候我们就可以在这个原方法里面赋值的时候进行判断,属性level是否发生变化,如果发生了变化, 则执行一个回调方法
简单演示如下
local heroModel = {
id = 1001, star = 1, level=1
}
---创建原表
local metaTable = {}
metaTable.newInfo = {} --保存新创建的属性值
metaTable.bindDataList = {} --存储回调方法
metaTable.__index = function(t , k)
printError("get " .. k)
local res = metaTable.newInfo[k]
return res
end
metaTable.__newindex = function(t, k, v)
printError("set " .. k .. " = " .. tostring(v))
if (not metaTable.newInfo[k] or metaTable.newInfo[k] ~= v) and metaTable.bindDataList[k] ~= nil then
local callback = metaTable.bindDataList[k]
callback()
end
metaTable.newInfo[k] = v
return v
end
-- 将heroModel中的属性赋值到原表,并清空heroModel,这样操作之后,只要给heroModel进行赋值就会执行它的原方法 __newindex
for k, v in pairs(heroModel) do
metaTable.newInfo[k] = v
heroModel[k] = nil
end
-- 设置元表
setmetatable(heroModel, metaTable)
-- 绑定回调方法
metaTable.bindDataList["level"] = function()
printError("level 值发生了变化")
end
--测试
printError("----test---")
printError("level 初始值 = "..tostring(heroModel.level)) --没有更新赋值时候进行调用
heroModel.level = 10 --通过__newindex 刷新属性值,并执行回调
heroModel.level = 11
local b = heroModel.level --通关__index 获取新属性值
printError("level新值 = " .. b)
heroModel.kkk = 111 --通过__newindex 创建新属性值
printError(heroModel.kkk) --通关__index 获取新属性值
输出结果
完整代码
DataBindModel = {}
---@class DataBindModel:nil
local DataBindModel = DataBindModel
---给实例添加绑定 内部方法 不要手动调用
---@param target dataBindInfo
function DataBindModel:__bindIns(target)
if (target.dataBindvars) then
return
end
local dataBindvars = {}
local mateTable = getmetatable(target)
setmetatable(dataBindvars, mateTable)
for k, v in pairs(target) do
dataBindvars[k] = v
target[k] = nil
end
target.dataBindvars = dataBindvars
target.bindInfo__ = {}
target.isBind = false
target.oldMateTable = mateTable
local settable = { __index = function(t, key)
return dataBindvars[key]
end }
settable.__newindex = DataBindModel.__newindex
setmetatable(target, settable)
end
function DataBindModel:unBindIns(target)
if (not target.dataBindvars) then
return
end
local dataBindvars = target.dataBindvars
local mateTable = target.oldMateTable
target.oldMateTable = nil
target.bindInfo__ = nil
target.isBind = nil
target.dataBindvars = nil
setmetatable(target, mateTable)
for k, v in pairs(dataBindvars) do
target[k] = v
end
end
function DataBindModel:newIndex(key)
return self.dataBindvars[key]
end
function DataBindModel:__newindex(key, var)
local vars = self.dataBindvars
local oldValue = vars[key]
vars[key] = var
if (self.isBind) then
if (oldValue ~= var) then
local bindInfo__ = self.bindInfo__
local bindData = bindInfo__[key]
if (bindData) then
local tempBindData = {}
for i, bdata in ipairs(bindData) do
table.insert(tempBindData, bdata)
end
for i, bdata in ipairs(tempBindData) do
bdata.callBack(var, bdata, self)
end
tempBindData = nil
end
end
end
end
---@param target any @model
---@param bindInfo dataBindInfo
function DataBindModel:bindKey(target, bindInfo)
if not target then
printWarningColor("FFB500FF", "dataModel is nil")
return
end
if (not target.dataBindvars) then
self:__bindIns(target)
end
local bindInfo__ = target.bindInfo__
local bindData = bindInfo__[bindInfo.key]
if (not bindData) then
bindData = {}
bindInfo__[bindInfo.key] = bindData
end
target.isBind = true
local uiNode = bindInfo.uiNode
if (uiNode and not tolua.isnull(uiNode)) then
---@type CS.NodeEventListener
local nodeEventListener = uiNode:GetComponent(typeof(CS.NodeEventListener))
if not nodeEventListener then
nodeEventListener = uiNode.gameObject:AddComponent(typeof(CS.NodeEventListener))
bindInfo.nodeEventHandle = function()
self:unBindKey(target, bindInfo)
end
if nodeEventListener.destroyCallback then
nodeEventListener.destroyCallback = nodeEventListener.destroyCallback + bindInfo.nodeEventHandle
else
nodeEventListener.destroyCallback = bindInfo.nodeEventHandle
end
end
end
table.insert(bindData, bindInfo)
end
---@param target any
---@param bindInfo dataBindInfo
function DataBindModel:unBindKey(target, bindInfo)
if (not target.dataBindvars) then
return
end
local bindInfo__ = target.bindInfo__
local bindData = bindInfo__[bindInfo.key]
if (bindData) then
local index = table.indexof(bindData, bindInfo)
if (index) then
table.remove(bindData, index)
local uiNode = bindInfo.uiNode
if (uiNode and not tolua.isnull(uiNode)) then
local nodeEventHandle = uiNode:GetComponent(typeof(CS.NodeEventListener))
if nodeEventHandle then
nodeEventHandle.destroyCallback = nodeEventHandle.destroyCallback - bindInfo.nodeEventHandle
end
end
end
if (#bindData == 0) then
bindInfo__[bindInfo.key] = nil
end
end
local count = 0;
for k, v in pairs(bindInfo__) do
count = count + 1;
break ;
end
if (count <= 0) then
self:unBindIns(target)
end
end
---@param bindDataInfos dataBindInfo
function DataBindModel:bindKeys(target, bindDataInfos)
for i, data in ipairs(bindDataInfos) do
self:bindKey(target, data)
end
end
---@param bindDataInfos dataBindInfo
function DataBindModel:unBindKeys(target, bindDataInfos)
for i, data in ipairs(bindDataInfos) do
self:unBindKey(target, data)
end
end
return DataBindModel
调用
注:DataBindModel 是一个全局table
local heroModel = {
id = 1001, star = 1, level=1
}
local bindDatas = {
key = "level",
callBack = function(value, bindInfo, model)
-- 执行刷新逻辑
self:checkRed()
end
}
DataBindModel:bindKeys(heroModel, bindDatas)