5. Unity3d Lua框架 - xLua - 所有UI组件基类

  1. UI组件基类:所有UI组件从这里继承
  2. UI容器基类:当一个UI组件持有其它UI组件时,它就是一个容器类,它要负责调度其它UI组件的相关函数
  3. UI控制层基类:发送网络请求(网络数据)、操作游戏逻辑、修改模型数据(本地数据)
  4. UI模型层基类:该界面相关数据,同时负责消息定制
  5. UI带有子窗口记忆功能的模型层基类:窗口被重新打开时会自动打开之前没有关闭的子级窗口
  6. UI视图层基类:该界面所有UI刷新操作,只和展示相关的数据放在这,只有操作相关数据放Model去

支付宝捐赠

打赏红包

1 . UIBaseComponent.lua

--[[
-- UI组件基类:所有UI组件从这里继承
-- 说明:
-- 1、采用基于组件的设计方式,容器类负责管理和调度子组件,实现类似于Unity中挂载脚本的功能
-- 2、组件对应Unity原生的各种Component和Script,容器对应Unity原生的GameObject
-- 3、写逻辑时完全不需要关注脚本调度,在cs中脚本函数怎么调度的,这里就怎么调度,只是要注意接口变动,lua侧没有Get、Set访问器
-- 注意:
-- 1、Lua侧组件的名字并不总是和Unity侧组件名字同步,Lua侧组件名字会作为组件系统中组件的标识
-- 2、Lua侧组件名字会在组件创建时提取Unity侧组件名字,随后二者没有任何关联,Unity侧组件名字可以随便改
-- 3、虽然Unity侧组件名字随后可以随意改,但是不建议(有GC),此外Lua侧组件一旦创建,使用时全部以Lua侧名字为准
-- 4、虽然支持Update、LateUpdate、FixedUpdate更新,但是UI组件最好不要使用---不要定义这些函数即可
-- 5、需要定时刷新的界面,最好启用定时器、协程,界面需要刷新的频率一般较低,倒计时之类的只需要每秒钟更新一次即可
--]]

local UIBaseComponent = BaseClass("UIBaseComponent", Updatable)
local base = Updatable

-- 构造函数:除非特殊情况,所有子类不要再写这个函数,初始化工作放OnCreate
-- 多种重载方式:
-- 1、ComponentTypeClass.New(relative_path)
-- 2、ComponentTypeClass.New(child_index)
-- 3、ComponentTypeClass.New(unity_gameObject)
local function __init(self, holder, var_arg)
	assert(not IsNull(holder), "Err : holder nil!")
	assert(not IsNull(holder.transform), "Err : holder tansform nil!")
	assert(not IsNull(var_arg), "Err: var_arg nil!")
	-- 窗口view层脚本
	self.view = nil
	-- 持有者
	self.holder = holder
	-- 脚本绑定的transform
	self.transform = nil
	-- transform对应的gameObject
	self.gameObject = nil
	-- trasnform对应的RectTransform
	self.rectTransform = nil
	-- 名字:Unity中获取Transform的名字是有GC的,而Lua侧组件大量使用了名字,所以这里缓存下
	self.__name = nil
	-- 绑定数据:在某些场景下可以提供诸多便利
	self.__bind_data = nil
	-- 可变类型参数,用于重载
	self.__var_arg = var_arg
	-- 这里一定要等资源异步加载完毕才启用Update
	self:EnableUpdate(false)
end

-- 析构函数:所有组件的子类不要再写这个函数,释放工作全部放到OnDestroy
local function __delete(self)
	self:OnDestroy()
end

