Files
XericUIActionVessel/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs
T
2026-06-21 19:32:37 +08:00

628 lines
19 KiB
C#
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.
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.Pool;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// UI 字段标记提供者接口
/// 所有 Tag 增强的 UI 组件需实现此接口,便于 Entry 在绑定阶段自动匹配
/// </summary>
public interface IXuiavrgFieldTagProvider
{
XuiavrgFieldTag EntryTag { get; }
int TagIndex { get; }
}
/// <summary>
/// 字段 Entry 接口
/// 数据对象字段与 UI 组件之间的中间缓存层
/// </summary>
public interface IFieldEntry
{
/// <summary>Entry 名称(字段名或编组路径)</summary>
string EntryName { get; }
/// <summary>Entry 完整路径(含父级路径)</summary>
string EntryPath { get; }
/// <summary>主要功能标记</summary>
XuiavrgFieldTag Tag { get; }
/// <summary>标记索引</summary>
int TagIndex { get; }
/// <summary>排序值(越小越靠前)</summary>
int FieldOrder { get; set; }
/// <summary>是否为编组 Entry(包含子 Entry</summary>
bool IsGroupEntry { get; }
/// <summary>父 Entry</summary>
IFieldEntry Parent { get; }
/// <summary>子 Entry 列表(只读)</summary>
IReadOnlyList<IFieldEntry> Children { get; }
/// <summary>生成的 UI 实例引用</summary>
GameObject UIInstance { get; }
/// <summary>绑定的预制体组</summary>
XuiavrgPropertyGroupScriptableObject BoundPrefabGroup { get; set; }
/// <summary>覆盖的预制体组名称</summary>
string PrefabOverrideName { get; set; }
// === 生命周期 ===
/// <summary>Entry 创建时调用</summary>
void OnEntryCreate();
/// <summary>绑定 UI 和数据时调用</summary>
void OnEntryBind();
/// <summary>释放时调用(归还池前)</summary>
void OnEntryRelease();
/// <summary>销毁时调用</summary>
void OnEntryDestroy();
// === 数据访问 ===
/// <summary>获取指定成员的数据值</summary>
object GetDataValue(string memberName);
/// <summary>设置指定成员的数据值</summary>
void SetDataValue(string memberName, object value);
/// <summary>获取 Entry 下所有绑定成员的名称</summary>
IEnumerable<string> GetMemberNames();
// === UI 绑定 ===
/// <summary>
/// 绑定 UI 组件
/// </summary>
/// <param name="uiInstance">UI 预制体实例</param>
/// <param name="componentCache">
/// 组件缓存列表。传入 null 时会自动收集 uiInstance 上所有 Component。
/// 父 Entry 可将已收集的缓存传递给子 Entry 以提升性能
/// </param>
void BindUIComponent(GameObject uiInstance, List<Component> componentCache);
}
/// <summary>
/// 字段临时 Entry 实现类
/// 作为数据对象字段和 UI 组件之间的中间缓存层
///
/// 支持:
/// - 多字段合并为一个 Entry(通过 XuiavrgFieldEntryGroupAttribute
/// - 嵌套 Entry(父 Entry 包含子 Entry
/// - 对象池复用(通过静态 ObjectPool)
/// - MVVM 双向绑定的生命周期
/// </summary>
public class XuiFieldTempEntry : IFieldEntry
{
#region 对象池
internal static ObjectPool<XuiFieldTempEntry> Pool { get; private set; }
static XuiFieldTempEntry()
{
Pool = new ObjectPool<XuiFieldTempEntry>(
createFunc: () => new XuiFieldTempEntry(),
actionOnGet: entry => entry.OnEntryCreate(),
actionOnRelease: entry => entry.OnEntryRelease(),
actionOnDestroy: entry => entry.OnEntryDestroy(),
collectionCheck: false,
defaultCapacity: 32,
maxSize: 512
);
}
/// <summary>从池中获取 Entry 实例</summary>
public static XuiFieldTempEntry Get() => Pool.Get();
/// <summary>将 Entry 归还池中</summary>
public static void Release(XuiFieldTempEntry entry) => Pool.Release(entry);
#endregion
#region 内部绑定数据结构
/// <summary>
/// 成员反射绑定信息
/// </summary>
internal class MemberBinding
{
/// <summary>绑定的数据目标对象</summary>
public object valueTarget;
/// <summary>是否为属性(false 为字段)</summary>
public bool isProperty;
/// <summary>属性信息</summary>
public PropertyInfo propertyInfo;
/// <summary>字段信息</summary>
public FieldInfo fieldInfo;
/// <summary>功能标记</summary>
public XuiavrgFieldTag tag;
/// <summary>标记索引</summary>
public int tagIndex;
/// <summary>
/// 从绑定目标获取值
/// </summary>
public object GetValue()
{
if (valueTarget == null) return null;
return isProperty
? propertyInfo.GetValue(valueTarget)
: fieldInfo.GetValue(valueTarget);
}
/// <summary>
/// 设置值到绑定目标
/// </summary>
public void SetValue(object value)
{
if (valueTarget == null) return;
if (isProperty)
{
if (propertyInfo.CanWrite)
propertyInfo.SetValue(valueTarget, value);
}
else
{
fieldInfo.SetValue(valueTarget, value);
}
}
}
#endregion
#region 字段
private string _entryName;
private string _entryPath;
private IFieldEntry _parent;
private List<XuiFieldTempEntry> _children = new List<XuiFieldTempEntry>();
private Dictionary<string, MemberBinding> _bindings = new Dictionary<string, MemberBinding>();
private GameObject _uiInstance;
private XuiavrgPropertyGroupScriptableObject _boundPrefabGroup;
private string _prefabOverrideName;
private int _fieldOrder;
#endregion
#region IFieldEntry 属性实现
public string EntryName => _entryName;
public string EntryPath
{
get
{
if (!string.IsNullOrEmpty(_entryPath)) return _entryPath;
if (_parent != null)
{
string parentPath = _parent.EntryPath;
return string.IsNullOrEmpty(parentPath)
? _entryName
: parentPath + "/" + _entryName;
}
return _entryName;
}
}
public XuiavrgFieldTag Tag { get; private set; }
public int TagIndex { get; private set; }
public int FieldOrder
{
get => _fieldOrder;
set => _fieldOrder = value;
}
public bool IsGroupEntry => _children.Count > 0;
public IFieldEntry Parent => _parent;
public IReadOnlyList<IFieldEntry> Children => _children;
public GameObject UIInstance => _uiInstance;
public XuiavrgPropertyGroupScriptableObject BoundPrefabGroup
{
get => _boundPrefabGroup;
set => _boundPrefabGroup = value;
}
public string PrefabOverrideName
{
get => _prefabOverrideName;
set => _prefabOverrideName = value;
}
#endregion
#region 初始化方法
/// <summary>
/// 设置 Entry 基本属性
/// </summary>
public void Initialize(string entryName, XuiavrgFieldTag tag = XuiavrgFieldTag.None, int tagIndex = 0)
{
_entryName = entryName;
_entryPath = null;
Tag = tag;
TagIndex = tagIndex;
_fieldOrder = 0;
}
/// <summary>
/// 设置父 Entry(用于嵌套)
/// </summary>
public void SetParent(IFieldEntry parent)
{
_parent = parent;
_entryPath = null; // 重置路径,下次访问重新计算
}
/// <summary>
/// 添加子 Entry
/// </summary>
public void AddChild(XuiFieldTempEntry child)
{
if (child == null) return;
child.SetParent(this);
_children.Add(child);
}
/// <summary>
/// 注册成员绑定
/// </summary>
/// <param name="memberName">成员名称</param>
/// <param name="memberInfo">反射信息</param>
/// <param name="target">数据目标对象</param>
/// <param name="tag">功能标记</param>
/// <param name="tagIndex">标记索引</param>
public void RegisterBinding(string memberName, MemberInfo memberInfo, object target,
XuiavrgFieldTag tag = XuiavrgFieldTag.None, int tagIndex = 0)
{
MemberBinding binding = new MemberBinding
{
valueTarget = target,
tag = tag,
tagIndex = tagIndex
};
switch (memberInfo)
{
case PropertyInfo propInfo:
binding.isProperty = true;
binding.propertyInfo = propInfo;
break;
case FieldInfo fieldInfo:
binding.isProperty = false;
binding.fieldInfo = fieldInfo;
break;
}
_bindings[memberName] = binding;
// 如果是第一个注册的绑定,设置主 Tag
if (Tag == XuiavrgFieldTag.None && tag != XuiavrgFieldTag.None)
{
Tag = tag;
TagIndex = tagIndex;
}
}
#endregion
#region 数据访问
public object GetDataValue(string memberName)
{
if (_bindings.TryGetValue(memberName, out var binding))
return binding.GetValue();
// 查找子 Entry
foreach (var child in _children)
{
var value = child.GetDataValue(memberName);
if (value != null) return value;
}
return null;
}
public void SetDataValue(string memberName, object value)
{
if (_bindings.TryGetValue(memberName, out var binding))
{
binding.SetValue(value);
return;
}
// 查找子 Entry
foreach (var child in _children)
{
if (child.HasBinding(memberName))
{
child.SetDataValue(memberName, value);
return;
}
}
}
public IEnumerable<string> GetMemberNames() => _bindings.Keys;
/// <summary>
/// 检查是否包含指定成员绑定
/// </summary>
public bool HasBinding(string memberName) => _bindings.ContainsKey(memberName);
/// <summary>
/// 获取指定成员的标记
/// </summary>
public XuiavrgFieldTag GetMemberTag(string memberName)
{
if (_bindings.TryGetValue(memberName, out var binding))
return binding.tag;
return XuiavrgFieldTag.None;
}
#endregion
#region UI 绑定
public void BindUIComponent(GameObject uiInstance, List<Component> componentCache)
{
_uiInstance = uiInstance;
if (_uiInstance == null) return;
if (componentCache == null)
{
componentCache = new List<Component>();
_uiInstance.GetComponentsInChildren(true, componentCache);
}
BindUIComponentWithCache(_uiInstance, componentCache);
// 子 Entry 复用组件缓存
foreach (var child in _children)
{
child.BindUIComponent(_uiInstance, componentCache);
}
}
private void BindUIComponentWithCache(GameObject uiInstance, List<Component> componentCache)
{
// 遍历组件缓存,匹配 tag
var locators = new List<XuiavrgLocator>();
foreach (var comp in componentCache)
{
if (comp is XuiavrgLocator locator)
{
locators.Add(locator);
}
if (comp is IXuiavrgFieldTagProvider tagProvider)
{
// 匹配 Tag 和 TagIndex
if (tagProvider.EntryTag == this.Tag &&
(this.TagIndex == 0 || tagProvider.TagIndex == this.TagIndex))
{
// 找到匹配的 UI 组件,建立绑定
BuildComponentBinding(comp);
}
}
}
// 如果没有 IXuiavrgFieldTagProvider,尝试通过 Locator 匹配
if (locators.Count > 0)
{
BuildLocatorBindings(locators);
}
}
/// <summary>
/// 通过标签提供者建立组件绑定
/// </summary>
private void BuildComponentBinding(Component comp)
{
// 根据组件类型和标记类型进行数据同步
switch (comp)
{
case UnityEngine.UI.Text uiText when Tag == XuiavrgFieldTag.Label || Tag == XuiavrgFieldTag.Output:
uiText.text = GetFirstBindingValue()?.ToString() ?? "";
break;
case UnityEngine.UI.Button _ when Tag == XuiavrgFieldTag.Button:
// 按钮绑定在 BindUIEvents 中处理
break;
case UnityEngine.UI.Toggle uiToggle when Tag == XuiavrgFieldTag.Toggle:
if (GetFirstBindingValue() is bool boolVal)
uiToggle.isOn = boolVal;
break;
case UnityEngine.UI.Slider uiSlider when Tag == XuiavrgFieldTag.Slider:
if (GetFirstBindingValue() is float floatVal)
uiSlider.value = floatVal;
break;
case UnityEngine.UI.InputField uiInput when Tag == XuiavrgFieldTag.Input:
uiInput.text = GetFirstBindingValue()?.ToString() ?? "";
break;
case UnityEngine.UI.Dropdown uiDropdown when Tag == XuiavrgFieldTag.Dropdown:
if (GetFirstBindingValue() is int intVal)
uiDropdown.value = intVal;
break;
}
}
/// <summary>
/// 通过 Locator 建立绑定
/// </summary>
private void BuildLocatorBindings(List<XuiavrgLocator> locators)
{
foreach (var locator in locators)
{
foreach (var locProp in locator.Properties)
{
// 按 propertyName 匹配成员名
if (_bindings.TryGetValue(locProp.propertyName, out var binding))
{
// 从数据端同步到 UI
var dataValue = binding.GetValue();
locProp.SetValue(dataValue);
}
}
}
}
/// <summary>
/// 获取第一个绑定的值
/// </summary>
private object GetFirstBindingValue()
{
foreach (var kvp in _bindings)
{
var val = kvp.Value.GetValue();
if (val != null) return val;
}
return null;
}
#endregion
#region 事件绑定
/// <summary>
/// 绑定 UI 交互事件(双向绑定)
/// </summary>
public void BindUIEvents()
{
if (_uiInstance == null) return;
var allComponents = new List<Component>();
_uiInstance.GetComponentsInChildren(true, allComponents);
foreach (var comp in allComponents)
{
if (comp is IXuiavrgFieldTagProvider tagProvider)
{
if (tagProvider.EntryTag != this.Tag) continue;
BindComponentEvents(comp);
}
}
// 子 Entry 事件绑定
foreach (var child in _children)
{
child.BindUIEvents();
}
}
private void BindComponentEvents(Component comp)
{
switch (comp)
{
case UnityEngine.UI.Button uiButton:
uiButton.onClick.AddListener(() =>
{
SetFirstBindingValue(true);
});
break;
case UnityEngine.UI.Toggle uiToggle:
uiToggle.onValueChanged.AddListener(val =>
{
SetFirstBindingValue(val);
});
break;
case UnityEngine.UI.Slider uiSlider:
uiSlider.onValueChanged.AddListener(val =>
{
SetFirstBindingValue(val);
});
break;
case UnityEngine.UI.InputField uiInput:
uiInput.onEndEdit.AddListener(val =>
{
SetFirstBindingValue(val);
});
break;
case UnityEngine.UI.Dropdown uiDropdown:
uiDropdown.onValueChanged.AddListener(val =>
{
SetFirstBindingValue(val);
});
break;
}
}
private void SetFirstBindingValue(object value)
{
foreach (var kvp in _bindings)
{
kvp.Value.SetValue(value);
break;
}
}
#endregion
#region 生命周期
public void OnEntryCreate()
{
_entryName = null;
_entryPath = null;
_parent = null;
_fieldOrder = 0;
_prefabOverrideName = null;
}
public void OnEntryBind()
{
BindUIEvents();
}
public void OnEntryRelease()
{
_entryName = null;
_entryPath = null;
_parent = null;
_uiInstance = null;
_boundPrefabGroup = null;
_prefabOverrideName = null;
Tag = XuiavrgFieldTag.None;
TagIndex = 0;
_fieldOrder = 0;
// 清理绑定引用
_bindings.Clear();
// 递归释放子 Entry
foreach (var child in _children)
{
Release(child);
}
_children.Clear();
}
public void OnEntryDestroy()
{
_bindings.Clear();
_children.Clear();
_uiInstance = null;
}
#endregion
}
}