表格设置单元格为预制体的情况下不会刷新这个预制体,因为条件写反了。

This commit is contained in:
2026-07-30 14:58:12 +08:00
parent a5616b6a06
commit f7931d41f1
8 changed files with 272 additions and 223 deletions
@@ -90,22 +90,13 @@ namespace XericUI.XTable.Rendering.Component
sc = Mathf.Max(sc, StartCol);
er = Mathf.Min(er, EndRow > 0 ? EndRow : rows.Length);
ec = Mathf.Min(ec, EndCol > 0 ? EndCol : cols.Length);
if (sr >= er || sc >= ec)
{
if (fullRefresh) ReleaseAll();
return;
}
if (sr >= er || sc >= ec) return;
var target = new HashSet<(int, int)>();
for (int r = sr; r < er; r++)
for (int c = sc; c < ec; c++)
target.Add((r, c));
if (fullRefresh)
ReleaseAll();
else
ReleaseExcept(target);
foreach (var cell in target)
if (fullRefresh || !m_Rendered.Contains(cell))
RenderCell(cell.Item1, cell.Item2, rows, cols, editorMode);
@@ -122,18 +113,6 @@ namespace XericUI.XTable.Rendering.Component
m_Rendered.Clear();
}
private void ReleaseExcept(HashSet<(int, int)> target)
{
var remove = new List<(int, int)>();
foreach (var pair in m_Tracked)
if (!target.Contains(pair.Key)) remove.Add(pair.Key);
foreach (var key in remove)
{
ReleaseTracked(m_Tracked[key]);
m_Tracked.Remove(key);
}
}
private void ReleaseTracked(Tracked tracked)
{
Assembler.ReleaseText(tracked.Text);
@@ -191,15 +170,20 @@ namespace XericUI.XTable.Rendering.Component
}
else if (cell.ContentType == CellContentType.Prefab && cell.Prefab != null && PrefabPool != null)
{
var prefab = PrefabPool.Acquire(cell.Prefab, cellId, m_Parent);
var key = (row, col);
GameObject activePrefab = null;
bool wasActive = ActivePrefabMap != null
&& ActivePrefabMap.TryGetValue(key, out activePrefab);
var prefab = wasActive ? activePrefab : PrefabPool.Acquire(cell.Prefab, cellId, m_Parent);
if (prefab != null)
{
var rt = prefab.GetComponent<RectTransform>();
rt.anchoredPosition = new Vector2(x, y);
rt.sizeDelta = new Vector2(width, height);
if (ActivePrefabMap != null)
ActivePrefabMap[(row, col)] = prefab;
cell.OnPrefabAcquire?.Invoke(prefab);
ActivePrefabMap[key] = prefab;
if (!wasActive)
cell.OnPrefabAcquire?.Invoke(prefab);
}
}
}
@@ -452,7 +452,9 @@ namespace XericUI.XTable.Rendering.Component
public void RemoveRow(int row)
{
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.RemoveRow, row))) return;
var prefabCallbacks = CaptureActivePrefabReleaseCallbacks();
RemoveRowImmediate(row);
RebuildRenderBindings(prefabCallbacks);
}
private void RemoveRowImmediate(int row)
@@ -509,7 +511,9 @@ namespace XericUI.XTable.Rendering.Component
public void RemoveColumn(int col)
{
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.RemoveColumn, col: col))) return;
var prefabCallbacks = CaptureActivePrefabReleaseCallbacks();
RemoveColumnImmediate(col);
RebuildRenderBindings(prefabCallbacks);
}
private void RemoveColumnImmediate(int col)
@@ -762,26 +766,16 @@ namespace XericUI.XTable.Rendering.Component
public void ClearData()
{
ThrowIfCheckingUpdate(nameof(ClearData));
var prefabCallbacks = CaptureActivePrefabReleaseCallbacks();
if (m_TableData != null)
{
// 清理预制体:先调用 OnPrefabRelease 再清理映射
if (m_ActivePrefabMap != null)
{
foreach (var kv in m_ActivePrefabMap)
{
var cellData = m_TableData.GetCell(kv.Key.Item1, kv.Key.Item2);
cellData?.OnPrefabRelease?.Invoke(kv.Value);
}
m_ActivePrefabMap.Clear();
}
m_PrefabPool?.ReturnAll();
m_TableData.ClearAllMerges();
m_TableData.BlockMap.Clear();
m_StyleNamespaceDirty = true;
}
m_Quadtree?.Clear();
ClearSelection();
RebuildRenderBindings(prefabCallbacks);
MarkDirty(TableDirtyType.DataChanged);
RefreshView();
}
@@ -54,6 +54,7 @@ namespace XericUI.XTable.Rendering.Component
{
Assembler = m_Assembler,
PrefabPool = m_PrefabPool,
ActivePrefabMap = m_ActivePrefabMap,
CellPadding = CellPadding,
Config = Config,
SelectionTable = this,
@@ -19,11 +19,12 @@ namespace XericUI.XTable.Rendering.Component
/// <summary>可复用列表,避免每帧分配 GC</summary>
[NonSerialized] private List<(int, int)> m_ReusableCellList = new(capacity: 128);
[NonSerialized] private List<QuadrantResult> m_ReusableQuadrants = new(capacity: 32);
/// <summary>控制滚动时是否使用四叉树(大表格推荐)</summary>
private bool UseQuadtreeForScroll => m_Quadtree != null && m_Quadtree.IsBuilt
&& m_RowHeights.Length * m_ColWidths.Length > 2000;
&& m_RowHeights.Length * m_ColWidths.Length > 2000;
private struct CellTrackedObjs
{
@@ -52,6 +53,7 @@ namespace XericUI.XTable.Rendering.Component
m_Assembler?.ReleaseText(objs.CellText);
m_Assembler?.ReleaseImage(objs.CellImage);
}
m_CellTracked.Clear();
m_LastRenderedCells.Clear();
}
@@ -212,13 +214,19 @@ namespace XericUI.XTable.Rendering.Component
{
if (data.Prefab == null) return;
GameObject instance = m_PrefabPool.Acquire(data.Prefab, cellId);
var key = (row, col);
GameObject instance = null;
bool wasActive = m_ActivePrefabMap != null
&& m_ActivePrefabMap.TryGetValue(key, out instance);
if (!wasActive)
instance = m_PrefabPool.Acquire(data.Prefab, cellId);
if (instance == null) return;
RectTransform prt = instance.GetComponent<RectTransform>();
prt.anchoredPosition = new Vector2(cellX, cellY);
prt.sizeDelta = new Vector2(cellW, cellH);
m_ActivePrefabMap[(row, col)] = instance;
data.OnPrefabAcquire?.Invoke(instance);
m_ActivePrefabMap[key] = instance;
if (!wasActive)
data.OnPrefabAcquire?.Invoke(instance);
}
#endregion
@@ -227,6 +235,8 @@ namespace XericUI.XTable.Rendering.Component
private void ReleaseRenderedBindings(Dictionary<GameObject, System.Action<GameObject>> prefabReleaseCallbacks = null)
{
// 结构重建必须解除所有 cellId 映射;不能只依赖当前可见区域的 tracked 集合。
m_Assembler?.ReleaseAllBindings();
ReleaseAllTrackedCells();
ReleaseFrozenCells();
m_LastRenderedCells.Clear();
@@ -238,6 +248,12 @@ namespace XericUI.XTable.Rendering.Component
m_ActivePrefabMap?.Clear();
}
/// <summary>结构删除后的唯一绑定重建入口;普通刷新和滚动不得调用。</summary>
private void RebuildRenderBindings(Dictionary<GameObject, System.Action<GameObject>> prefabReleaseCallbacks)
{
ReleaseRenderedBindings(prefabReleaseCallbacks);
}
public void RefreshView(bool releaseBindings = true)
{
TableDirtyType refreshedFlags = m_DirtyFlag.Flags;
@@ -271,9 +287,6 @@ namespace XericUI.XTable.Rendering.Component
UpdateBackground();
if (releaseBindings)
ReleaseRenderedBindings(CaptureActivePrefabReleaseCallbacks());
// 清理孤立子对象
CleanOrphanChildren();
@@ -334,32 +347,9 @@ namespace XericUI.XTable.Rendering.Component
var newVisible = new HashSet<(int, int)>();
ComputeVisibleCellsScrollInto(newVisible);
// 2. 回收不再可见的追踪对象
m_ReusableCellList.Clear();
foreach (var cell in m_LastRenderedCells)
if (!newVisible.Contains(cell))
m_ReusableCellList.Add(cell);
foreach (var cell in m_ReusableCellList)
ReleaseTrackedCell(cell.Item1, cell.Item2);
// 2. 离屏单元格保持其绑定,供后续重绘直接复用。
// 3. 回收不再可见的预制体(仅从映射表移除,预制体保留在计数器槽位中复用)
if (m_ActivePrefabMap != null && m_ActivePrefabMap.Count > 0)
{
m_ReusableCellList.Clear();
foreach (var kv in m_ActivePrefabMap)
if (!newVisible.Contains(kv.Key))
m_ReusableCellList.Add(kv.Key);
foreach (var key in m_ReusableCellList)
{
GameObject instance = m_ActivePrefabMap[key];
var data = TableData.GetCell(key.Item1, key.Item2);
m_PrefabPool?.Release(instance, data?.OnPrefabRelease);
m_ActivePrefabMap.Remove(key);
}
}
// 4. 渲染单元格:选区变更时全量重渲所有可见单元格,否则仅新进入的
// 3. 渲染单元格:选区变更时全量重渲所有可见单元格,否则仅新进入的
foreach (var cell in newVisible)
{
if (!m_LastRenderedCells.Contains(cell))
@@ -411,7 +401,8 @@ namespace XericUI.XTable.Rendering.Component
if (m_BackgroundRenderer != null) return;
var backgroundGo = new GameObject("_TableBackground",
typeof(RectTransform), typeof(CanvasRenderer), typeof(XTableBackgroundRenderer), typeof(XTableSelectionInput));
typeof(RectTransform), typeof(CanvasRenderer), typeof(XTableBackgroundRenderer),
typeof(XTableSelectionInput));
backgroundGo.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;
backgroundGo.transform.SetParent(ContentTransform, false);
backgroundGo.transform.SetAsFirstSibling();
@@ -451,7 +442,8 @@ namespace XericUI.XTable.Rendering.Component
{
if (string.IsNullOrEmpty(quadrant.Namespace) || quadrant.Namespace == baseNamespace)
continue;
rects.Add(new XTableBackgroundRenderer.RectData(quadrant.Region, GetCellBgColor(quadrant.Namespace)));
rects.Add(new XTableBackgroundRenderer.RectData(quadrant.Region,
GetCellBgColor(quadrant.Namespace)));
}
}
}
@@ -474,4 +466,4 @@ namespace XericUI.XTable.Rendering.Component
#endregion
}
}
}
@@ -72,17 +72,20 @@ namespace XericUI.XTable.Rendering.Component
private sealed class UpdateContext
{
public TableDirtyType DirtyFlags;
public bool RequiresBindingRebuild;
public readonly List<UpdateCommand> Commands = new();
public void Reset()
{
DirtyFlags = TableDirtyType.None;
RequiresBindingRebuild = false;
Commands.Clear();
}
public void MergeFrom(UpdateContext child)
{
DirtyFlags |= child.DirtyFlags;
RequiresBindingRebuild |= child.RequiresBindingRebuild;
Commands.AddRange(child.Commands);
}
}
@@ -134,7 +137,10 @@ namespace XericUI.XTable.Rendering.Component
private bool TryEnqueueUpdateCommand(UpdateCommand command)
{
if (!IsCheckingUpdate) return false;
m_UpdateContextStack.Peek().Commands.Add(command);
UpdateContext context = m_UpdateContextStack.Peek();
context.Commands.Add(command);
context.RequiresBindingRebuild |= command.Type == UpdateCommandType.RemoveRow
|| command.Type == UpdateCommandType.RemoveColumn;
return true;
}
@@ -158,12 +164,14 @@ namespace XericUI.XTable.Rendering.Component
m_UpdateContextStack.Push(context);
try
{
Dictionary<GameObject, Action<GameObject>> prefabCallbacks = CaptureActivePrefabReleaseCallbacks();
Dictionary<GameObject, Action<GameObject>> prefabCallbacks = context.RequiresBindingRebuild
? CaptureActivePrefabReleaseCallbacks() : null;
ExecuteUpdateCommands(context);
if (context.DirtyFlags == TableDirtyType.None) return;
m_UpdateContextStack.Pop();
ReleaseRenderedBindings(prefabCallbacks);
if (context.RequiresBindingRebuild)
RebuildRenderBindings(prefabCallbacks);
ApplyDirty(context.DirtyFlags);
RefreshView(false);
RefreshSelectionOverlays();