-- 创建
local function OnCreate(self)
	assert(not IsNull(self.holder), "Err : holder nil!")
	assert(not IsNull(self.holder.transform), "Err : holder tansform nil!")
	-- 初始化view
	if self._class_type == UILayer then
		self.view = nil
	else
		local now_holder = self.holder
		while not IsNull(now_holder) do	
			if now_holder._class_type == UILayer then
				self.view = self
				break
			elseif not IsNull(now_holder.view) then
				self.view = now_holder.view
				break
			end
			now_holder = now_holder.holder
		end
		assert(not IsNull(self.view))
	end
	
	-- 初始化其它基本信息
	if type(self.__var_arg) == "string" then
		-- 与持有者的相对路径
		self.transform = UIUtil.FindTrans(self.holder.transform, self.__var_arg)
		self.gameObject = self.transform.gameObject
	elseif type(self.__var_arg) == "number" then
		-- 持有者第index个孩子
		self.transform = UIUtil.GetChild(self.holder.transform, self.__var_arg)
		self.gameObject = self.transform.gameObject
	elseif type(self.__var_arg) == "userdata" then
		-- Unity侧GameObject
		self.gameObject = self.__var_arg
		self.transform = gameObject.transform
	else
		error("OnCreate : error params list! "..type(self.__var_arg).." "..tostring(self.__var_arg))
	end
	self.__name = self.gameObject.name
	self.rectTransform = UIUtil.FindComponent(self.transform, typeof(CS.UnityEngine.RectTransform))
	self.__var_arg = nil
end

-- 打开
local function OnEnable(self)
	-- 启用更新函数
	self:EnableUpdate(true)
end

-- 获取名字
local function GetName(self)
	return self.__name
end

-- 设置名字:toUnity指定是否同时设置Unity侧的名字---不建议,实在想不到什么情况下会用,但是调试模式强行设置,好调试
local function SetName(self, name, toUnity)
	if self.holder.OnComponentSetName ~= nil then
		self.holder:OnComponentSetName(self, name)
	end
	self.__name = name
	if toUnity or Config.Debug then
		if IsNull(self.gameObject) then
			Logger.LogError("gameObject null, you maybe have to wait for loading prefab finished!")
			return
		end
		self.gameObject.__name = name
	end
end

-- 设置绑定数据
local function SetBindData(self, data)
	self.__bind_data = data
end

-- 获取绑定数据
local function GetBindData(self)
	return self.__bind_data
end

-- 激活、反激活
local function SetActive(self, active)
	if active then
		self.gameObject:SetActive(active)
		self:OnEnable()
	else
		self:OnDisable()
		self.gameObject:SetActive(active)
	end
end

-- 获取激活状态
local function GetActive(self)
	return self.gameObject.activeSelf
end

-- 等待资源准备完毕:用于协程
local function WaitForCreated(self)
	coroutine.waituntil(function()
		return not IsNull(self.gameObject)
	end)
end

-- 关闭
local function OnDisable(self)
	-- 禁用更新函数
	self:EnableUpdate(false)
end

-- 销毁
local function OnDestroy(self)
	if self.holder.OnComponentDestroy ~= nil then
		self.holder:OnComponentDestroy(self)
	end
	self.holder = nil
	self.transform = nil
	self.gameObject = nil
	self.rectTransform = nil
	self.__name = nil
	self.__bind_data = nil
end

UIBaseComponent.__init = __init
UIBaseComponent.__delete = __delete
UIBaseComponent.OnCreate = OnCreate
UIBaseComponent.OnEnable = OnEnable
UIBaseComponent.GetName = GetName
UIBaseComponent.SetName = SetName
UIBaseComponent.SetBindData = SetBindData
UIBaseComponent.GetBindData = GetBindData
UIBaseComponent.SetActive = SetActive
UIBaseComponent.GetActive = GetActive
UIBaseComponent.WaitForCreated = WaitForCreated
UIBaseComponent.OnDisable = OnDisable
UIBaseComponent.OnDestroy = OnDestroy

return UIBaseComponent

2 . UIBaseContainer.lua

--[[
-- UI容器基类:当一个UI组件持有其它UI组件时,它就是一个容器类,它要负责调度其它UI组件的相关函数
-- 注意:
-- 1、window.view是窗口最上层的容器类
-- 2、AddComponent用来添加组件,一般在window.view的OnCreate中使用,RemoveComponent相反
-- 3、GetComponent用来获取组件,GetComponents用来获取一个类别的组件
-- 4、很重要:子组件必须保证名字互斥,即一个不同的名字要保证对应于Unity中一个不同的Transform
--]]

