在lua里实现事件监听系统

36 篇文章 0 订阅
14 篇文章 1 订阅

功能优势:1.事件使用2级标签【2个key值】,相比起单个key值,2级标签能够适应多种场合与情况,。2.监听的绑定已支持自动销毁,随着物体被销毁而注销事件,并不需要人为的操作,以减少bug。3.事件支持休眠与唤醒功能,在物体隐藏时不在响应事件的接收,唤醒后自动刷新数据。【该功能很好适用于MVC框架,主要给在UI界面使用,界面某个物体隐藏时理应不在刷新数据,而它显示出来时会强制更新一次数据】

PS:该文章部分源码依据于此在lua里实现类似unity生命周期的监听事件_Le_Sam的博客-CSDN博客

正文:

源码

local class3 = function(_name, _super)
	local name = _name
	local super = _super
	if type(_name) ~= "string" then
		name = nil
		super = nil
	end
	local class_type = {}
	class_type.ctor = false
	class_type.super = super

	local vtbl = {super = super, className = name}
	class_type.vtbl = vtbl
 
	setmetatable(class_type, {
	__index = function( t, k )
		return vtbl[k]
	end,
	__newindex = function(t, k, v)
		vtbl[k] = v
	end
	})
 
	if super then
		setmetatable(vtbl, {__index =
			function(t,k)
				local ret = super.vtbl[k]
				vtbl[k] = ret
				return ret
			end
		})
	end
	
 	class_type.new = function(...)
		local obj = {}
		setmetatable(obj, {__index = vtbl})
		do
			--[[
			local create
			create = function(c, ...)
				if c.super then
					create(c.super, ...)
				end
				if c.ctor then
					c.ctor(obj, ...)
				end
			end

			create(class_type, ...)
			]]
			if (class_type.ctor) then
				class_type.ctor(obj, ...);
			end
		end
        obj.isClassObject = true
		return obj
	end

	return class_type
end

return class3

--[[
--Description: 事件派发器,提供事件订阅和分发接口
--]]


local class3 = require("class3")
local Helpers = require("Helpers")
local EventDispatcherEx = class3("EventDispatcherEx")

local mListener = {}
local _mGUID = 1;

-- 事件订阅
function EventDispatcherEx:RegisterEventListener(dataOwner, dataName, func, priority)

    assert(type(func) == "function", "func 必须是function 类型");
	
    priority = priority or 0;
    
    local owner = mListener[dataOwner];
    
    if not owner then
        owner = {};
        mListener[dataOwner] = owner;
    end
    
    local listeners = owner[dataName];
    
    if not listeners then
        listeners = {};
        owner[dataName] = listeners;
    end
    
    local handle = _mGUID;
    _mGUID = handle + 1;
    
    listeners[handle] = {func = func, priority = priority, handle = handle};
    
    return function()
        self:UnregisterEventListener(dataOwner, dataName, handle);
    end
end

-- 客户端事件取消订阅
function EventDispatcherEx:UnregisterEventListener(dataOwner, dataName, handle)
    local owner = mListener[dataOwner];
	
	if not owner then
		--logwarning("DataNotificationCenter:unregister : 不存在的dataOwner");
		return;
	end
	
	local listeners = owner[dataName];
	
	if not listeners then
		logwarning("DataNotificationCenter:unregister : 不存在的dataName");
		return;
	end
	
	listeners[handle] = nil;
	
	if not next(listeners) then
		-- listeners空了
		owner[dataName] = nil;
		
		if not next(owner) then
			mListener[dataOwner] = nil;
		end
	end
end

function EventDispatcherEx:UnregisterAllByDataName(dataOwnerToRemove, dataNameToRemove)
	for dataOwner, owner in pairs(mListener) do
        if dataOwner == dataOwnerToRemove then
            for dataName, listeners in pairs(owner) do
                if dataName == dataNameToRemove then
                    owner[dataName] = nil;
                    
                    if not next(owner) then
                        mListener[dataOwnerToRemove] = nil;
                    end
                    
                    return;
                end
            end
        end
	end
end

