c1_lua/lua/app/ui/base_ui.lua
2023-04-07 15:22:56 +08:00

689 lines
17 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 部分函数和变量名前加了下划线主要是避免被子类不经意间重写,同时也不应该直接调用这些属性或者方法
local BF_UI_HELPER = GConst.TYPEOF_UNITY_CLASS.BF_UI_HELPER
local BF_BASE_SORTING_ORDER_HELPER = GConst.TYPEOF_UNITY_CLASS.BF_BASE_SORTING_ORDER_HELPER
local BaseUI = {
_baseAlreadyClosed = false,
_baseUIOrder = 0
}
function BaseUI:ctor()
end
-- 需要在打开界面之前预先加载的资源列表,例如
-- {
-- [GConst.TYPEOF_UNITY_CLASS.GAME_OBJECT] = {
-- "xxx1", "xxx2"
-- }
-- }
function BaseUI:getPreLoadList()
return nil
end
function BaseUI:showCommonBG()
return true
end
-- 界面prefab路径
function BaseUI:getPrefabPath()
return ""
end
-- 关闭界面
function BaseUI:closeUI()
UIManager:closeUI(self)
end
function BaseUI:addLoadUICompleteListener(callback)
if self._baseLoadComplete then
if callback then
callback()
end
else
self._onLoadUICompleteCallback = callback
end
end
function BaseUI:getIsLoadedComplete()
return self._baseLoadComplete
end
function BaseUI:addEnterAniCompleteListener(callback)
self._onEnterAniCompleteCallback = callback
end
function BaseUI:addExitAniCompleteListener(callback)
self._onExitAniCompleteCallback = callback
end
-- 界面进入动画播放完
function BaseUI:onEnterAnimationComplete()
end
-- 界面退出动画播放完
function BaseUI:onExitAnimationComplete()
end
-- 界面的prefab加载完成后被调用
function BaseUI:onLoadRootComplete()
end
-- 当UI对象被创建时调用注意此时还没加载好ui预制件
function BaseUI:onCreate()
end
-- 当此界面关闭时调用
function BaseUI:onClose()
end
-- 当此界面之上的其他界面都关闭,导致此界面处于最顶层时调用,第一次创建此界面的时候不会调用
-- 与onCover配对
function BaseUI:onReshow()
end
-- 当此界面处于最顶层的时候打开一个新的界面添加到此界面之上时调用
-- 与onReshow配对
function BaseUI:onCover()
end
-- 当此界面需要刷新时,界面刷新逻辑都在这里面处理
function BaseUI:onRefresh()
end
-- 当界面隐藏或从隐藏状态恢复显示时
function BaseUI:onUIVisibleChanged()
end
-- 当界面上的模型激活状态改变时
function BaseUI:onUIModelActiveChanged(active)
end
-- 当界面层级被重新设置时
function BaseUI:onSetUIOrder()
end
-- 此界面是否已经关闭了
function BaseUI:isClosed()
return self._baseAlreadyClosed
end
-- 此界面是否被隐藏了,只有当被全屏界面挡住才会隐藏当前界面
function BaseUI:isVisible()
return self._baseVisible ~= false
end
-- 此界面是不是当前最上层的
function BaseUI:isTopUI()
return UIManager:getTopUIObj() == self
end
-- 响应安卓后退事件
function BaseUI:onPressBackspace()
end
-- 添加粒子之类的特效
function BaseUI:addEffect(effect, parent, order)
if self:isClosed() then
return
end
if self._baseEffectList == nil then
self._baseEffectList = {}
end
self._baseEffectList[effect] = 1
if self._baseUIHelper == nil then
self._baseUIHelper = self.root:getGameObject():AddComponent(BF_UI_HELPER)
end
self._baseUIHelper:AddEffect(effect:getComponent(BF_BASE_SORTING_ORDER_HELPER), self._baseUIOrder, order)
if parent then
effect:setParent(parent, false)
end
end
-- 对已经存在的子节点层级排序
function BaseUI:sortChildOrder(childObj, order)
if self._baseUIHelper == nil then
self._baseUIHelper = self.root:getGameObject():AddComponent(BF_UI_HELPER)
end
self._baseUIHelper:AddEffect(childObj:getComponent(BF_BASE_SORTING_ORDER_HELPER), self._baseUIOrder, order)
end
-- 移除粒子之类的特效
function BaseUI:removeEffect(effect, isNeedCleanUp)
if self._baseEffectList then
local effectNode = self._baseEffectList[effect]
if effectNode then
self._baseEffectList[effect] = nil
if self._baseUIHelper then
self._baseUIHelper:RemoveEffect(effect:getComponent(BF_BASE_SORTING_ORDER_HELPER))
end
effect:removeFromParent(isNeedCleanUp)
end
end
end
-- 界面的自定义事件添加监听
function BaseUI:addEventListener(key, func, priority)
if self:isClosed() then
return
end
local tag = EventManager:addEventListener(key, func, priority)
self._baseEventListeners = self._baseEventListeners or {}
self._baseEventListeners[key] = tag
end
-- 界面的自定义事件移除监听
function BaseUI:removeEventListener(key)
if self._baseEventListeners and self._baseEventListeners[key] then
EventManager:removeEventListener(key, self._baseEventListeners[key])
self._baseEventListeners[key] = nil
end
end
-- 请查看UIManager.UI_TYPE的说明
function BaseUI:getUIType()
return UIManager.UI_TYPE.DEFAULT
end
-- 获取界面索引其实就是界面lua的路径
function BaseUI:getUIIndex()
return self._baseUIIndex
end
-- 此界面配套的BGM
function BaseUI:getBGMId()
return nil
end
function BaseUI:setUIIndex(index)
self._baseUIIndex = index
end
function BaseUI:setUIOrder(siblingIndex, order)
self._baseSiblingIndex = siblingIndex
self._baseUIOrder = order
if self.root then
self.root:getTransform():SetSiblingIndex(self._baseSiblingIndex)
self._baseRootCanvas.sortingOrder = order
if self._baseUIHelper then
self._baseUIHelper:SetSortingOrder(order)
self:onSetUIOrder()
end
end
end
function BaseUI:getUIOrder()
return self._baseUIOrder
end
function BaseUI:getSiblingIndex()
return self._baseSiblingIndex
end
function BaseUI:getIsShowComplete()
return self._showComplete
end
function BaseUI:isFullScreen()
return true
end
function BaseUI:currencyParams()
return nil, false
end
function BaseUI:updateCurrencyBar()
UIManager:showCurrencyBar(self)
end
function BaseUI:swallowTouchBetweenUI()
return true
end
-- 加载需要显示在UI上的模型统一接口方便管理
function BaseUI:loadUIModelRoot(path, callback)
ModelManager:loadModelAsync(path, nil, function(roleShowModel)
if self:isClosed() then
roleShowModel:destroy()
return
end
self._baseShowModelList = self._baseShowModelList or {}
table.insert(self._baseShowModelList, roleShowModel)
callback(roleShowModel)
if self._baseVisible == false then
roleShowModel:setActive(false)
self:onUIModelActiveChanged(false)
end
end)
end
function BaseUI:bind(data, fieldName, bindFunc, immediately)
if self:isClosed() then
return
end
if not self._baseBindData then
self._baseBindData = {}
end
if not self._baseBindData[data] then
self._baseBindData[data] = {}
end
table.insert(self._baseBindData[data], fieldName)
data:bind(fieldName, self, bindFunc, immediately)
end
function BaseUI:unBind(data, fieldName)
if self._baseBindData and self._baseBindData[data] then
for i, field in ipairs(self._baseBindData[data]) do
if field == fieldName then
data:unBind(field, self)
table.remove(self._baseBindData[data], i)
break
end
end
end
end
function BaseUI:unBindAll()
if not self._baseBindData then
return
end
for data, fields in pairs(self._baseBindData) do
for _, field in ipairs(fields) do
data:unBind(field, self)
end
end
self._baseBindData = nil
end
function BaseUI:scheduleGlobal(func, inter)
if self:isClosed() then
return 0
end
local sid = SchedulerManager:scheduleGlobal(func, inter)
if self._schedulerIds == nil then
self._schedulerIds = {}
end
table.insert(self._schedulerIds, sid)
return sid
end
function BaseUI:performWithDelayGlobal(func, delay)
if self:isClosed() then
return 0
end
local sid = SchedulerManager:performWithDelayGlobal(func, delay)
if self._schedulerIds == nil then
self._schedulerIds = {}
end
table.insert(self._schedulerIds, sid)
return sid
end
function BaseUI:pauseScheduleGlobal(sid)
SchedulerManager:pause(sid)
end
function BaseUI:resumeScheduleGlobal(sid)
SchedulerManager:resume(sid)
end
function BaseUI:unscheduleGlobal(sid)
if self._schedulerIds == nil then
return
end
for k, v in ipairs(self._schedulerIds) do
if v == sid then
table.remove(self._schedulerIds, k)
break
end
end
SchedulerManager:unscheduleGlobal(sid)
end
function BaseUI:unloadPreloadList()
if self._preloadListFinished then
if self._preloadList then
for objType, list in pairs(self._preloadList) do
for _, path in ipairs(list) do
ResourceManager:unload(path)
end
end
self._preloadList = nil
end
end
end
function BaseUI:disableUITouch()
if not self._baseTouchDisabled and not self:isClosed() then
self._baseTouchDisabled = true
UIManager:disableTouch()
end
end
function BaseUI:enableUITouch()
if self._baseTouchDisabled then
self._baseTouchDisabled = false
UIManager:enableTouch()
end
end
-- 以下都是private function,不要在外部直接调用
function BaseUI:_onCreate()
self:_loadRoot()
self:onCreate()
end
-- 异步加载界面prefab文件每个界面只能有一个root prefab
function BaseUI:_loadRoot()
if self.root then
return
end
local totalCount = 0
local function finishCallback()
totalCount = totalCount - 1
if totalCount <= 0 then
self._preloadListFinished = true
if self:isClosed() then
self:unloadPreloadList()
self:_killAniTween()
return
end
UIManager:getUIPrefab(self:getPrefabPath(), function(root)
if self:isClosed() then
self:unloadPreloadList()
UIManager:putbackUIPrefab(root)
return
end
self._baseUIHelper = root:getComponent(BF_UI_HELPER)
self.root = root
self:_fitNotchScreen()
self:_onLoadRootComplete()
-- 个别特殊的界面可能会在init或者refresh的时候关闭自己
if self:isClosed() then
self:_onEnterAnimationComplete()
return
end
self:_playEnterAnimation()
if self._baseUICovered then
self:onCover()
end
end)
end
end
self._preloadList = self:getPreLoadList()
if self._preloadList then
for objType, list in pairs(self._preloadList) do
totalCount = totalCount + #list
end
for objType, list in pairs(self._preloadList) do
for _, path in ipairs(list) do
ResourceManager:loadOriginAssetAsync(path, objType, finishCallback)
end
end
else
totalCount = totalCount + 1
finishCallback()
end
end
function BaseUI:_recordParams(params)
self._baseUIParams = params
end
function BaseUI:_getRecordParams()
return self._baseUIParams
end
-- 显示或者隐藏界面,只是给UIManager用来隐藏被遮挡的界面减少渲染压力用的
function BaseUI:_setVisible(visible)
if self._baseVisible == visible then
return
end
self._baseVisible = visible
if self._baseRootCanvas then
local pos = visible and 0 or 10000000000
self.root:setLocalPosition(0, 0, pos)
end
if self._baseShowModelList then
for k, v in ipairs(self._baseShowModelList) do
v:setActive(visible)
end
self:onUIModelActiveChanged(visible)
end
self:onUIVisibleChanged()
end
function BaseUI:_getRootCanvas()
self._baseRootCanvas = self.root:getComponent(GConst.TYPEOF_UNITY_CLASS.CANVAS)
if not self._baseRootCanvas then
Logger.logTodo("ui root canvas is not add on prefab:%s", self.root:getAssetPath())
self._baseRootCanvas = self.root:addComponent(GConst.TYPEOF_UNITY_CLASS.CANVAS)
end
return self._baseRootCanvas
end
function BaseUI:_onLoadRootComplete()
self:_getRootCanvas()
self._baseRootCanvas.overrideSorting = true
if not self.root:getComponent(GConst.TYPEOF_UNITY_CLASS.GRAPHIC_RAYCASTER) then
Logger.logTodo("ui root GraphicRaycaster is not add on prefab:%s", self.root:getAssetPath())
self.root:addComponent(GConst.TYPEOF_UNITY_CLASS.GRAPHIC_RAYCASTER)
end
if self._baseVisible == false then
self.root:setLocalPosition(0, 0, 10000000000)
else
self.root:setLocalPosition(0, 0, 0)
end
if self._baseSiblingIndex then
self.root:getTransform():SetSiblingIndex(self._baseSiblingIndex)
self._baseRootCanvas.sortingOrder = self._baseUIOrder
if self._baseUIHelper then
self._baseUIHelper:SetSortingOrder(self._baseUIOrder)
end
end
UIManager:showCurrencyBar(self)
self:onLoadRootComplete()
self:onRefresh()
self._baseLoadComplete = true
UIManager:onUILoadedComplete(self)
if self._onLoadUICompleteCallback then
self._onLoadUICompleteCallback()
self._onLoadUICompleteCallback = nil
end
end
-- 适配异形屏
function BaseUI:_fitNotchScreen()
-- 只有绑定了该组件的UI 才需要适配
if self._baseUIHelper then
local notchScreenNodeCount = self._baseUIHelper:GetNotchScreenNodeCount()
if notchScreenNodeCount > 0 and not self._baseUIHelper:GetHasInitDefaultNotchScreenHeight() then
-- 获取偏移值
local height = SafeAreaManager:getNotchScreenHeight()
if height < 1 then -- 没有刘海
return
end
local index = 0
for i = 1, notchScreenNodeCount do
index = i - 1
local haveNode = self._baseUIHelper:GetIsHaveNotchScreenNodeGameObject(index)
local adjustHeight = self._baseUIHelper:GetNotchScreenNodeAdjustHeight(index)
if haveNode then
-- 获取anchor数据 根据情况处理位置
self._baseUIHelper:CacheAnchorMin(index)
local anchorMinX = self._baseUIHelper.PositionX
local anchorMinY = self._baseUIHelper.PositionY
self._baseUIHelper:CacheAnchorMax(index)
local anchorMaxX = self._baseUIHelper.PositionX
local anchorMaxY = self._baseUIHelper.PositionY
if math.abs(anchorMinX - 0.5) < 0.00001 and math.abs(anchorMinY) < 0.00001 and math.abs(anchorMaxX - 0.5) < 0.00001 and math.abs(anchorMaxY - 1) < 0.00001 then -- 上下填充,仅调整高度
self._baseUIHelper:CacheOffsetMax(index)
local offsetMaxX = self._baseUIHelper.PositionX
local offsetMaxY = self._baseUIHelper.PositionY
self._baseUIHelper:SetOffsetMax(index, offsetMaxX, offsetMaxY - height + adjustHeight)
elseif math.abs(anchorMinX) < 0.00001 and math.abs(anchorMinY) < 0.00001 and math.abs(anchorMaxX - 1) < 0.00001 and math.abs(anchorMaxY - 1) < 0.00001 then -- 整体填充,仅调整高度
self._baseUIHelper:CacheOffsetMax(index)
local offsetMaxX = self._baseUIHelper.PositionX
local offsetMaxY = self._baseUIHelper.PositionY
self._baseUIHelper:SetOffsetMax(index, offsetMaxX, offsetMaxY - height + adjustHeight)
else -- 直接下移
self._baseUIHelper:CacheAnchoredPosition(index)
local notchScreenNodeOriginPosX = self._baseUIHelper.PositionX
local notchScreenNodeOriginPosY = self._baseUIHelper.PositionY
self._baseUIHelper:SetAnchoredPosition(index, notchScreenNodeOriginPosX, notchScreenNodeOriginPosY - height + adjustHeight)
end
end
end
self._baseUIHelper:SetInit(true)
end
end
end
function BaseUI:_playEnterAnimation()
local aniType = UIManager:getAniType(self)
if aniType then
if aniType == UIManager.ANI_TYPE.POP then
self.root:setAnchoredPosition(0, 0)
self:_playPop()
else
UIManager:_updateUISwallowOrder()
self.root:setAnchoredPosition(0, 0)
self:_onEnterAnimationComplete()
end
else
self.root:setAnchoredPosition(0, 0)
self:_onEnterAnimationComplete()
end
end
function BaseUI:_playExitAnimation()
self:_onExitAnimationComplete()
end
function BaseUI:_onEnterAnimationComplete()
self._showComplete = true
if self:getUIType() == UIManager.UI_TYPE.MAIN then
UIManager:closeBehindUI(self)
end
if self:isFullScreen() then
UIManager:hideBehindUI(self)
end
if self._onEnterAniCompleteCallback then
self._onEnterAniCompleteCallback()
end
self:onEnterAnimationComplete()
-- 部分ui会有动画,所以在这里算完成
EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.UI_SHOW_COMPLETE, self:getUIIndex())
end
function BaseUI:_onExitAnimationComplete()
self:onExitAnimationComplete()
if self._onExitAniCompleteCallback then
self._onExitAniCompleteCallback()
end
self:_destroy()
UIManager:onUIClosedComplete(self)
end
function BaseUI:_playPop()
self.popSequence = self.root:createBindTweenSequence()
local scaleTween1 = self.root:getTransform():DOScale(1.05, 0.15)
self.popSequence:Append(scaleTween1)
local scaleTween2 = self.root:getTransform():DOScale(1, 0.2)
self.popSequence:Append(scaleTween2)
self.popSequence:OnComplete(function()
UIManager:_updateUISwallowOrder()
self:_onEnterAnimationComplete()
self.popSequence = nil
end)
end
function BaseUI:_killAniTween()
if self.popSequence then
self.popSequence:Kill(true)
self.popSequence = nil
end
end
function BaseUI:_onCover()
self._baseUICovered = true
if self.root then
self:onCover()
end
end
function BaseUI:_onReshow()
if self._baseVisible == false then
UIManager:updateBarsState(self)
if self:isFullScreen() then
UIManager:hideBehindUI(self)
else
UIManager:showBehindUI(self)
end
self:_setVisible(true)
end
self._baseUICovered = false
if self.root then
self:onReshow()
end
end
function BaseUI:_onClose()
EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.UI_CLOSE, self:getUIIndex())
self._baseAlreadyClosed = true
self:onClose()
self:_baseClear()
end
function BaseUI:_baseClear()
if self._baseEventListeners then
for key, tag in pairs(self._baseEventListeners) do
EventManager:removeEventListener(key, tag)
end
self._baseEventListeners = nil
end
self._baseEffectList = nil
if self._schedulerIds then
for k, v in ipairs(self._schedulerIds) do
SchedulerManager:unscheduleGlobal(v)
end
self._schedulerIds = nil
end
self:unBindAll()
self:enableUITouch()
end
function BaseUI:_destroy()
if self.root then
-- 干掉界面上的模型
if self._baseShowModelList then
for i = 1, #self._baseShowModelList do
table.remove(self._baseShowModelList):destroy()
end
end
UIManager:putbackUIPrefab(self.root)
self:_killAniTween()
self.root = nil
self._baseRootCanvas = nil
end
self:unloadPreloadList()
end
return BaseUI