local UIBaseContainer = BaseClass("UIBaseContainer", UIBaseComponent)
-- 基类,用来调用基类方法
local base = UIBaseComponent

-- 创建
local function OnCreate(self)
	base.OnCreate(self)
	self.components = {}
	self.length = 0
end

-- 打开
local function OnEnable(self)
	base.OnEnable(self)
	self:Walk(function(component)
		component:OnEnable()
	end)
end

-- 遍历:注意,这里是无序的
local function Walk(self, callback, component_class)
	for _,components in pairs(self.components) do
		for cmp_class,component in pairs(components) do
			if component_class == nil then
				callback(component)
			elseif cmp_class == component_class then
				callback(component)
			end
		end
	end
end

-- 如果必要,创建新的记录,对应Unity下一个Transform下所有挂载脚本的记录表
local function AddNewRecordIfNeeded(self, name)
	if self.components[name] == nil then
		self.components[name] = {}
	end
end

-- 记录Component
local function RecordComponent(self, name, component_class, component)
	-- 同一个Transform不能挂两个同类型的组件
	assert(self.components[name][component_class] == nil, "Aready exist component_class : ", component_class.__cname)
	self.components[name][component_class] = component
end

-- 子组件改名回调
local function OnComponentSetName(self, component, new_name)
	AddNewRecordIfNeeded(self, new_name)
	-- 该名字对应Unity的Transform下挂载的所有脚本都要改名
	local old_name = component:GetName()
	local components = self.components[old_name]
	for k,v in pairs(components) do
		v:SetName(new_name)
		RecordComponent(self, new_name, k, v)
	end
	self.components[old_name] = nil
end

-- 子组件销毁
local function OnComponentDestroy(self, component)
	self.length = self.length - 1
end

-- 添加组件
-- 多种重载方式
-- 1、直接添加Lua侧组件:inst:AddComponent(ComponentTypeClass, luaComponentInst)
-- 2、指定Lua侧组件类型和必要参数,新建组件并添加,多种重载方式:
--    A)inst:AddComponent(ComponentTypeClass, relative_path)
--    B)inst:AddComponent(ComponentTypeClass, child_index)
--    C)inst:AddComponent(ComponentTypeClass, unity_gameObject)
local function AddComponent(self, component_target, var_arg, ...)
	assert(component_target.__ctype == ClassType.class)
	local component_inst = nil
	local component_class = nil
	if type(var_arg) == "table" and var_arg.__ctype == ClassType.instance then
		component_inst = var_arg
		component_class = var_arg._class_type
	else
		component_inst = component_target.New(self, var_arg)
		component_class = component_target
		component_inst:OnCreate(...)
	end
	
	local name = component_inst:GetName()
	AddNewRecordIfNeeded(self, name)
	RecordComponent(self, name, component_class, component_inst)
	self.length = self.length + 1
	return component_inst
end

-- 获取组件
local function GetComponent(self, name, component_class)
	local components = self.components[name]
	if components == nil then
		return nil
	end
	
	if component_class == nil then
		-- 必须只有一个组件才能不指定类型,这一点由外部代码保证
		assert(table.count(components) == 1, "Must specify component_class while there are more then one component!")
		for _,component in pairs(components) do
			return component
		end
	else
		return components[component_class]
	end
end

-- 获取一系列组件:2种重载方式
-- 1、获取一个类别的组件
-- 2、获取某个name(Transform)下的所有组件
local function GetComponents(self, component_target)
	local components = {}
	if type(component_target) == "table" then
		self:Walk(function(component)
			table.insert(component)
		end, component_target)
	elseif type(component_target) == "string" then
		components = self.components[component_target]
	else
		error("GetComponents params err!")
	end
	return components
end

-- 获取组件个数
local function GetComponentsCount(self)
	return self.length