-- 客户端事件派发
function EventDispatcherEx:DispatchEvent(dataOwner, dataName, ...)
    local owner = mListener[dataOwner];
	if not owner then
		return;
	end
	
	local listeners = owner[dataName];
	if not listeners then
		return;
	end
	
	local funcInfoList = {};
	
	for _, funcInfo in pairs(listeners) do
		table.insert(funcInfoList, funcInfo);
	end
	
	table.sort(funcInfoList, function(a, b)
		return a.priority < b.priority;
	end)
	
	for _, v in ipairs(funcInfoList) do
        
        if v.func then
            v.func(...)
        end

		-- if v.func(...) then
		-- 	self:UnregisterEventListener(dataOwner, dataName, v.handle);
		-- end
	end
end

-- AutoBind自动绑定,依据unity生命周期进行的绑定,物体删除后自动清除事件,物体隐藏时暂停监听,直到显示时在恢复
-- 参数,使用dataOwner和dataName两层结构是为了保证独立性,单单使用key值容易混淆
--[[
  tr				物体
  dataOwner   		1级Key ==> Tabel类型
  dataName     		2级Key ==> Object类型
  func      		回调函数
  isEnableCB    	物体显示时,直接触发func(),用于保持界面内的数据是最新的,默认为false
  priority    		优先级,默认为0
--]]
function EventDispatcherEx:AutoBind(tr, dataOwner, dataName, func, isEnableCB, priority)

	assert(tr ~= nil and tostring(tr) ~= "null", "tr 必须是Transform类型");

	local unregFunc = nil; 
	local bDestory = false;
	local bUnbind = false;
	
	local function unRegister()
		if unregFunc ~= nil then
			unregFunc();
		end
		unregFunc = nil
	end

	local function onEnable()
		--logdebug(string.format("EventDispatcherEx AutoBind ==> onEnable name:%s", tr.name))
		unRegister();
		unregFunc = self:RegisterEventListener(dataOwner, dataName, func, priority);
		if isEnableCB then
			func();
		end
	end

	local function onDisable()
		--logdebug(string.format("EventDispatcherEx AutoBind ==> onDisable name:%s", tr.name))
		unRegister();
	end

	local function onDestory()
		--logdebug(string.format("EventDispatcherEx AutoBind ==> onDestory name:%s", tr.name))
		bDestory = true;
		if not bUnbind then
			unRegister();
		end
	end

    local function unBind()
		bUnbind = true;
		if not bDestory then
			unRegister();
			Helpers.RemoveEnableListener(tr, onEnable)
			Helpers.RemoveDisableListener(tr, onDisable)
			Helpers.RemoveDestroyListener(tr, onDestory)
		end
	end


    Helpers.AddEnableListener(tr, onEnable)
	Helpers.AddDisableListener(tr, onDisable)
	Helpers.AddDestroyListener(tr, onDestory)
	return unBind; -- 取消事件监听、生命周期相关监听
end

return EventDispatcherEx

示例:

-- Model-Controller
GameData = {}

GameData.UserInfo =
{
    gold = 0,
}

GameData.UpdataGold = function(num)
    GameData.UserInfo.gold = num
    EventDispatcherEx:DispatchEvent(GameData.UserInfo, "gold", GameData.UserInfo.gold)
end

return GameData
-- View
local UITest = ClassView("UITest")
local EventDispatcherEx = require("EventDispatcherEx")

function UITest:OnCreate() 
    self.goldTxt = self:FindChild("goldTxt"):GetComponent("Text")
    self:InitEvent()
end

function UITest:InitEvent()
    local function updataGoldInfo()
        local num = 0;
        if GameData.UserInfo.gold ~= nil then
            num = GameData.UserInfo.gold
        end
        self.goldTxt.text = tostring(num);
    end
    EventDispatcherEx:AutoBind(self.goldTxt.transform, GameData.UserInfo, "gold", updataGoldInfo)
end
-- Test
local Main = {}
local GameData= require("GameData")

function Main.Start()
	GameData.UpdataGold(10086)
end

Main.Start()
return Main

结语:GameData.UpdataGold接口还可以更进一步的优化,改为数据绑定的方式【此方式自动调用DispatchEvent接口,减少手写“EventDispatcherEx:DispatchEvent()”】
后续内容。。。敬请期待

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值