属性反射生成界面基本框架。

修复懒加载框的布局问题。
This commit is contained in:
2026-04-24 19:42:35 +08:00
parent 296beac104
commit 283a444228
26 changed files with 953 additions and 66 deletions
+21 -7
View File
@@ -19,6 +19,17 @@ namespace XericUI.Helper
/// <summary>
/// 计算所有项目的布局
/// </summary>
/// <param name="childCount">子项数量</param>
/// <param name="containerSize">所需尺寸</param>
/// <param name="cellSize">单元尺寸</param>
/// <param name="spacing">单元空隙</param>
/// <param name="padding">外围空隙</param>
/// <param name="startCorner">起始角</param>
/// <param name="startAxis">起始轴</param>
/// <param name="constraint">容器尺寸</param>
/// <param name="constraintCount">需要的列数</param>
/// <param name="childAlignment">子项对齐</param>
/// <returns></returns>
public static List<LayoutItem> CalculateGridLayout(
List<LayoutItem> layoutItems,
int childCount,
@@ -143,13 +154,13 @@ namespace XericUI.Helper
float baseOffset;
if (axis == 0) // X轴
{
// 如果startCorner在右侧,基准点应该是padding.right
// 如果startCorner在右侧,基准点应该是availableSpace - padding.right - requiredSpace
int cornerX = (int)startCorner % 2;
baseOffset = (cornerX == 0) ? padding.left : (availableSpace - padding.right - requiredSpace);
}
else // Y轴
{
// 如果startCorner在底部,基准点应该是padding.bottom
// 如果startCorner在底部,基准点应该是availableSpace - padding.bottom - requiredSpace
int cornerY = (int)startCorner / 2;
baseOffset = (cornerY == 0) ? padding.top : (availableSpace - padding.bottom - requiredSpace);
}
@@ -176,10 +187,12 @@ namespace XericUI.Helper
if (startAxis == GridLayoutGroup.Axis.Horizontal)
{
if (constraint == GridLayoutGroup.Constraint.FixedRowCount &&
childCount - index <= childrenToMove)
childrenToMove > 0 && childCount - index <= childrenToMove)
{
// 计算偏移量,确保gridY不会为负数
int offset = childCount - index - 1;
gridX = 0;
gridY = constraintCount - (childCount - index);
gridY = Mathf.Min(offset, constraintCount - 1);
}
else
{
@@ -190,9 +203,11 @@ namespace XericUI.Helper
else
{
if (constraint == GridLayoutGroup.Constraint.FixedColumnCount &&
childCount - index <= childrenToMove)
childrenToMove > 0 && childCount - index <= childrenToMove)
{
gridX = constraintCount - (childCount - index);
// 计算偏移量,确保gridX不会为负数
int offset = childCount - index - 1;
gridX = Mathf.Min(offset, constraintCount - 1);
gridY = 0;
}
else
@@ -201,7 +216,6 @@ namespace XericUI.Helper
gridY = index % cellCountY;
}
}
}
private static int CalculateChildrenToMove(
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5661a6f69ad8f9e4a8085712f4ac7e38
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,7 @@
namespace XericUI.ReflectionUIGenerator
{
public interface IXuiavrgAttribute
{
public void BindProperty(XuiavrgInspectorProperty property);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 998589d7b3414ddd99b69cb128d7e052
timeCreated: 1777014404
@@ -0,0 +1,107 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
using UnityEngine;
using XericLibrary.Runtime.MacroLibrary;
using Object = UnityEngine.Object;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// 属性映射器
/// </summary>
[Serializable]
[CreateAssetMenu(fileName = "Xeric UI avr Property", menuName = "Xeric Library/UI Action Vessel/Reflection UI Gen/UI avr Property", order = 10)]
public class XuiavrPropertyScriptableObject : ScriptableObject
{
#if ODIN_INSPECTOR
[LabelText("属性类型")]
[ValueDropdown("AssemblyValueTypeNames")]
#endif
public string valueType;
#if ODIN_INSPECTOR
[LabelText("属性名称")]
#endif
public string propertyName;
#if ODIN_INSPECTOR
[LabelText("界面预制体")]
#endif
public GameObject prefab;
/// <summary>
/// 属性类型
/// </summary>
public System.Type ValueType
{
get
{
if (_valueType != null)
return _valueType;
return _valueType = Type.GetType(valueType);
}
}
private System.Type _valueType;
/// <summary>
/// 属性类型名称枚举
/// </summary>
private IEnumerable AssemblyValueTypeNames
{
get
{
if (_assemblyValueTypeName != null)
return _assemblyValueTypeName;
_assemblyValueTypeName = new ValueDropdownList<string>();
// foreach (var typeName in MacroReflection.GetAllAssignableTypeInAppDomainAssembly(
// typeof(float),
// typeof(string),
// typeof(bool),
// typeof(Enum),
// typeof(int)
// ).Select(a => a.FullName))
// {
// _assemblyValueTypeName.Add(typeName);
// }
_assemblyValueTypeName.AddRange(AllValueType.Select(a => new ValueDropdownItem<string>(a.Name, a.FullName)));
return _assemblyValueTypeName;
}
}
private static ValueDropdownList<string> _assemblyValueTypeName = null;
/// <summary>
/// 直接定义所有需要检查的类型
/// </summary>
public static IEnumerable<Type> AllValueType
{
get
{
yield return typeof(Enum);
yield return typeof(decimal);
yield return typeof(double);
yield return typeof(float);
yield return typeof(ulong);
yield return typeof(long);
yield return typeof(int);
yield return typeof(uint);
yield return typeof(short);
yield return typeof(ushort);
yield return typeof(byte);
yield return typeof(sbyte);
yield return typeof(string);
yield return typeof(char);
yield return typeof(bool);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: be0acbdac8e549578dcccf8c076a5433
timeCreated: 1776998687
@@ -1,7 +1,78 @@
namespace XericUI.ReflectionUIGenerator
using System;
namespace XericUI.ReflectionUIGenerator
{
public class XuiavrgAttribute
/// <summary>
/// 反射属性标记
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class XuiavrgAttribute : Attribute, IXuiavrgAttribute
{
#region 字段属性
public readonly string LabelName;
public readonly string UIPropertyName;
public readonly string UnitLabelName;
public readonly bool MustFillIn;
public readonly int InspectorOrder;
public readonly string GroupName;
public readonly string OnTriggerCall;
public readonly XuiavrgSize ArgumentSize;
/// <summary>
/// 标记属性
/// </summary>
/// <param name="labelName">属性显示名称</param>
/// <param name="uiPropertyName">属性标记名(对应界面元素名称)</param>
/// <param name="unitLabelName">属性显示单位名称</param>
/// <param name="mustFillin">是否标记为必填项目</param>
/// <param name="inspectorOrder">属性显示顺序</param>
/// <param name="groupName">属性组名称(可以是以/&\分割的路径)</param>
/// <param name="onTriggerCall">属性附加的动作,调用此内联反射调用的函数</param>
/// <param name="size">属性组名称(可以是以/&\分割的路径)</param>
public XuiavrgAttribute(string labelName, string uiPropertyName, string unitLabelName, bool mustFillin,
int inspectorOrder = 0, string groupName = null, string onTriggerCall = "", XuiavrgSize size = XuiavrgSize.Normal)
{
LabelName = labelName;
UIPropertyName = uiPropertyName;
UnitLabelName = unitLabelName;
MustFillIn = mustFillin;
InspectorOrder = inspectorOrder;
GroupName = groupName;
OnTriggerCall = onTriggerCall;
ArgumentSize = size;
}
#endregion
#region 生成器
public void BindProperty(XuiavrgInspectorProperty property)
{
property.labelName = LabelName;
property.uiPropertyName = UIPropertyName;
property.unitLabelName = UnitLabelName;
property.mustFillIn = MustFillIn;
property.inspectorOrder = InspectorOrder;
property.groupName = GroupName;
property.onTriggerCall = OnTriggerCall;
property.argumentSize = ArgumentSize;
}
#endregion
}
/// <summary>
/// 属性尺寸
/// </summary>
public enum XuiavrgSize
{
None = 0,
Tiny = 1000 - 2,
Small = 1000 - 1,
Normal = 1000,
Big,
Large,
}
}
@@ -0,0 +1,28 @@
using System;
using UnityEngine.Serialization;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// 隐藏标记
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class XuiavrgHideAttribute : Attribute, IXuiavrgAttribute
{
public readonly bool Hide;
/// <summary>
/// 标记属性
/// </summary>
/// <param name="hideInInspector">隐藏属性</param>
public XuiavrgHideAttribute(bool hideInInspector = true)
{
Hide = hideInInspector;
}
public void BindProperty(XuiavrgInspectorProperty property)
{
property.hideInInspector = Hide;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 12f7aecaa5224304b1058ab9dc04a95f
timeCreated: 1776995602
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Deconstruction.Type.Peculiarity;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
using UnityEngine;
using UnityEngine.Pool;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// 节点下生成反射属性管理器
/// </summary>
/// <remarks>
/// 管理器需要挂载在unity节点中,管理器中指定的属性承载节点将用于生成属性内容。
///
/// 为了准确生成界面属性,首先需要准备界面属性预制体。
/// 在包含界面交互元素的预制体跟目录上挂载 <see cref="XuiavrgLocator"/> 脚本,其将作为组件入口,管理所有的交互组件,必须在脚本中添加所有允许交互的元素。
/// (注:如果你没有odin插件,那么组件样式可能会与设计的不一致,请务必确保每个数组中只包含一个组件)
///
/// 然后在工程中创建 <see cref="XuiavrPropertyScriptableObject"/> 对象,指定对应的数据类型,将刚刚创建的界面预制体拖入,这样就完成了两者的绑定。
/// 为了方便管理,再创建一个 <see cref="XuiavrgPropertyGroupScriptableObject"/> 对象,它可以成为一个组,快速管理和切换所需的数据集合。
///
/// 接下来,在管理器中填入使用的 <see cref="XuiavrgPropertyGroupScriptableObject"/> 对象,然后调用 SchemaHydration 方法传入负载,界面顷刻生成。
///
/// </remarks>
public class XuiavrgInspectorManager : MonoBehaviour
{
public static Func<XuiavrgInspectorProperty> CreatInspectorProperty = () => new XuiavrgInspectorProperty();
public static Action<XuiavrgInspectorProperty> GetInspectorProperty = a => {};
public static Action<XuiavrgInspectorProperty> ReleaseInspectorProperty = a => a.Clear();
public static Action<XuiavrgInspectorProperty> DestoryInspectorProperty = a => {};
public static ObjectPool<XuiavrgInspectorProperty> InspectorPropertyPool
= new ObjectPool<XuiavrgInspectorProperty>(CreatInspectorProperty, GetInspectorProperty, ReleaseInspectorProperty, DestoryInspectorProperty);
#if ODIN_INSPECTOR
[LabelText("预制体组")]
#endif
public XuiavrgPropertyGroupScriptableObject propertyUIPrefabsGroup;
#if ODIN_INSPECTOR
[LabelText("反射属性预览")]
#endif
public List<XuiavrgInspectorProperty> allProperty = new List<XuiavrgInspectorProperty>();
/// <summary>
/// 装填数据负载
/// </summary>
/// <param name="payload">数据端对象</param>
#if ODIN_INSPECTOR
[Button]
#endif
public void SchemaHydration(object payload)
{
// 清空环境和界面
allProperty.Clear();
InspectorPropertyPool.Clear();
if (!propertyUIPrefabsGroup)
{
Debug.LogError("未指定编组,无法生成界面");
return;
}
if (payload != null)
{
Debug.LogError("未指定有效负载,无法生成界面");
return;
}
// 从反射构建
var type = payload.GetType();
SchemaHydration(allProperty, type, payload);
}
/// <summary>
/// 状态数据负载
/// </summary>
/// <param name="properties">反射属性结合</param>
/// <param name="payload">负载实例</param>
/// <typeparam name="T">进行检查的负载类型</typeparam>
private static void SchemaHydration<T>(List<XuiavrgInspectorProperty> properties, object payload)
=> SchemaHydration(properties, typeof(T), payload);
/// <summary>
/// 装填数据负载
/// </summary>
/// <param name="properties">反射属性集合</param>
/// <param name="payloadType">进行检查的负载类型</param>
/// <param name="payload">负载实例</param>
private static void SchemaHydration(List<XuiavrgInspectorProperty> properties, Type payloadType, object payload)
{
bool needSort = false;
foreach (var memberInfo in payloadType.GetRuntimeMemberInfo())
{
var mw = InspectorPropertyPool.Get();
// 绑定负载
mw.RegsiterDataValue(memberInfo, payload);
// 字段属性上的所有特性都进行标记
foreach (var memberAttribute in memberInfo.GetCustomAttributes()
.OfType<IXuiavrgAttribute>())
{
memberAttribute.BindProperty(mw);
needSort |= mw.inspectorOrder != 0; // 如果有成员使用了order
}
properties.Add(mw);
}
// 排序
if (needSort)
properties.Sort((property1, property2) => property1.inspectorOrder.CompareTo(property2.inspectorOrder));
}
}
}
@@ -0,0 +1,142 @@
using System;
using System.Reflection;
using UnityEngine.Serialization;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// 反射属性中间层
/// </summary>
[Serializable]
public class XuiavrgInspectorProperty
{
public object valueTarget;
public bool isProperty;
public PropertyInfo propertyInfo;
public FieldInfo fieldInfo;
public string labelName;
public string uiPropertyName;
public string unitLabelName;
public bool mustFillIn;
public int inspectorOrder;
public string groupName;
public string onTriggerCall;
public XuiavrgSize argumentSize;
public bool hideInInspector;
/// <summary>
/// 绑定到数据端
/// </summary>
/// <param name="memberInfo"></param>
/// <param name="target"></param>
public void RegsiterDataValue(MemberInfo memberInfo, object target)
{
switch (memberInfo)
{
case PropertyInfo propertyInfo:
RegsiterDataValue(propertyInfo, target);
break;
case FieldInfo fieldInfo:
RegsiterDataValue(fieldInfo, target);
break;
}
}
/// <summary>
/// 绑定到数据端
/// </summary>
/// <param name="memberInfo"></param>
/// <param name="target"></param>
public virtual void RegsiterDataValue(PropertyInfo memberInfo, object target)
{
valueTarget = target;
isProperty = true;
propertyInfo = memberInfo;
}
/// <summary>
/// 绑定到数据端
/// </summary>
/// <param name="memberInfo"></param>
/// <param name="target"></param>
public virtual void RegsiterDataValue(FieldInfo memberInfo, object target)
{
valueTarget = target;
isProperty = false;
fieldInfo = memberInfo;
}
/// <summary>
/// 绑定到界面端
/// </summary>
public virtual void RegsiterUIElement()
{
}
/// <summary>
/// 数据端值发生变更
/// </summary>
public void CallDataValueChange()
{
}
/// <summary>
/// 界面端数据发生变更
/// </summary>
public void CallUIValueChange()
{
}
/// <summary>
/// 从数据端获取值
/// </summary>
/// <returns></returns>
public object GetValueFromTargetMember()
{
if (isProperty)
return propertyInfo.GetValue(valueTarget);
else
return fieldInfo.GetValue(valueTarget);
}
/// <summary>
/// 设置值到数据端
/// </summary>
/// <param name="value"></param>
public void SetValueToTargetMember(object value)
{
if (isProperty)
propertyInfo.SetValue(valueTarget, value);
else
fieldInfo.SetValue(valueTarget, value);
}
/// <summary>
/// 从界面端获取值
/// </summary>
/// <returns></returns>
public object GetValueFormUIElement()
{
return null;
}
/// <summary>
/// 设置值到界面端
/// </summary>
/// <param name="value"></param>
public void SetValueToUIElement(object value)
{
}
public void Clear()
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5d82ae110adb48b397c09939694cc3b2
timeCreated: 1777011022
@@ -0,0 +1,305 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// ui反射属性定位器,放在UI对象的根节点上,设定好接下来的目标即可
/// </summary>
public class XuiavrgLocator : MonoBehaviour
{
public delegate void AnyTrigger(LocatorProperty locator, object value, int selectStart, int selectEnd);
#if ODIN_INSPECTOR
[LabelText("映射组件")]
#endif
public List<LocatorProperty> Properties;
[Serializable]
public class LocatorProperty
{
#if ODIN_INSPECTOR
[LabelText("组件标识名")]
#endif
[Tooltip("建议使用 name, label, unit, sign, input, output, button, slider, edit, select 等简单易懂的名称进行标记")]
public string propertyName;
#if ODIN_INSPECTOR
[LabelText("组件类型")]
#endif
public UIElementClassify classify;
#if dUI_TextMeshPro
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.TMPText)]
#endif
public TMPro.TMP_Text TMPText;
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.TMPInputField)]
#endif
public TMPro.TMP_InputField TMPInputField;
#endif
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.UIText)]
#endif
public UnityEngine.UI.Text UIText;
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.UIInputField)]
#endif
public UnityEngine.UI.InputField UIInputField;
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.UIToggle)]
#endif
public UnityEngine.UI.Toggle UIToggle;
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.UIButton)]
#endif
public UnityEngine.UI.Button UIButton;
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.UISlider)]
#endif
public UnityEngine.UI.Slider UISlider;
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.UIDropdown)]
#endif
public UnityEngine.UI.Dropdown UIDropdown;
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.TMPInputField)]
#endif
public TMPInputTriggerEvent TMPInputTriggerOnEventType;
#if ODIN_INSPECTOR
[ShowIf("classify", UIElementClassify.UIInputField)]
#endif
public UIInputTriggerEvent UIInputTriggerOnEventType;
/// <summary>
/// 支持的组件类型
/// </summary>
public enum UIElementClassify
{
None,
#if dUI_TextMeshPro
TMPText,
TMPInputField,
#endif
UIText,
UIInputField,
UIToggle,
UIButton,
UISlider,
UIDropdown,
}
[Flags]
public enum TMPInputTriggerEvent
{
none,
onEndEdit = 1 << 1,
onSubmit = 1 << 2,
onSelect = 1 << 3,
onDeselect = 1 << 4,
onTextSelection = 1 << 5,
onEndTextSelection = 1 << 6,
onValueChanged = 1 << 7,
onTouchScreenKeyboardStatusChanged = 1 << 8,
}
public enum UIInputTriggerEvent
{
none,
onEndEdit,
onSubmit,
onValueChanged,
}
/// <summary>
/// 获取值
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public object GetValue()
{
switch (classify)
{
#if dUI_TextMeshPro
case UIElementClassify.TMPText:
return TMPText.text;
case UIElementClassify.TMPInputField:
return TMPInputField.text;
#endif
case UIElementClassify.UIText:
return UIText.text;
case UIElementClassify.UIInputField:
return UIInputField.text;
case UIElementClassify.UIToggle:
return UIToggle.isOn;
case UIElementClassify.UIButton:
return false;
case UIElementClassify.UISlider:
return UISlider.value;
case UIElementClassify.UIDropdown:
return UIDropdown.value;
default:
return null;
}
}
/// <summary>
/// 设置值
/// </summary>
/// <param name="value"></param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public void SetValue(object value)
{
switch (classify)
{
#if dUI_TextMeshPro
case UIElementClassify.TMPText:
TMPText.text = value.ToString();
break;
case UIElementClassify.TMPInputField:
TMPInputField.text = value.ToString();
break;
#endif
case UIElementClassify.UIText:
UIText.text = value.ToString();
break;
case UIElementClassify.UIInputField:
UIInputField.text = value.ToString();
break;
case UIElementClassify.UIToggle:
UIToggle.isOn = Convert.ToBoolean(value);
break;
case UIElementClassify.UIButton:
// UIButton do nothing
break;
case UIElementClassify.UISlider:
UISlider.value = Convert.ToSingle(value);
break;
case UIElementClassify.UIDropdown:
UIDropdown.value = Convert.ToInt32(value);
break;
default:
break;
}
}
/// <summary>
/// ui触发事件
/// </summary>
public event AnyTrigger OnUITrigger
{
add
{
switch (classify)
{
#if dUI_TextMeshPro
case UIElementClassify.TMPInputField:
switch (TMPInputTriggerOnEventType)
{
case TMPInputTriggerEvent.onEndEdit:
TMPInputField.onEndEdit.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
case TMPInputTriggerEvent.onSubmit:
TMPInputField.onSubmit.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
case TMPInputTriggerEvent.onSelect:
TMPInputField.onSelect.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
case TMPInputTriggerEvent.onDeselect:
TMPInputField.onDeselect.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
case TMPInputTriggerEvent.onTextSelection:
TMPInputField.onTextSelection.AddListener((content, start, end) =>
value?.Invoke(this, content, start, end));
break;
case TMPInputTriggerEvent.onEndTextSelection:
TMPInputField.onEndTextSelection.AddListener((content, start, end) =>
value?.Invoke(this, content, start, end));
break;
case TMPInputTriggerEvent.onValueChanged:
TMPInputField.onValueChanged.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
case TMPInputTriggerEvent.onTouchScreenKeyboardStatusChanged:
TMPInputField.onTouchScreenKeyboardStatusChanged.AddListener(a =>
value?.Invoke(this, a, 0, 0));
break;
default:
break;
}
break;
#endif
case UIElementClassify.UIInputField:
switch (UIInputTriggerOnEventType)
{
case UIInputTriggerEvent.onEndEdit:
UIInputField.onEndEdit.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
case UIInputTriggerEvent.onSubmit:
UIInputField.onSubmit.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
case UIInputTriggerEvent.onValueChanged:
UIInputField.onValueChanged.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
default: break;
}
break;
case UIElementClassify.UIToggle:
UIToggle.onValueChanged.AddListener(a => value?.Invoke(this, value, 0, 0));
break;
case UIElementClassify.UIButton:
UIButton.onClick.AddListener(() => value?.Invoke(this, true, 0, 0));
break;
case UIElementClassify.UISlider:
UISlider.onValueChanged.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
case UIElementClassify.UIDropdown:
UIDropdown.onValueChanged.AddListener(a => value?.Invoke(this, a, 0, 0));
break;
default:
break;
}
}
remove { Debug.Log("任意触发器无法卸载特定事件"); }
}
/// <summary>
/// 清空ui触发事件
/// </summary>
public void ClearTriggerEvent()
{
if (TMPInputField)
{
TMPInputField.onEndEdit.RemoveAllListeners();
TMPInputField.onSubmit.RemoveAllListeners();
TMPInputField.onSelect.RemoveAllListeners();
TMPInputField.onDeselect.RemoveAllListeners();
TMPInputField.onTextSelection.RemoveAllListeners();
TMPInputField.onEndTextSelection.RemoveAllListeners();
TMPInputField.onValueChanged.RemoveAllListeners();
TMPInputField.onTouchScreenKeyboardStatusChanged.RemoveAllListeners();
}
if (UIInputField)
{
UIInputField.onEndEdit.RemoveAllListeners();
UIInputField.onSubmit.RemoveAllListeners();
UIInputField.onValueChanged.RemoveAllListeners();
}
UIToggle?.onValueChanged.RemoveAllListeners();
UIButton?.onClick.RemoveAllListeners();
UISlider?.onValueChanged.RemoveAllListeners();
UIDropdown?.onValueChanged.RemoveAllListeners();
}
}
// todo 使用名称,类型筛选得到组件
// todo 根据名称组建字典树加速查找(或许不用)
}
}
@@ -1,30 +0,0 @@
using Deconstruction.Type.Peculiarity;
using UnityEngine;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// 节点下生成反射属性管理器
/// </summary>
public class XuiavrgManager
{
/// <summary>
/// 装填数据负载
/// </summary>
/// <param name="payload"></param>
public void PerformSchemaDrivenHydration(object payload)
{
var type = payload.GetType();
var attributes = type.GetCustomAttributes(true);
foreach (var attribute in attributes)
{
if (attribute is not XuiavrgAttribute xuiavrgAttribute)
continue;
}
}
}
}
@@ -1,14 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// ui反射属性定位器,放在池对象的根节点上,设定好接下来的目标即可
/// </summary>
public class XuiavrgPooledLocator : MonoBehaviour
{
}
}
@@ -0,0 +1,77 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace XericUI.ReflectionUIGenerator
{
/// <summary>
/// 界面属性编组
/// </summary>
[Serializable]
[CreateAssetMenu(fileName = "Xeric UI avr Property group", menuName = "Xeric Library/UI Action Vessel/Reflection UI Gen/UI avr Property Group", order = 10)]
public class XuiavrgPropertyGroupScriptableObject : ScriptableObject, IList<XuiavrPropertyScriptableObject>
{
public List<XuiavrPropertyScriptableObject> propertys = new List<XuiavrPropertyScriptableObject>();
public IEnumerator<XuiavrPropertyScriptableObject> GetEnumerator()
{
return propertys.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)propertys).GetEnumerator();
}
public void Add(XuiavrPropertyScriptableObject item)
{
propertys.Add(item);
}
public void Clear()
{
propertys.Clear();
}
public bool Contains(XuiavrPropertyScriptableObject item)
{
return propertys.Contains(item);
}
public void CopyTo(XuiavrPropertyScriptableObject[] array, int arrayIndex)
{
propertys.CopyTo(array, arrayIndex);
}
public bool Remove(XuiavrPropertyScriptableObject item)
{
return propertys.Remove(item);
}
public int Count => propertys.Count;
public bool IsReadOnly => true;
public int IndexOf(XuiavrPropertyScriptableObject item)
{
return propertys.IndexOf(item);
}
public void Insert(int index, XuiavrPropertyScriptableObject item)
{
propertys.Insert(index, item);
}
public void RemoveAt(int index)
{
propertys.RemoveAt(index);
}
public XuiavrPropertyScriptableObject this[int index]
{
get => propertys[index];
set => propertys[index] = value;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bcd1c64483d2457ab9143f086dff511d
timeCreated: 1777024131
@@ -1,7 +1,22 @@
namespace XericUI.ReflectionUIGenerator
using System;
using System.Collections.Generic;
using System.Reflection;
namespace XericUI.ReflectionUIGenerator
{
public static class XuiavrgUtilities
{
/// <summary>
/// 获取所有运行时成员(字段和属性)
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<MemberInfo> GetRuntimeMemberInfo(this Type type)
{
foreach (var field in type.GetRuntimeFields())
yield return field;
foreach (var property in type.GetRuntimeProperties())
yield return property;
}
}
}
+33 -10
View File
@@ -33,11 +33,11 @@ namespace XericUI.OverrideLayout
[Tooltip("容器预加载像素范围")] [SerializeField]
protected float m_preloadMargin = 200f;
[Tooltip("容器加载响应可被察觉的变化长度")] [SerializeField]
[Tooltip("容器加载响应可被察觉的变化长度(建议设置为子项的二维最小的尺寸)")] [SerializeField]
protected float m_discernibleDistance = 10f;
[Tooltip("自动剔除更新速率,当设定趋近零时,实际上是期望每帧执行更新")] [SerializeField]
protected float m_AAMVUpdateRate = .5f;
protected float m_AAMVUpdateRate = .1f;
// grid布局组件
[SerializeField] protected Vector2 m_cellSize = new Vector2(100, 100);
@@ -261,7 +261,7 @@ namespace XericUI.OverrideLayout
// 扩展可见区域边界
var visibleMin = viewportRect.min - new Vector2(m_preloadMargin, m_preloadMargin);
var visibleMax = viewportRect.max + new Vector2(m_preloadMargin, m_preloadMargin);
var visibleMax = viewportRect.max + new Vector2(m_preloadMargin, -m_preloadMargin);
// 获取当前滚动归一化位置
// var scrollPos = new Vector2(
@@ -397,22 +397,34 @@ namespace XericUI.OverrideLayout
private void RefreshContentSizeByChildrenLayouts(List<GridLayoutCalculator.LayoutItem> layoutItems)
{
if (layoutItems.Count == 0) return;
var first = layoutItems[0];
var last = layoutItems[^1];
var size = first.position - last.position;
size = size.Abs();
var spacing = new Vector2(GridLayoutCalculator.Grid.x * m_spacing.x, GridLayoutCalculator.Grid.y * m_spacing.y);
// 计算实际使用的行列数
int maxRow = 0;
int maxColumn = 0;
foreach (var item in layoutItems)
{
maxRow = Mathf.Max(maxRow, item.rowIndex);
maxColumn = Mathf.Max(maxColumn, item.columnIndex);
}
int actualCellCountX = maxColumn + 1;
int actualCellCountY = maxRow + 1;
var spacing = new Vector2((actualCellCountX - 1) * m_spacing.x, (actualCellCountY - 1) * m_spacing.y);
switch (m_startAxis)
{
case GridLayoutGroup.Axis.Horizontal:
var ySpacing = GridLayoutCalculator.Grid.y * m_spacing.y;
size.x = Mathf.Max(size.x, viewport.rect.width);
size.y = Mathf.Max(size.y + spacing.y, m_cellSize.y);
size.y = Mathf.Max(size.y + m_spacing.y, m_cellSize.y);
break;
case GridLayoutGroup.Axis.Vertical:
var xSpacing = GridLayoutCalculator.Grid.x * m_spacing.x;
size.x = Mathf.Max(size.x + spacing.x, m_cellSize.x);
size.x = Mathf.Max(size.x + m_spacing.x, m_cellSize.x);
size.y = Mathf.Max(size.y, viewport.rect.height);
break;
}
@@ -454,7 +466,17 @@ namespace XericUI.OverrideLayout
}
private List<GridLayoutCalculator.LayoutItem> GetChildrenLayout(int childrenCount)
=> GridLayoutCalculator.CalculateGridLayout(
{
// 根据约束类型设置合适的 constraintCount
int constraintCount = 0;
if (m_constraint == GridLayoutGroup.Constraint.FixedColumnCount || m_constraint == GridLayoutGroup.Constraint.FixedRowCount)
{
// 这里可以根据实际需求设置合适的约束数量
// 暂时使用一个默认值,实际项目中可能需要从外部传入或通过其他方式计算
constraintCount = 3;
}
return GridLayoutCalculator.CalculateGridLayout(
_layoutItems,
childCount: childrenCount,
containerSize: ContextSize,
@@ -464,9 +486,10 @@ namespace XericUI.OverrideLayout
startCorner: m_startCorner,
startAxis: m_startAxis,
constraint: m_constraint,
constraintCount: 0,
constraintCount: constraintCount,
childAlignment: m_childAlignment
);
}
public void RefreshChildrenLayout()
{