end

-- 移除组件
local function RemoveComponent(self, name, component_class)
	local component = self:GetComponent(name, component_class)
	if component ~= nil then
		local cmp_class = component._class_type
		component:Delete()
		self.components[name][cmp_class] = nil
	end
end

-- 移除一系列组件:2种重载方式
-- 1、移除一个类别的组件
-- 2、移除某个name(Transform)下的所有组件
local function RemoveComponents(self, component_target)
	local components = self:GetComponents(component_target)
	for _,component in pairs(components) do
		local cmp_name = component:GetName()
		local cmp_class = component._class_type
		component:Delete()
		self.components[cmp_name][cmp_class] = nil
	end
	return components
end

-- 关闭
local function OnDisable(self)
	base.OnDisable(self)
	self:Walk(function(component)
		component:OnDisable()
	end)
end

-- 销毁
local function OnDestroy(self)
	self:Walk(function(component)
		-- 说明:现在一个组件可以被多个容器持有,但是holder只有一个,所以由holder去释放
		if component.holder == self then
			component:Delete()
		end
	end)
	self.components = nil
	base.OnDestroy(self)
end

UIBaseContainer.OnCreate = OnCreate
UIBaseContainer.OnEnable = OnEnable
UIBaseContainer.Walk = Walk
UIBaseContainer.OnComponentSetName = OnComponentSetName
UIBaseContainer.OnComponentDestroy = OnComponentDestroy
UIBaseContainer.AddComponent = AddComponent
UIBaseContainer.GetComponent = GetComponent
UIBaseContainer.GetComponents = GetComponents
UIBaseContainer.GetComponentsCount = GetComponentsCount
UIBaseContainer.RemoveComponent = RemoveComponent
UIBaseContainer.RemoveComponents = RemoveComponents
UIBaseContainer.OnDisable = OnDisable
UIBaseContainer.OnDestroy = OnDestroy

return UIBaseContainer

3 . UIBaseCtrl.lua

--[[
-- UI控制层基类:发送网络请求(网络数据)、操作游戏逻辑、修改模型数据(本地数据)
-- 注意:
-- 1、UI控制层用于衔接Model层和View层,主要是用来修改数据,或者进行游戏逻辑控制
-- 2、修改数据:界面操作相关数据直接写Model层、游戏逻辑相关数据写数据中心
-- 3、游戏控制:发送网络请求、调用游戏控制逻辑函数
-- 4、Ctrl层是无状态的,不能保存变量--调试模式下强制
--]]

local UIBaseCtrl = BaseClass("UIBaseCtrl")

local function __init(self, model)
	assert(model ~= nil)
	-- 强制不能直接写变量
	if Config.Debug then
		self.model = setmetatable({}, {
			__index = model,
			__newindex = function(tb, key, value)
				if type(value) ~= "function" then
					error("You can't save data here!", 2)
				end
			end
		})
	else
		self.model = model
	end
end

local function __delete(self)
	self.model = nil
end

UIBaseCtrl.__init = __init
UIBaseCtrl.__delete = __delete

return UIBaseCtrl

4 . UIBaseModel.lua

--[[
-- UI模型层基类:该界面相关数据,同时负责消息定制
-- 注意:
-- 1、数据大体分为两类:游戏逻辑数据、界面控制数据
-- 2、游戏逻辑数据:从游戏数据中心取数据,这里不做为数据源,只做中转和必要处理(如排序)---游戏中心数据改动以后在这里监听变化
-- 3、界面控制数据:一般会影响到多个界面展示的控制数据,登陆界面显示当前服务器,当受到选服界面操作的影响
-- 4、界面Model层对View层是只读不写的,一定不要在View层写Model
-- 5、界面Model层不依赖Ctrl层和View层,意思是说拿掉这这两层代码,Model依旧能完好运行
-- 6、界面Model层数据只影响UI,不影响游戏逻辑,游戏逻辑不能从Model层取数据,意思是没有界面,游戏依旧能跑
--]]

