优化UIService模块

优化UI模块更多细节容错处理 优化UI模块内存泄露问题 优化性能
This commit is contained in:
陈思海 2026-04-23 20:19:46 +08:00
parent ac15608019
commit cd5de2c374
17 changed files with 301 additions and 145 deletions

View File

@ -1,5 +1,4 @@
using System;
using System.Linq;
using AlicizaX.Resource.Runtime;
using AlicizaX;
using Cysharp.Threading.Tasks;

View File

@ -1,11 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.PlayerLoop;
namespace AlicizaX.UI.Runtime
{
@ -37,38 +33,36 @@ namespace AlicizaX.UI.Runtime
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Register(Type uiType, Type holderType, UILayer layer = UILayer.UI, bool fullScreen = false, int cacheTime = 0, bool needUpdate = false)
{
var holderHandle = holderType.TypeHandle;
var uiHandle = uiType.TypeHandle;
RuntimeTypeHandle holderHandle = holderType.TypeHandle;
RuntimeTypeHandle uiHandle = uiType.TypeHandle;
_typeHandleMap[uiHandle] = new UIMetaInfo(uiHandle, holderHandle, layer, fullScreen, cacheTime, needUpdate);
_stringHandleMap[uiType.Name] = uiHandle;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGet(RuntimeTypeHandle handle, out UIMetaInfo info)
{
if (_typeHandleMap.TryGetValue(handle, out info))
{
return true;
}
var t = Type.GetTypeFromHandle(handle);
if (TryReflectAndRegister(t, out info))
return true;
return false;
return TryReflectAndRegister(Type.GetTypeFromHandle(handle), out info);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGet(string typeName, out UIMetaInfo info)
{
if (_stringHandleMap.TryGetValue(typeName, out var handle))
if (_stringHandleMap.TryGetValue(typeName, out RuntimeTypeHandle handle))
{
return TryGet(handle, out info);
}
var type = AlicizaX.Utility.Assembly.GetType(typeName);
Type type = AlicizaX.Utility.Assembly.GetType(typeName);
if (type != null && TryReflectAndRegister(type, out info))
{
return true;
}
info = default;
return false;
@ -77,7 +71,13 @@ namespace AlicizaX.UI.Runtime
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool TryReflectAndRegister(Type uiType, out UIMetaInfo info)
{
Log.Warning($"[UI] UI未注册[{uiType.FullName}] 反射进行缓存");
if (uiType == null)
{
info = default;
return false;
}
Log.Warning($"[UI] UI not pre-registered: {uiType.FullName}, using reflection fallback.");
return TryReflectAndRegisterInternal(uiType, out info);
}
@ -86,14 +86,11 @@ namespace AlicizaX.UI.Runtime
{
try
{
Type baseType = uiType;
#pragma warning disable CS8632
Type? holderType = null;
var genericArgs = baseType.GetGenericArguments();
if (genericArgs.Length > 0)
Type holderType = ResolveHolderType(uiType);
if (holderType == null)
{
holderType = genericArgs[0];
info = default;
return false;
}
UILayer layer = UILayer.UI;
@ -101,28 +98,39 @@ namespace AlicizaX.UI.Runtime
int cacheTime = 0;
bool needUpdate = false;
var windowAttribute = CustomAttributeData.GetCustomAttributes(uiType)
.FirstOrDefault(a => a.AttributeType.Name == nameof(WindowAttribute));
var uiUpdateAttribute = CustomAttributeData.GetCustomAttributes(uiType)
.FirstOrDefault(a => a.AttributeType.Name == nameof(UIUpdateAttribute));
if (windowAttribute != null)
IList<CustomAttributeData> attributes = CustomAttributeData.GetCustomAttributes(uiType);
for (int i = 0; i < attributes.Count; i++)
{
var args = windowAttribute.ConstructorArguments;
if (args.Count > 0) layer = (UILayer)(args[0].Value ?? UILayer.UI);
if (args.Count > 1) fullScreen = (bool)(args[1].Value ?? false);
if (args.Count > 2) cacheTime = (int)(args[2].Value ?? 0);
CustomAttributeData attribute = attributes[i];
string attributeName = attribute.AttributeType.Name;
if (attributeName == nameof(WindowAttribute))
{
IList<CustomAttributeTypedArgument> args = attribute.ConstructorArguments;
if (args.Count > 0)
{
layer = (UILayer)(args[0].Value ?? UILayer.UI);
}
needUpdate = uiUpdateAttribute != null;
if (holderType != null)
if (args.Count > 1)
{
fullScreen = (bool)(args[1].Value ?? false);
}
if (args.Count > 2)
{
cacheTime = (int)(args[2].Value ?? 0);
}
}
else if (attributeName == nameof(UIUpdateAttribute))
{
needUpdate = true;
}
}
Register(uiType, holderType, layer, fullScreen, cacheTime, needUpdate);
info = _typeHandleMap[uiType.TypeHandle];
return true;
}
}
catch (Exception ex)
{
Log.Error($"[UI] Failed to register UI type {uiType.FullName}: {ex.Message}");
@ -131,5 +139,25 @@ namespace AlicizaX.UI.Runtime
info = default;
return false;
}
private static Type ResolveHolderType(Type uiType)
{
Type current = uiType;
while (current != null && current != typeof(object))
{
if (current.IsGenericType)
{
Type[] genericArgs = current.GetGenericArguments();
if (genericArgs.Length == 1 && typeof(UIHolderObjectBase).IsAssignableFrom(genericArgs[0]))
{
return genericArgs[0];
}
}
current = current.BaseType;
}
return null;
}
}
}

View File

@ -35,6 +35,15 @@ namespace AlicizaX.UI.Runtime
return;
View = (UIBase)InstanceFactory.CreateInstanceOptimized(UILogicType);
EnsureCancellationToken();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnsureCancellationToken()
{
if (_cancellationTokenSource == null)
{
_cancellationTokenSource = new CancellationTokenSource();
}
}
@ -51,7 +60,7 @@ namespace AlicizaX.UI.Runtime
DisposeAsync().Forget();
}
private async UniTask DisposeAsync()
internal async UniTask DisposeAsync()
{
CancelAsyncOperations();
@ -64,11 +73,22 @@ namespace AlicizaX.UI.Runtime
public UIMetadata(Type uiType)
{
if (uiType == null)
{
throw new ArgumentNullException(nameof(uiType));
}
UILogicType = uiType;
UIMetaRegistry.TryGet(UILogicType.TypeHandle, out MetaInfo);
if (!UIMetaRegistry.TryGet(UILogicType.TypeHandle, out MetaInfo))
{
throw new InvalidOperationException($"[UI] Metadata not registered for {UILogicType.FullName}");
}
UIResRegistry.TryGet(MetaInfo.HolderRuntimeTypeHandle, out ResInfo);
if (!UIResRegistry.TryGet(MetaInfo.HolderRuntimeTypeHandle, out ResInfo))
{
throw new InvalidOperationException($"[UI] Resource metadata not registered for holder of {UILogicType.FullName}");
}
}
}
}

