提供了xericimage,这个组件可以帮助我们追踪toggle的checkmark事件,因为toggle的事件都没有提供重写,目前的c#版本也不支持钩子,所以这样是比较优的方案。

This commit is contained in:
2026-04-22 18:10:43 +08:00
parent e91aa14e9e
commit 296beac104
5 changed files with 188 additions and 49 deletions
+6
View File
@@ -18,9 +18,11 @@ namespace XericUIEditor.OverrideUI
SerializedProperty m_TextProperty;
SerializedProperty m_EnableTextColorTransitionProperty;
SerializedProperty m_isonStatusColor;
SerializedProperty m_TextTransitionColorModeProperty;
SerializedProperty m_enableTextContextTransitionProperty;
SerializedProperty m_textTransitionContextModeProperty;
SerializedProperty m_isonStatusTextContext;
protected override void OnEnable()
{
@@ -34,9 +36,11 @@ namespace XericUIEditor.OverrideUI
m_TextProperty = serializedObject.FindProperty("m_text");
m_EnableTextColorTransitionProperty = serializedObject.FindProperty("m_enableTextColorTransition");
m_isonStatusColor = serializedObject.FindProperty("m_isonStatusColor");
m_TextTransitionColorModeProperty = serializedObject.FindProperty("m_textTransitionColorMode");
m_enableTextContextTransitionProperty = serializedObject.FindProperty("m_enableTextContextTransition");
m_textTransitionContextModeProperty = serializedObject.FindProperty("m_textTransitionContextMode");
m_isonStatusTextContext = serializedObject.FindProperty("m_isonStatusTextContext");
}
public override void OnInspectorGUI()
@@ -76,6 +80,7 @@ namespace XericUIEditor.OverrideUI
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_TextTransitionColorModeProperty);
EditorGUILayout.PropertyField(m_isonStatusColor);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(m_enableTextContextTransitionProperty);
@@ -83,6 +88,7 @@ namespace XericUIEditor.OverrideUI
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_textTransitionContextModeProperty);
EditorGUILayout.PropertyField(m_isonStatusTextContext);
EditorGUI.indentLevel--;
}
+59 -41
View File
@@ -56,7 +56,7 @@ namespace XericUI.OverrideLayout
/// <summary>
/// 对象数据
/// </summary>
[SerializeField] private readonly List<ScrollItemData> _childrenDatas = ListPool<ScrollItemData>.Get();
[SerializeField] private readonly List<ScrollItemData> _childrenDatas = new List<ScrollItemData>();
public Vector2 ContextSize
{
@@ -83,6 +83,7 @@ namespace XericUI.OverrideLayout
private Queue<RectTransform> _activeQueue = new Queue<RectTransform>();
private bool layoutHasRebuildAtThisFrame;
private bool layoutDirty;
private bool indexMapDirty;
@@ -97,20 +98,21 @@ namespace XericUI.OverrideLayout
private bool _uiVisibal;
protected GameObject item;
public RectTransform itemRectTransform => item ? item.RectTransform() : null;
public IUIVesselVisibalControl ItemUIVesselVisibalControlComponent => item ? itemRectTransform.GetComponent<IUIVesselVisibalControl>() : null;
public bool GetUIVisibal => _uiVisibal;
public virtual void UnderVisibal()
{
item = parentScrollRect.childrenPool.Get();
var rectTransform = item.RectTransform();
parentScrollRect.UpdateChildPivot(layout, item.RectTransform());
rectTransform.anchoredPosition = layout.position;
itemRectTransform.anchoredPosition = layout.position;
}
public virtual void OutOfVisibal()
{
parentScrollRect.childrenPool.DirectlyRelease(item);
item = null;
}
public void UpdateLayout(GridLayoutCalculator.LayoutItem layoutData)
@@ -211,18 +213,22 @@ namespace XericUI.OverrideLayout
{
// 只有距离大于这个才会触发更新
_lastContextAnchoredPosition = position;
UpdateAAMVSVisibleStatus(position);
UpdateAAMVSVisibleStatus();
}
}
}
protected override void LateUpdate()
{
layoutHasRebuildAtThisFrame = false;
base.LateUpdate();
// 布局刷新
if (layoutDirty)
{
layoutDirty = false;
// 常规的刷新依赖视图的更新,如果这一帧只增删了对象,但是视图没有移动,那么这里就会执行更新
if (layoutHasRebuildAtThisFrame)
UpdateAAMVSVisibleStatus();
RefreshLayout();
}
// 全部索引刷新
@@ -239,8 +245,9 @@ namespace XericUI.OverrideLayout
/// <summary>
/// 自动剔除更新标记
/// </summary>
protected void UpdateAAMVSVisibleStatus(Vector2 position)
protected void UpdateAAMVSVisibleStatus()
{
layoutHasRebuildAtThisFrame = true;
// 将视口尺寸转换到content本地空间
// 手动计算视口在content空间的矩形
viewport.GetWorldCorners(viewportVertex);
@@ -261,57 +268,68 @@ namespace XericUI.OverrideLayout
// horizontal ? horizontalScrollbar?.value ?? 0 : 0,
// vertical ? 1 - (verticalScrollbar?.value ?? 1) : 0);
// ReSharper disable once PossibleInvalidCastExceptionInForeachLoop
foreach (RectTransform child in content.GetChildren())
foreach (var childData in _childrenDatas)
{
var visControl = child.GetComponent<IUIVesselVisibalControl>();
var isVisControlValid = visControl != null;
var childRectTransform = childData.itemRectTransform;
if (childRectTransform)
{
var visControl = childData.ItemUIVesselVisibalControlComponent;
var isVisControlValid = visControl != null;
// 直接使用anchoredPosition进行边界检测
var childPos = childRectTransform.anchoredPosition;
var isVisible = CheckIsVisibal(childPos);
var nowActive = childRectTransform.GetActivity();
var rectTransform = child.RectTransform();
// 直接使用anchoredPosition进行边界检测
var childPos = rectTransform.anchoredPosition;
var isVisible =
childPos.x >= visibleMin.x &&
childPos.x <= visibleMax.x &&
childPos.y >= visibleMin.y &&
childPos.y <= visibleMax.y;
// 以接口为准
if (isVisControlValid)
nowActive = visControl.GetUIVisibal;
var nowActive = child.GetActivity();
// 现在的状态与实际一致,跳过
if (isVisible == nowActive)
continue;
// 以接口为准
if (isVisControlValid)
nowActive = visControl.GetUIVisibal;
// 现在的状态与实际一致,跳过
if (isVisible == nowActive)
continue;
// 向成员发送状态(如果成员没有实现接口,就直接通过可见性控制)
if (isVisControlValid)
visControl.CallVisibal(isVisible);
// 向成员发送状态(如果成员没有实现接口,就直接通过可见性控制)
if (isVisControlValid)
visControl.CallVisibal(isVisible);
else
childRectTransform.gameObject.SetActive(isVisible);
}
else
child.gameObject.SetActive(isVisible);
{
var isVisible = CheckIsVisibal(childData.layout.position);
if (isVisible)
{
childData.UnderVisibal();
}
}
}
bool CheckIsVisibal(Vector2 pos)
{
return
pos.x >= visibleMin.x &&
pos.x <= visibleMax.x &&
pos.y >= visibleMin.y &&
pos.y <= visibleMax.y;
}
}
#endregion
#region 数据管理
public T GetScrollItemData<T>()
where T : ScrollItemData, new()
{
var result = new T
{
parentScrollRect = this
};
return result;
}
/// <summary>
/// 添加一个子项(自行重写这个子项类型数据结构,基于其中的生命周期函数完成内容的更新)
/// </summary>
/// <param name="Data"></param>
public void AddChildData(ScrollItemData Data)
{
layoutDirty = true;
Data.index = _childrenDatas.Count;
Data.parentScrollRect = this;
_childrenDatas.Add(Data);
}
+59
View File
@@ -0,0 +1,59 @@
using System;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
using XericUI.Helper;
namespace XericUI.OverrideUI
{
/// <summary>
/// 图像组件
/// </summary>
/// <remarks>
/// 这个组件主要为了提供graphic层的事件,用于应对部分无法重写的ui组件事件
/// </remarks>
public class XericImage : Image
{
/// <summary>
/// 针对toggle的回报(ison)
/// </summary>
internal Action<bool> OnToggleCrossFadeCall;
public override void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha, bool useRGB)
{
base.CrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha, useRGB);
OnToggleCrossFadeCall?.Invoke(targetColor.a > 0);
}
#region 组件替换
#if UNITY_EDITOR
[UnityEditor.MenuItem("CONTEXT/Image/Replace To XericImage", true)]
private static bool ValidateReplaceToXeric(UnityEditor.MenuCommand command)
=> command.context is Image and not XericImage;
[UnityEditor.MenuItem("CONTEXT/Image/Replace To XericImage", false, 10)]
private static void ReplaceToXeric(UnityEditor.MenuCommand command)
=> command.ReplaceCommandContext<Image, XericImage, Sprite>(
o =>
o.sprite,
(t, d) =>
{
t.sprite = d;
});
[UnityEditor.MenuItem("CONTEXT/Image/Replace To Image", true)]
private static bool ValidateReplaceToUnity(UnityEditor.MenuCommand command)
=> command.context is XericImage;
[UnityEditor.MenuItem("CONTEXT/Image/Replace To Image", false, 10)]
private static void ReplaceToUnity(UnityEditor.MenuCommand command)
=> command.ReplaceCommandContext<XericImage, Image, Sprite>(
o =>
o.sprite,
(t, d) =>
{
t.sprite = d;
});
#endif
#endregion
}
}
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 52aeb4e8bd3a4b6c9513b8ad21b41558
timeCreated: 1776850195
+61 -8
View File
@@ -6,6 +6,7 @@ using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
using XericUI.Helper;
using XericUI.OverrideUI;
namespace XericUI
{
@@ -46,6 +47,7 @@ namespace XericUI
public bool m_enableTextColorTransition = true;
public ColorBlock m_textTransitionColorMode = ColorBlock.defaultColorBlock;
public Color m_isonStatusColor = Color.white;
public bool m_enableTextContextTransition = false;
public AnimationTriggers m_textTransitionContextMode = new AnimationTriggers()
{
@@ -55,6 +57,9 @@ namespace XericUI
selectedTrigger = "toggle_selected",
disabledTrigger = "toggle_disabled",
};
public string m_isonStatusTextContext = "toggle_ison";
public bool EnableTextColorTransition
{
@@ -67,7 +72,39 @@ namespace XericUI
get => m_textTransitionColorMode;
set => m_textTransitionColorMode = value;
}
private XericImage m_ximage;
protected XericImage ximage
{
get
{
if (m_ximage)
return m_ximage;
#if UNITY_EDITOR
if (!graphic)
return null;
#endif
m_ximage = graphic.GetComponent<XericImage>();
if (m_ximage)
return m_ximage;
Debug.Log("注意替换toggle的checkmark为XericImage以支持完整的功能");
return null;
}
}
private bool bindXImage;
protected override void InstantClearState()
{
base.InstantClearState();
if (m_enableTextColorTransition)
TextColor = colors.normalColor;
if (m_enableTextContextTransition)
TextContent = m_textTransitionContextMode.normalTrigger;
}
protected override void DoStateTransition(SelectionState state, bool instant)
{
if (!gameObject.activeInHierarchy)
@@ -117,8 +154,8 @@ namespace XericUI
transitionName = m_textTransitionContextMode.disabledTrigger;
break;
default:
tintColor = Color.black;
textColor = Color.white;
tintColor = colors.normalColor;
textColor = m_textTransitionColorMode.normalColor;
transitionSprite = null;
triggerName = string.Empty;
transitionName = m_textTransitionContextMode.normalTrigger;
@@ -138,13 +175,25 @@ namespace XericUI
break;
}
if (!bindXImage && ximage != null)
{
bindXImage = true;
ximage.OnToggleCrossFadeCall = b =>
{
if (m_enableTextColorTransition)
TextColor = b ? m_textTransitionColorMode.pressedColor : m_textTransitionColorMode.normalColor;
if (m_enableTextContextTransition)
TextContent = b ? m_textTransitionContextMode.pressedTrigger : m_textTransitionContextMode.normalTrigger;
};
}
if (m_enableTextColorTransition)
TextColor = isOn ? m_textTransitionColorMode.selectedColor : textColor;
TextColor = isOn ? m_isonStatusColor : textColor;
if (m_enableTextContextTransition)
TextContent = transitionName;
}
void StartColorTween(Color targetColor, bool instant)
{
if (targetGraphic == null)
@@ -184,8 +233,9 @@ namespace XericUI
=> command.context is Toggle and not XericToggle;
[UnityEditor.MenuItem("CONTEXT/Toggle/Replace To XericToggle", false, 10)]
private static void ReplaceToXeric(UnityEditor.MenuCommand command)
=> command.ReplaceCommandContext<Toggle, XericToggle,
private static void ReplaceToXeric(UnityEditor.MenuCommand command)
{
command.ReplaceCommandContext<Toggle, XericToggle,
(Graphic graphic, ToggleGroup group, ToggleEvent onValueChanged)>(
o =>
(o.graphic, o.group, o.onValueChanged),
@@ -195,6 +245,8 @@ namespace XericUI
t.group = d.group;
t.onValueChanged = d.onValueChanged;
});
Debug.Log("请手动替换toggle下的Checkmark Image组件为XericImage,否则Toggle工作时取消选择的状态会保持为ison状态。");
}
[UnityEditor.MenuItem("CONTEXT/Toggle/Replace To Toggle", true)]
private static bool ValidateReplaceToUnity(UnityEditor.MenuCommand command)
@@ -214,5 +266,6 @@ namespace XericUI
});
#endif
#endregion
}
}