local UIBaseModel = BaseClass("UIBaseModel")

-- 如非必要,别重写构造函数,使用OnCreate初始化
local function __init(self, ui_name)
	-- 回调管理,使其最长保持和Model等同的生命周期
	self.__ui_callback = {}
	self.__data_callback = {}
	self.__ui_name = ui_name
	self:OnCreate()
end

-- 如非必要,别重写析构函数,使用OnDestroy销毁资源
local function __delete(self)
	self:OnDestroy()
	for k,v in pairs(self.__ui_callback) do
		self:RemoveUIListener(k, v)
	end
	for k,v in pairs(self.__data_callback) do
		self:RemoveDataListener(k, v)
	end
	self.__ui_callback = nil
	self.__data_callback = nil
	self.__ui_name = nil
end

-- 创建:变量定义,初始化,消息注册
-- 注意:窗口生命周期内保持的成员变量放这
local function OnCreate(self)
end

-- 打开:刷新数据模型
-- 注意:窗口关闭时可以清理的成员变量放这
local function OnEnable(self, ...)
end

-- 关闭
-- 注意:必须清理OnEnable中声明的变量
local function OnDisable(self)
end

-- 销毁
-- 注意:必须清理OnCreate中声明的变量
local function OnDestroy(self)
end

-- 注册消息
local function OnAddListener(self)
end

-- 注销消息
local function OnRemoveListener(self)
end

-- 激活:给UIManager用,别重写
local function Activate(self, ...)
	self:OnAddListener()
	self:OnEnable(...)
end

-- 反激活:给UIManager用,别重写
local function Deactivate(self)
	self:OnRemoveListener()
	self:OnDisable()
end

local function AddCallback(keeper, msg_name, callback)
	assert(callback ~= nil)
	keeper[msg_name] = callback
end

local function GetCallback(keeper, msg_name)
	return keeper[msg_name]
end

local function RemoveCallback(keeper, msg_name, callback)
	assert(callback ~= nil)
	keeper[msg_name] = nil
end

-- 注册UI数据监听事件,别重写
local function AddUIListener(self, msg_name, callback)
	local bindFunc = Bind(self, callback)
	AddCallback(self.__ui_callback, msg_name, bindFunc)
	UIManager:GetInstance():AddListener(msg_name, bindFunc)
end

-- 发送UI数据变动事件,别重写
local function UIBroadcast(self, msg_name, ...)
	UIManager:GetInstance():Broadcast(msg_name, ...)
end

-- 注销UI数据监听事件,别重写
local function RemoveUIListener(self, msg_name, callback)
	local bindFunc = GetCallback(self.__ui_callback, msg_name)
	RemoveCallback(self.__ui_callback, msg_name, bindFunc)
	UIManager:GetInstance():RemoveListener(msg_name, bindFunc)
end

-- 注册游戏数据监听事件,别重写
local function AddDataListener(self, msg_name, callback)
	local bindFunc = Bind(self, callback)
	AddCallback(self.__data_callback, msg_name, bindFunc)
	DataManager:GetInstance():AddListener(msg_name, bindFunc)
end

-- 注销游戏数据监听事件,别重写
local function RemoveDataListener(self, msg_name, callback)
	local bindFunc = GetCallback(self.__data_callback, msg_name)
	RemoveCallback(self.__data_callback, msg_name, bindFunc)
	DataManager:GetInstance():RemoveListener(msg_name, bindFunc)
end

UIBaseModel.__init = __init
UIBaseModel.__delete = __delete
UIBaseModel.OnCreate = OnCreate
UIBaseModel.OnEnable = OnEnable
UIBaseModel.OnDisable = OnDisable
UIBaseModel.OnDestroy = OnDestroy
UIBaseModel.OnAddListener = OnAddListener
UIBaseModel.OnRemoveListener = OnRemoveListener
UIBaseModel.Activate = Activate
UIBaseModel.Deactivate = Deactivate
UIBaseModel.AddUIListener = AddUIListener
UIBaseModel.UIBroadcast = UIBroadcast
UIBaseModel.RemoveUIListener = RemoveUIListener
UIBaseModel.AddDataListener = AddDataListener
UIBaseModel.RemoveDataListener = RemoveDataListener

