diff --git a/.gitignore b/.gitignore index bdcf39e..31091bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .trae/ +.recode/ diff --git a/Runtime/Resources/Style/DefaultTableStyles.xsss b/Runtime/Resources/Style/DefaultTableStyles.xsss index dd99254..32f8470 100644 --- a/Runtime/Resources/Style/DefaultTableStyles.xsss +++ b/Runtime/Resources/Style/DefaultTableStyles.xsss @@ -40,7 +40,7 @@ cell/font/ { size = 14:System.Int32 @Desc:默认字体大小 alignment = MiddleCenter:UnityEngine.TextAnchor @Desc:默认文本对齐 richText = true:System.Boolean @Desc:是否启用富文本 - fontAsset = guid:68dace674a028544187e7ffe55cdae8b:TMPro.TMP_FontAsset:TMPro.TMP_FontAsset @Desc:字体资源 @ResourceSoftReference:Packages/com.lrss3.xericuiactionvessel/Runtime/Font/SourceHanSansSC-Regular-2 SDF.asset + #fontAsset = guid:68dace674a028544187e7ffe55cdae8b:TMPro.TMP_FontAsset:TMPro.TMP_FontAsset @Desc:字体资源 @ResourceSoftReference:Packages/com.lrss3.xericuiactionvessel/Runtime/Font/SourceHanSansSC-Regular-2 SDF.asset } cell/default/ { diff --git a/Runtime/XTable/Rendering/Component/XTableSelectionInput.cs b/Runtime/XTable/Rendering/Component/XTableSelectionInput.cs index 573b197..663d129 100644 --- a/Runtime/XTable/Rendering/Component/XTableSelectionInput.cs +++ b/Runtime/XTable/Rendering/Component/XTableSelectionInput.cs @@ -33,7 +33,8 @@ namespace XericUI.XTable.Rendering.Component { if (!m_Dragging) return; m_Dragging = false; - m_Table?.EndSelection(); + // 用 Unity 判空而非 ?.:表格可能已被销毁(“假 null”)。 + if (m_Table != null) m_Table.EndSelection(); } } } diff --git a/Runtime/XTable/Rendering/Component/XTableSelectionOverlay.cs b/Runtime/XTable/Rendering/Component/XTableSelectionOverlay.cs index dff91dc..7e9a357 100644 --- a/Runtime/XTable/Rendering/Component/XTableSelectionOverlay.cs +++ b/Runtime/XTable/Rendering/Component/XTableSelectionOverlay.cs @@ -17,6 +17,7 @@ namespace XericUI.XTable.Rendering.Component public void Configure(XericUIActionTable table, int rowStart, int rowEnd, int colStart, int colEnd) { + if (table == null || this == null) return; m_Table = table; m_RowStart = rowStart; m_RowEnd = rowEnd; @@ -35,7 +36,11 @@ namespace XericUI.XTable.Rendering.Component public void RefreshVisual() { + // 防御:覆盖层自身可能已被销毁(“假 null”),此处的 Unity == 判空直接拦截, + // 防止继续走到 GetComponent 原生层抛 NullReferenceException。 + if (this == null) return; EnsureVisuals(); + if (m_Fill == null) return; if (m_Table == null || !m_Table.SelectionHandle.HasSelection || m_RowStart >= m_RowEnd || m_ColStart >= m_ColEnd) { m_Fill.gameObject.SetActive(false); @@ -63,7 +68,11 @@ namespace XericUI.XTable.Rendering.Component private void EnsureVisuals() { + if (this == null) return; + // Image 组件理论上由 RequireComponent 保证,但外部代码可能移除它; + // GetComponent 缺失时返回 null,直接补一个,避免 input.color 空引用。 var input = GetComponent(); + if (input == null) input = gameObject.AddComponent(); input.color = Color.clear; input.raycastTarget = false; for (int i = transform.childCount - 1; i >= 0; i--) diff --git a/Runtime/XTable/Rendering/Component/XericUIActionTable.Freeze.cs b/Runtime/XTable/Rendering/Component/XericUIActionTable.Freeze.cs index f17ebc8..f02d40b 100644 --- a/Runtime/XTable/Rendering/Component/XericUIActionTable.Freeze.cs +++ b/Runtime/XTable/Rendering/Component/XericUIActionTable.Freeze.cs @@ -171,7 +171,10 @@ namespace XericUI.XTable.Rendering.Component private XTableSelectionOverlay CreateOrUpdateSelectionOverlay(Transform parent, XTableSelectionOverlay overlay, int rowStart, int rowEnd, int colStart, int colEnd, string name) { - if (parent == null) return overlay; + // 父容器已销毁:旧引用是“假 null”,直接返回 null 让调用方清空字段, + // 避免把已销毁引用写回字段后在 RefreshSelectionOverlays 中崩溃。 + if (parent == null) return null; + // Unity 的 == 能识别已销毁覆盖层的“假 null”,此处会正确重建。 if (overlay == null) { var go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(UnityEngine.UI.Image), typeof(XTableSelectionOverlay)); @@ -179,6 +182,7 @@ namespace XericUI.XTable.Rendering.Component go.transform.SetParent(parent, false); overlay = go.GetComponent(); } + if (overlay == null) return null; // Overlay 必须处于当前区域最上层,才能先于边框和其他 Graphic 接收 PointerDown/Drag。 overlay.Configure(this, rowStart, rowEnd, colStart, colEnd); overlay.transform.SetAsLastSibling(); @@ -187,10 +191,12 @@ namespace XericUI.XTable.Rendering.Component private void RefreshSelectionOverlays() { - m_NormalSelectionOverlay?.RefreshVisual(); - m_FrozenColumnSelectionOverlay?.RefreshVisual(); - m_FrozenRowSelectionOverlay?.RefreshVisual(); - m_FrozenCornerSelectionOverlay?.RefreshVisual(); + // 注意:不能用 ?. —— C# 的 ?. 只检查托管 null,识别不了 Unity 已销毁对象的“假 null”; + // 对已销毁的覆盖层调用 RefreshVisual 会在 GetComponent 原生层抛 NullReferenceException。 + if (m_NormalSelectionOverlay != null) m_NormalSelectionOverlay.RefreshVisual(); + if (m_FrozenColumnSelectionOverlay != null) m_FrozenColumnSelectionOverlay.RefreshVisual(); + if (m_FrozenRowSelectionOverlay != null) m_FrozenRowSelectionOverlay.RefreshVisual(); + if (m_FrozenCornerSelectionOverlay != null) m_FrozenCornerSelectionOverlay.RefreshVisual(); } private void SyncFrozenContainers() diff --git a/Runtime/XTable/Rendering/Component/XericUIActionTable.UpdateScope.cs b/Runtime/XTable/Rendering/Component/XericUIActionTable.UpdateScope.cs index 944fef8..dee337f 100644 --- a/Runtime/XTable/Rendering/Component/XericUIActionTable.UpdateScope.cs +++ b/Runtime/XTable/Rendering/Component/XericUIActionTable.UpdateScope.cs @@ -106,7 +106,8 @@ namespace XericUI.XTable.Rendering.Component { if (m_Disposed) return; m_Disposed = true; - m_Table?.EndCheckUpdate(m_Token); + // 用 Unity 判空而非 ?.:表格可能已被外部销毁(“假 null”),?. 仍会调用其方法。 + if (m_Table != null) m_Table.EndCheckUpdate(m_Token); m_Table = null; } }