View File

@ -27,5 +27,16 @@ namespace AlicizaX.UI.Runtime
{
}
}
protected internal override void OnUnspawn()
{
base.OnUnspawn();
UIMetadata metadata = (UIMetadata)Target;
if (metadata != null)
{
metadata.CancelAsyncOperations();
}
}
}
}

View File

@ -1,19 +1,12 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
public static class UIResRegistry
{
private static readonly Dictionary<RuntimeTypeHandle, UIResInfo> _typeHandleMap = new();
public readonly struct UIResInfo
{
public readonly string Location;
@ -26,29 +19,35 @@ namespace AlicizaX.UI.Runtime
LoadType = loadType;
}
}
private static readonly Dictionary<RuntimeTypeHandle, UIResInfo> _typeHandleMap = new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Register(Type holderType, string location, EUIResLoadType loadType)
{
var handle = holderType.TypeHandle;
RuntimeTypeHandle handle = holderType.TypeHandle;
_typeHandleMap[handle] = new UIResInfo(location, loadType);
}
public static bool TryGet(RuntimeTypeHandle handle, out UIResInfo info)
{
if (_typeHandleMap.TryGetValue(handle, out info))
{
return true;
}
var t = Type.GetTypeFromHandle(handle);
if (TryReflectAndRegister(t, out info))
return true;
return false;
return TryReflectAndRegister(Type.GetTypeFromHandle(handle), out info);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool TryReflectAndRegister(Type holderType, out UIResInfo info)
{
if (holderType == null)
{
info = default;
return false;
}
return TryReflectAndRegisterInternal(holderType, out info);
}
@ -57,17 +56,21 @@ namespace AlicizaX.UI.Runtime
{
try
{
var cad = CustomAttributeData.GetCustomAttributes(holderType)
.FirstOrDefault(a => a.AttributeType.Name == nameof(UIResAttribute));
string resLocation = string.Empty;
EUIResLoadType resLoadType = EUIResLoadType.AssetBundle;
if (cad != null)
IList<CustomAttributeData> attributes = CustomAttributeData.GetCustomAttributes(holderType);
for (int i = 0; i < attributes.Count; i++)
{
var args = cad.ConstructorArguments;
if (args.Count > 0) resLocation = (string)(args[0].Value ?? string.Empty);
if (args.Count > 1) resLoadType = (EUIResLoadType)(args[1].Value ?? EUIResLoadType.AssetBundle);
CustomAttributeData attribute = attributes[i];
if (attribute.AttributeType.Name != nameof(UIResAttribute))
{
continue;
}
IList<CustomAttributeTypedArgument> args = attribute.ConstructorArguments;
string resLocation = args.Count > 0 ? (string)(args[0].Value ?? string.Empty) : string.Empty;
EUIResLoadType resLoadType = args.Count > 1
? (EUIResLoadType)(args[1].Value ?? EUIResLoadType.AssetBundle)
: EUIResLoadType.AssetBundle;
Register(holderType, resLocation, resLoadType);
info = _typeHandleMap[holderType.TypeHandle];
return true;

View File

@ -37,7 +37,10 @@ namespace AlicizaX
public void Clear()
{
for (int i = _eventHandles.Count - 1; i >= 0; i--)
{
_eventHandles[i].Dispose();
}
_eventHandles.Clear();
}
}

View File

@ -27,13 +27,14 @@ namespace AlicizaX.UI.Runtime
/// <param name="timeDuration">倒计时/s</param>
public void SetUIBlock(float timeDuration)
{
ITimerService timerService = GetTimerService();
if (m_LastCountDownGuid != 0)
{
_timerService.RemoveTimer(m_LastCountDownGuid);
timerService.RemoveTimer(m_LastCountDownGuid);
}
SetLayerBlockOption(true);
m_LastCountDownGuid = _timerService.AddTimer(OnBlockCountDown, timeDuration);
m_LastCountDownGuid = timerService.AddTimer(OnBlockCountDown, timeDuration);
}
/// <summary>
@ -41,9 +42,10 @@ namespace AlicizaX.UI.Runtime
/// </summary>
public void ForceExitBlock()
{
ITimerService timerService = GetTimerService();
if (m_LastCountDownGuid != 0)
{
_timerService.RemoveTimer(m_LastCountDownGuid);
timerService.RemoveTimer(m_LastCountDownGuid);
}
RecoverLayerOptionAll();

View File

@ -40,7 +40,8 @@ namespace AlicizaX.UI.Runtime
uiMetadata.View.Holder.transform.SetParent(UICacheLayer);
if (uiMetadata.MetaInfo.CacheTime > 0)
{
timerId = _timerService.AddTimer(
ITimerService timerService = GetTimerService();
timerId = timerService.AddTimer(
OnTimerDisposeWindow,
uiMetadata,
uiMetadata.MetaInfo.CacheTime,
@ -62,8 +63,8 @@ namespace AlicizaX.UI.Runtime
{
if (meta != null)
{
meta.Dispose();
RemoveFromCache(meta.MetaInfo.RuntimeTypeHandle);
meta.Dispose();
}
}
@ -73,11 +74,22 @@ namespace AlicizaX.UI.Runtime
{
m_CacheWindow.Remove(typeHandle);
entry.Metadata.InCache = false;
if (entry.TimerId > 0)
if (entry.TimerId > 0 && _timerService != null)
{
_timerService.RemoveTimer(entry.TimerId);
}
}
}
private ITimerService GetTimerService()
{
if (_timerService != null)
{
return _timerService;
}
_timerService = AppServices.Require<ITimerService>();
return _timerService;
}
}
}

View File

@ -28,6 +28,7 @@ namespace AlicizaX.UI.Runtime
private async UniTask<UIBase> ShowUIImplAsync(UIMetadata metaInfo, params object[] userDatas)
{
CreateMetaUI(metaInfo);
EnsureMetaCanOpen(metaInfo);
await UIHolderFactory.CreateUIResourceAsync(metaInfo, UICacheLayer);
if (metaInfo.View == null || metaInfo.State == UIState.Uninitialized || metaInfo.State == UIState.Destroyed)
{
@ -42,6 +43,7 @@ namespace AlicizaX.UI.Runtime
private UIBase ShowUIImplSync(UIMetadata metaInfo, params object[] userDatas)
{
CreateMetaUI(metaInfo);
EnsureMetaCanOpen(metaInfo);
UIHolderFactory.CreateUIResourceSync(metaInfo, UICacheLayer);
if (metaInfo.View == null || metaInfo.State == UIState.Uninitialized || metaInfo.State == UIState.Destroyed)
{
@ -63,7 +65,7 @@ namespace AlicizaX.UI.Runtime
if (meta.State == UIState.CreatedUI)
{
meta.CancelAsyncOperations();
meta.Dispose();
await meta.DisposeAsync();
return;
}
@ -79,6 +81,7 @@ namespace AlicizaX.UI.Runtime
}
meta.CancelAsyncOperations();
meta.EnsureCancellationToken();
await meta.View.InternalClose();
if (meta.State != UIState.Closed)
{
@ -103,6 +106,15 @@ namespace AlicizaX.UI.Runtime
if (meta.State == UIState.Uninitialized) meta.CreateUI();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void EnsureMetaCanOpen(UIMetadata meta)
{
if (meta.State == UIState.Closed)
{
meta.EnsureCancellationToken();
}
}
private void FinalizeShow(UIMetadata meta, object[] userDatas)
{

View File

@ -17,6 +17,7 @@ namespace AlicizaX.UI.Runtime
protected override void OnDestroyService()
{
DestroyAllManagedUI().Forget();
}
void IServiceTickable.Tick(float deltaTime)
@ -99,5 +100,81 @@ namespace AlicizaX.UI.Runtime
{
_timerService = timerService;
}
private async UniTask DestroyAllManagedUI()
{
for (int layerIndex = 0; layerIndex < _openUI.Length; layerIndex++)
{
LayerData layer = _openUI[layerIndex];
if (layer == null)
{
continue;
}
int count = layer.OrderList.Count;
for (int i = count - 1; i >= 0; i--)
{
UIMetadata meta = layer.OrderList[i];
if (meta == null)
{
continue;
}
meta.CancelAsyncOperations();
await meta.DisposeAsync();
}
layer.OrderList.Clear();
layer.IndexMap.Clear();
layer.LastFullscreenIndex = -1;
}
if (m_CacheWindow.Count > 0)
{
RuntimeTypeHandle[] handles = new RuntimeTypeHandle[m_CacheWindow.Count];
int writeIndex = 0;
foreach (var pair in m_CacheWindow)
{
handles[writeIndex++] = pair.Key;
}
for (int i = 0; i < writeIndex; i++)
{
if (!m_CacheWindow.TryGetValue(handles[i], out CacheEntry entry))
{
continue;
}
if (entry.TimerId > 0 && _timerService != null)
{
_timerService.RemoveTimer(entry.TimerId);
}
entry.Metadata.InCache = false;
entry.Metadata.CancelAsyncOperations();
await entry.Metadata.DisposeAsync();
}
m_CacheWindow.Clear();
}
if (m_LastCountDownGuid != 0 && _timerService != null)
{
_timerService.RemoveTimer(m_LastCountDownGuid);
m_LastCountDownGuid = 0;
}
if (m_LayerBlock != null)
{
UnityEngine.Object.Destroy(m_LayerBlock);
m_LayerBlock = null;
}
UICacheLayer = null;
UICanvasRoot = null;
UICanvas = null;
UICamera = null;
UIRoot = null;
}
}
}

View File

@ -5,8 +5,6 @@ namespace AlicizaX.UI.Runtime
{
public interface IUITransitionPlayer
{
int Priority { get; }
UniTask PlayOpenAsync(CancellationToken cancellationToken = default);
UniTask PlayCloseAsync(CancellationToken cancellationToken = default);

View File

@ -8,7 +8,6 @@ namespace AlicizaX.UI.Runtime
[DisallowMultipleComponent]
public sealed class UIAnimationFlowTransition : AnimationFlow.Runtime.AnimationFlow, IUITransitionPlayer
{
public int Priority => 0;
[SerializeField] private string openClip = "Open";
[SerializeField] private string closeClip = "Close";

View File

@ -38,8 +38,6 @@ namespace AlicizaX.UI.Runtime
public float Alpha;
}
public int Priority => 100;
[SerializeField] private UITransitionPreset openPreset = UITransitionPreset.FadeScale;
[SerializeField] private UITransitionPreset closePreset = UITransitionPreset.FadeScale;
[SerializeField] private UITransitionEase openEase = UITransitionEase.OutCubic;

View File

@ -5,7 +5,6 @@ using System.Threading;
using AlicizaX;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Pool;
namespace AlicizaX.UI.Runtime
{
@ -39,8 +38,17 @@ namespace AlicizaX.UI.Runtime
for (int j = 0; j < i; j++)
{
if (temp[j].View.Visible) await temp[j].View.InternalClose();
temp[j].Dispose();
UIMetadata metadata = temp[j];
if (metadata.View.Visible)
{
metadata.CancelAsyncOperations();
metadata.EnsureCancellationToken();
await metadata.View.InternalClose(metadata.CancellationToken);
}
await metadata.DisposeAsync();
UIMetadataFactory.ReturnToPool(metadata);
temp[j] = null;
}
}
finally
@ -54,9 +62,9 @@ namespace AlicizaX.UI.Runtime
private void ChildVisible(bool value)
{
foreach (var kvp in _children)
foreach (KeyValuePair<UIBase, UIMetadata> kvp in _children)
{
var view = kvp.Value.View;
UIBase view = kvp.Value.View;
if (view.State == UIState.Opened)
{
view.Visible = value;
@ -142,7 +150,13 @@ namespace AlicizaX.UI.Runtime
private async UniTask ProcessWidget(UIMetadata meta, bool visible, CancellationToken cancellationToken = default)
{
if (!AddWidget(meta)) return;
if (!AddWidget(meta))
{
await meta.DisposeAsync();
UIMetadataFactory.ReturnToPool(meta);
return;
}
await meta.View.InternalInitlized(cancellationToken);
meta.View.Visible = visible;
if (meta.View.Visible)
@ -156,8 +170,6 @@ namespace AlicizaX.UI.Runtime
if (!_children.TryAdd(meta.View, meta))
{
Log.Warning("Already has widget:{0}", meta.View);
meta.Dispose();
UIMetadataFactory.ReturnToPool(meta);
return false;
}
@ -174,16 +186,33 @@ namespace AlicizaX.UI.Runtime
if (_children.Remove(widget, out var meta))
{
meta.CancelAsyncOperations();
await widget.InternalClose();
meta.EnsureCancellationToken();
await widget.InternalClose(meta.CancellationToken);
if (meta.MetaInfo.NeedUpdate)
{
_updateableChildren.Remove(meta);
RemoveUpdateableChild(meta);
}
meta.Dispose();
await meta.DisposeAsync();
UIMetadataFactory.ReturnToPool(meta);
}
}
private void RemoveUpdateableChild(UIMetadata meta)
{
for (int i = 0; i < _updateableChildren.Count; i++)
{
if (_updateableChildren[i] != meta)
{
continue;
}
int lastIndex = _updateableChildren.Count - 1;
_updateableChildren[i] = _updateableChildren[lastIndex];
_updateableChildren.RemoveAt(lastIndex);
return;
}
}
}
}

View File

@ -16,7 +16,6 @@ namespace AlicizaX.UI.Runtime
_state = UIState.CreatedUI;
}
~UIBase() => Dispose(false);
private bool _disposed;
internal Canvas _canvas;
@ -105,7 +104,6 @@ namespace AlicizaX.UI.Runtime
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
@ -190,7 +188,7 @@ namespace AlicizaX.UI.Runtime
private void ReleaseEventListenerProxy()
{
if (!_eventListenerProxy.IsNull())
if (_eventListenerProxy != null)
{
MemoryPool.Release(_eventListenerProxy);
_eventListenerProxy = null;

View File

@ -1,4 +1,5 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
@ -6,7 +7,7 @@ using UnityEngine;
namespace AlicizaX.UI.Runtime
{
[DisallowMultipleComponent]
public abstract class UIHolderObjectBase : UnityEngine.MonoBehaviour
public abstract class UIHolderObjectBase : MonoBehaviour
{
public Action OnWindowInitEvent;
public Action OnWindowBeforeShowEvent;
@ -17,30 +18,17 @@ namespace AlicizaX.UI.Runtime
private GameObject _target;
private IUITransitionPlayer _transitionPlayer;
/// <summary>
/// UI实例资源对象。
/// </summary>
public GameObject Target => _target ??= gameObject;
private RectTransform _rectTransform;
public RectTransform RectTransform => _rectTransform ??= Target.transform as RectTransform;
/// <summary>
/// 窗口矩阵位置组件。
/// </summary>
public RectTransform RectTransform => _rectTransform ??= _target.transform as RectTransform;
/// <summary>
/// 可见性
/// </summary>
public bool Visible
{
get => Target.activeSelf;
internal set { _target.SetActive(value); }
internal set => Target.SetActive(value);
}
public virtual void Awake()
{
_target = gameObject;
@ -55,21 +43,21 @@ namespace AlicizaX.UI.Runtime
internal UniTask PlayOpenTransitionAsync(CancellationToken cancellationToken = default)
{
return TryGetTransitionPlayer(out var transitionPlayer)
return TryGetTransitionPlayer(out IUITransitionPlayer transitionPlayer)
? transitionPlayer.PlayOpenAsync(cancellationToken)
: UniTask.CompletedTask;
}
internal UniTask PlayCloseTransitionAsync(CancellationToken cancellationToken = default)
{
return TryGetTransitionPlayer(out var transitionPlayer)
return TryGetTransitionPlayer(out IUITransitionPlayer transitionPlayer)
? transitionPlayer.PlayCloseAsync(cancellationToken)
: UniTask.CompletedTask;
}
internal void StopTransition()
{
if (TryGetTransitionPlayer(out var transitionPlayer))
if (TryGetTransitionPlayer(out IUITransitionPlayer transitionPlayer))
{
transitionPlayer.Stop();
}
@ -83,24 +71,7 @@ namespace AlicizaX.UI.Runtime
return true;
}
_transitionPlayer = null;
int bestPriority = int.MinValue;
MonoBehaviour[] behaviours = GetComponents<MonoBehaviour>();
for (int i = 0; i < behaviours.Length; i++)
{
MonoBehaviour behaviour = behaviours[i];
if (behaviours.IsNull() || !behaviour.isActiveAndEnabled || behaviour is not IUITransitionPlayer player)
{
continue;
}
if (player.Priority > bestPriority)
{
_transitionPlayer = player;
bestPriority = player.Priority;
}
}
_transitionPlayer = GetComponent<IUITransitionPlayer>();
transitionPlayer = _transitionPlayer;
return transitionPlayer != null;
}

View File

@ -58,8 +58,7 @@ namespace AlicizaX.UI.Runtime
// 初始化方法(泛型版本)
protected void InitTabVirtuallyView<TTab>(Transform parent = null) where TTab : UIWidget
{
var metadata = UIMetadataFactory.GetWindowMetadata<TTab>();
CacheTabMetadata(metadata, parent);
CacheTabMetadata(typeof(TTab).TypeHandle, parent);
}
// 初始化方法(类型名版本)
@ -67,15 +66,12 @@ namespace AlicizaX.UI.Runtime
{
if (UIMetaRegistry.TryGet(typeName, out var metaRegistry))
{
var metadata = UIMetadataFactory.GetWindowMetadata(metaRegistry.RuntimeTypeHandle);
CacheTabMetadata(metadata, parent);
CacheTabMetadata(metaRegistry.RuntimeTypeHandle, parent);
}
}
private void CacheTabMetadata(UIMetadata metadata, Transform parent)
private void CacheTabMetadata(RuntimeTypeHandle typeHandle, Transform parent)
{
var typeHandle = metadata.MetaInfo.RuntimeTypeHandle;
if (!_tabCache.ContainsKey(typeHandle))
{
_typeOrder.Add(typeHandle);
@ -105,10 +101,10 @@ namespace AlicizaX.UI.Runtime
try
{
var metadata = UIMetadataFactory.GetWindowMetadata(typeHandle);
var parent = _tabCache[typeHandle];
UIMetadata metadata = UIMetadataFactory.GetWidgetMetadata(typeHandle);
Transform parent = _tabCache[typeHandle];
var widget = await CreateWidgetUIAsync(metadata, parent, false);
UIBase widget = await CreateWidgetUIAsync(metadata, parent, false);
if (widget is not UIWidget tabWidget) return;
_loadedTabs[typeHandle] = tabWidget;