return UIBaseModel

5 . UIBaseRecordModel.lua

--[[
-- UI带有子窗口记忆功能的模型层基类:窗口被重新打开时会自动打开之前没有关闭的子级窗口
--]]

local UIBaseRecordModel = BaseClass("UIBaseRecordModel", UIBaseModel)
local base = UIBaseModel

-- 创建
local function OnCreate(self)
	base.OnCreate(self)
	-- 子级窗口栈
	self.__window_stack = {}
	-- 是否启用记录
	self.__enable_record = false
	-- 保持Model
	UIManager:GetInstance():SetKeepModel(self.__ui_name, true)
end

-- 打开
local function OnEnable(self, ...)
	base.OnEnable(self, ...)
	-- 重新打开上次保留的窗口
	table.walk(self.__window_stack, function(index, ui_name)
		UIManager:GetInstance():OpenWindow(ui_name)
	end)
	self.__enable_record = true
end

-- 获取栈
local function GetWindowStack(self)
	return self.__window_stack
end

-- 清空栈
local function ClearWindowStack(self)
	self.__window_stack = {}
end

-- 子级窗口入栈
local function OnWindowOpen(self, window)
	if not self.__enable_record or window.Layer:GetName() ~= UILayers.NormalLayer.Name then
		return
	end
	
	table.insert(self.__window_stack, window.Name)
	-- 保持Model
	UIManager:GetInstance():SetKeepModel(window.Name, true)
end

-- 出栈
local function OnWindowClose(self, window)
	if not self.__enable_record or window.Layer:GetName() ~= UILayers.NormalLayer.Name then
		return
	end
	
	local index = nil
	for i,v in pairs(self.__window_stack) do
		if v == window.Name then
			index = i
			break
		end
	end
	if index then
		local length = table.length(self.__window_stack)
		for i = 0, length - index do
			local ui_name = table.remove(self.__window_stack, length - i)
			-- 取消保持Model
			UIManager:GetInstance():SetKeepModel(ui_name, false)
		end
	end
end

-- 注册消息
local function OnAddListener(self)
	base.OnAddListener(self)
	self:AddUIListener(UIMessageNames.UIFRAME_ON_WINDOW_OPEN, OnWindowOpen)
	self:AddUIListener(UIMessageNames.UIFRAME_ON_WINDOW_CLOSE, OnWindowClose)
end

-- 注销消息
local function OnRemoveListener(self)
	base.OnRemoveListener(self)
	self:RemoveUIListener(UIMessageNames.UIFRAME_ON_WINDOW_OPEN, OnWindowOpen)
	self:RemoveUIListener(UIMessageNames.UIFRAME_ON_WINDOW_CLOSE, OnWindowClose)
end

-- 关闭
local function OnDisable(self)
	base.OnDisable(self)
	self.__enable_record = false
end

-- 销毁
local function OnDestroy(self)
	self.__window_stack = nil
	base.OnDestroy(self)
end

UIBaseRecordModel.OnCreate = OnCreate
UIBaseRecordModel.OnEnable = OnEnable
UIBaseRecordModel.GetWindowStack = GetWindowStack
UIBaseRecordModel.ClearWindowStack = ClearWindowStack
UIBaseRecordModel.OnAddListener = OnAddListener
UIBaseRecordModel.OnRemoveListener = OnRemoveListener
UIBaseRecordModel.OnDisable = OnDisable
UIBaseRecordModel.OnDestroy = OnDestroy

return UIBaseRecordModel

6 . UIBaseView.lua

--[[
-- UI视图层基类:该界面所有UI刷新操作,只和展示相关的数据放在这,只有操作相关数据放Model去
-- 注意:
-- 1、被动刷新:所有界面刷新通过消息驱动---除了打开界面时的刷新
-- 2、对Model层可读,不可写---调试模式下强制
-- 3、所有写数据、游戏控制操作、网络相关操作全部放Ctrl层
-- 4、Ctrl层不依赖View层,但是依赖Model层
-- 5、任何情况下不要在游戏逻辑代码操作界面刷新---除了打开、关闭界面
--]]

local UIBaseView = BaseClass("UIBaseView", UIBaseContainer)
local base = UIBaseContainer

-- 构造函数:必须把基类需要的所有参数列齐---即使在这里不用,提高代码可读性
-- 子类别再写构造函数,初始化工作放OnCreate
local function __init(self, holder, var_arg, model, ctrl)
	assert(model ~= nil)
	assert(ctrl ~= nil)
	self.ctrl = ctrl
	-- 强制不能直接写Model层
	if Config.Debug then
		self.model = setmetatable({}, {
			__index = model,
			__newindex = function(tb, key, value)
				error("You can't write model derectly!", 2)
			end
		})
	else 
		self.model = model
	end
	
	-- 窗口画布
	self.canvas = nil
	-- 窗口基础order,窗口内添加的其它canvas设置的order都以它做偏移
	self.base_order = 0
end

-- 创建:资源加载完毕
local function OnCreate(self)
	base.OnCreate(self)
	-- 窗口画布
	self.canvas = self:AddComponent(UICanvas, "", 0)
	-- 回调管理,使其最长保持和View等同的生命周期
	self.__ui_callback = {}
	-- 初始化RectTransform
	self.rectTransform.offsetMax = Vector2.zero
	self.rectTransform.offsetMin = Vector2.zero
	self.rectTransform.localScale = Vector3.one
	self.rectTransform.localPosition = Vector3.zero
end

-- 打开:窗口显示
local function OnEnable(self)
	self.base_order = self.holder:PopWindowOder()
	base.OnEnable(self)
	self:OnAddListener()
end

-- 注册消息
local function OnAddListener(self)
end

-- 注销消息
local function OnRemoveListener(self)
end

local function AddCallback(keeper, msg_name, callback)
	assert(callback ~= nil)
	keeper[msg_name] = callback
end

local function GetCallback(keeper, msg_name)
	return keeper[msg_name]
end

local function RemoveCallback(keeper, msg_name, callback)
	assert(callback ~= nil)
	keeper[msg_name] = nil
end

-- 注册UI数据监听事件,别重写
local function AddUIListener(self, msg_name, callback)
	local bindFunc = Bind(self, callback)
	AddCallback(self.__ui_callback, msg_name, bindFunc)
	UIManager:GetInstance():AddListener(msg_name, bindFunc)
end

-- 注销UI数据监听事件,别重写
local function RemoveUIListener(self, msg_name, callback)
	local bindFunc = GetCallback(self.__ui_callback, msg_name)
	RemoveCallback(self.__ui_callback, msg_name, bindFunc)
	UIManager:GetInstance():RemoveListener(msg_name, bindFunc)
end

-- 关闭:窗口隐藏
local function OnDisable(self)
	self:OnRemoveListener()
	base.OnDisable(self)
	self.holder:PushWindowOrder()
end

-- 销毁:窗口销毁
local function OnDestroy(self)
	for k,v in pairs(self.__ui_callback) do
		self:RemoveUIListener(k, v)
	end
	self.model = nil
	self.ctrl = nil
	self.__ui_callback = nil
	base.OnDestroy(self)
end

UIBaseView.__init = __init
UIBaseView.OnCreate = OnCreate
UIBaseView.OnEnable = OnEnable
UIBaseView.OnAddListener = OnAddListener
UIBaseView.OnRemoveListener = OnRemoveListener
UIBaseView.OnDisable = OnDisable
UIBaseView.AddUIListener = AddUIListener
UIBaseView.RemoveUIListener = RemoveUIListener
UIBaseView.OnDestroy = OnDestroy

return UIBaseView

支付宝捐赠

打赏红包

你可能感兴趣的:(Lua)