添加按比例缩放的表格计算逻辑,使用小于等于1的单元格尺寸来获取相对容器的尺寸

This commit is contained in:
2026-08-03 17:34:44 +08:00
parent f7931d41f1
commit 29c7c4e81e
10 changed files with 126 additions and 54 deletions
@@ -75,5 +75,11 @@ namespace XericUI.XTable.Mapping
int localCol = col % blockSizeX;
return localRow * blockSizeX + localCol;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ResolveSize(float rawSize, float axisBaseSize)
{
return rawSize > 0f && rawSize <= 1f ? rawSize * axisBaseSize : rawSize;
}
}
}
@@ -339,7 +339,7 @@ namespace XericUI.XTable.Rendering.Component
=> TableData.IsMergeSource(row, col, out rowSpan, out colSpan);
/// <summary>
/// 在末尾追加一行,默认行高来自 Config。
/// 在末尾追加一行,默认行高来自 Config。尺寸在 (0, 1] 时按 viewport、父级或自身高度比例解析,> 1 时为像素。
/// 行为:普通调用立即扩展行数组;CheckUpdate 内记录追加,结束时只重建一次。
/// 建议:循环追加或随后填充新行时使用 CheckUpdate。
/// </summary>
@@ -367,7 +367,7 @@ namespace XericUI.XTable.Rendering.Component
}
/// <summary>
/// 在末尾追加一列,默认列宽来自 Config。
/// 在末尾追加一列,默认列宽来自 Config。尺寸在 (0, 1] 时按 viewport、父级或自身宽度比例解析,> 1 时为像素。
/// 行为:普通调用立即扩展列数组;CheckUpdate 内记录追加,结束时只重建一次。
/// 建议:循环追加或随后填充新列时使用 CheckUpdate。
/// </summary>
@@ -395,7 +395,7 @@ namespace XericUI.XTable.Rendering.Component
}
/// <summary>
/// 设置指定行的高度。
/// 设置指定行的高度。尺寸在 (0, 1] 时按 viewport、父级或自身高度比例解析,> 1 时为像素。
/// 行为:普通调用立即更新布局;CheckUpdate 内延后,最终按命令顺序应用。
/// 建议:批量调整行高时使用 CheckUpdate。
/// </summary>
@@ -420,7 +420,7 @@ namespace XericUI.XTable.Rendering.Component
}
/// <summary>
/// 设置指定列的宽度。
/// 设置指定列的宽度。尺寸在 (0, 1] 时按 viewport、父级或自身宽度比例解析,> 1 时为像素。
/// 行为:普通调用立即更新布局;CheckUpdate 内延后,最终按命令顺序应用。
/// 建议:批量调整列宽时使用 CheckUpdate。
/// </summary>
@@ -672,6 +672,7 @@ namespace XericUI.XTable.Rendering.Component
m_Assembler = new CellAssembler(ContentTransform, m_DefaultStyleNamespace);
m_PrefabPool = new PrefabPrototypePool(ContentTransform);
m_ActivePrefabMap = new Dictionary<(int, int), GameObject>();
UpdateResolvedCellSizes();
RecalculateTotalSize();
CreateDefaultSampleData();
CreateBackground();
@@ -732,6 +733,7 @@ namespace XericUI.XTable.Rendering.Component
m_Assembler = new CellAssembler(ContentTransform, m_DefaultStyleNamespace);
m_PrefabPool = new PrefabPrototypePool(ContentTransform);
m_ActivePrefabMap = new Dictionary<(int, int), GameObject>();
UpdateResolvedCellSizes();
RecalculateTotalSize();
// 恢复文本数据到新 TableData
@@ -820,6 +822,7 @@ namespace XericUI.XTable.Rendering.Component
m_PrefabPool = new PrefabPrototypePool(ContentTransform);
m_ActivePrefabMap = new Dictionary<(int, int), GameObject>();
MarkDirty(TableDirtyType.All);
UpdateResolvedCellSizes();
RecalculateTotalSize();
CreateBackground();
CreateViewportRenderer();
@@ -881,6 +884,7 @@ namespace XericUI.XTable.Rendering.Component
// 写回单元格文本与合并信息
data.PopulateToTable(this);
UpdateResolvedCellSizes();
RecalculateTotalSize();
RequestRefresh(TableDirtyType.DataChanged | TableDirtyType.LayoutChanged, true);
SetChildrenVisible(m_ChildrenVisible);
@@ -18,9 +18,9 @@ namespace XericUI.XTable.Rendering.Component
{
// 计入 m_ContentOffset 以匹配单元格在 Content 中的实际渲染位置
float cellLeft = GetColLeftX(col) + m_ContentOffset;
float cellRight = cellLeft + m_ColWidths[col];
float cellRight = cellLeft + m_ResolvedColWidths[col];
float cellTop = GetRowTopY(row) + m_ContentOffset;
float cellBottom = cellTop + m_RowHeights[row];
float cellBottom = cellTop + m_ResolvedRowHeights[row];
return cellRight > m_VisibleRect.x && cellLeft < m_VisibleRect.xMax &&
cellBottom > m_VisibleRect.y && cellTop < m_VisibleRect.yMax;
@@ -43,16 +43,16 @@ namespace XericUI.XTable.Rendering.Component
private float GetRowTopY(int row)
{
float y = 0;
for (int i = 0; i < row && i < m_RowHeights.Length; i++)
y += m_RowHeights[i];
for (int i = 0; i < row && i < m_ResolvedRowHeights.Length; i++)
y += m_ResolvedRowHeights[i];
return y;
}
private float GetColLeftX(int col)
{
float x = 0;
for (int i = 0; i < col && i < m_ColWidths.Length; i++)
x += m_ColWidths[i];
for (int i = 0; i < col && i < m_ResolvedColWidths.Length; i++)
x += m_ResolvedColWidths[i];
return x;
}
@@ -83,13 +83,13 @@ namespace XericUI.XTable.Rendering.Component
private void RecalculateTotalSize()
{
m_TotalWidth = 0;
if (m_ColWidths != null)
foreach (float w in m_ColWidths)
if (m_ResolvedColWidths != null)
foreach (float w in m_ResolvedColWidths)
m_TotalWidth += w;
m_TotalHeight = 0;
if (m_RowHeights != null)
foreach (float h in m_RowHeights)
if (m_ResolvedRowHeights != null)
foreach (float h in m_ResolvedRowHeights)
m_TotalHeight += h;
m_ContentOffset = GetCellBorderWidth(m_DefaultStyleNamespace) * 0.5f;
@@ -125,9 +125,9 @@ namespace XericUI.XTable.Rendering.Component
ec = 0;
float accY = 0;
for (int r = 0; r < m_RowHeights.Length; r++)
for (int r = 0; r < m_ResolvedRowHeights.Length; r++)
{
float rowBottom = accY + m_RowHeights[r];
float rowBottom = accY + m_ResolvedRowHeights[r];
if (rowBottom > m_VisibleRect.y && accY < m_VisibleRect.yMax)
{
if (er == 0 && sr == 0) sr = r;
@@ -139,9 +139,9 @@ namespace XericUI.XTable.Rendering.Component
}
float accX = 0;
for (int c = 0; c < m_ColWidths.Length; c++)
for (int c = 0; c < m_ResolvedColWidths.Length; c++)
{
float colRight = accX + m_ColWidths[c];
float colRight = accX + m_ResolvedColWidths[c];
if (colRight > m_VisibleRect.x && accX < m_VisibleRect.xMax)
{
if (ec == 0 && sc == 0) sc = c;
@@ -233,7 +233,7 @@ namespace XericUI.XTable.Rendering.Component
cells.Add(new XTableQuadtreeCell { Row = r, Col = c, Namespace = ResolveCellNamespace(data) });
}
m_Quadtree.Build(m_TotalWidth, m_TotalHeight, m_RowHeights, m_ColWidths, cells, Config.QuadtreeMaxCellsPerNode);
m_Quadtree.Build(m_TotalWidth, m_TotalHeight, m_ResolvedRowHeights, m_ResolvedColWidths, cells, Config.QuadtreeMaxCellsPerNode);
m_QuadtreeDirty = false;
}
@@ -264,6 +264,7 @@ namespace XericUI.XTable.Rendering.Component
}
// 标记脏并刷新
UpdateResolvedCellSizes();
RecalculateTotalSize();
RequestRefresh(TableDirtyType.DataChanged | TableDirtyType.LayoutChanged, true);
}
@@ -61,8 +61,8 @@ namespace XericUI.XTable.Rendering.Component
GetStyleColor = (ns, path, fallback) => GetStyleColor(ns, path, fallback),
GetStyleInt = (ns, path, fallback) => GetStyleInt(ns, path, fallback),
GetStyleFontAsset = (ns, path) => GetStyleFontAsset(ns, path),
GetRowHeights = () => m_RowHeights,
GetColWidths = () => m_ColWidths,
GetRowHeights = () => m_ResolvedRowHeights,
GetColWidths = () => m_ResolvedColWidths,
GetTableData = () => m_TableData,
GetDefaultStyleNamespace = () => m_DefaultStyleNamespace,
};
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using XericLibrary.Runtime.MacroLibrary;
using XericUI.XTable.Core;
using XericUI.XTable.Rendering.Elements;
#if UNITY_EDITOR
@@ -134,16 +135,16 @@ namespace XericUI.XTable.Rendering.Component
float cellX = GetCellContentX(col);
float cellY = GetCellContentY(row);
float cellW = m_ColWidths[col];
float cellH = m_RowHeights[row];
float cellW = m_ResolvedColWidths[col];
float cellH = m_ResolvedRowHeights[row];
// 合并源:扩展尺寸
if (TableData.IsMergeSource(row, col, out int mergeRS, out int mergeCS))
{
for (int i = 1; i < mergeCS && col + i < m_ColWidths.Length; i++)
cellW += m_ColWidths[col + i];
for (int i = 1; i < mergeRS && row + i < m_RowHeights.Length; i++)
cellH += m_RowHeights[row + i];
for (int i = 1; i < mergeCS && col + i < m_ResolvedColWidths.Length; i++)
cellW += m_ResolvedColWidths[col + i];
for (int i = 1; i < mergeRS && row + i < m_ResolvedRowHeights.Length; i++)
cellH += m_ResolvedRowHeights[row + i];
}
int cellId = row * m_ColWidths.Length + col;
@@ -267,6 +268,7 @@ namespace XericUI.XTable.Rendering.Component
if (m_RowHeights == null || m_ColWidths == null) return;
if (m_RowHeights.Length == 0 || m_ColWidths.Length == 0) return;
UpdateResolvedCellSizes();
CreateBackground();
#if UNITY_EDITOR
@@ -70,12 +70,12 @@ namespace XericUI.XTable.Rendering.Component
internal bool TryGetCellFromTablePoint(Vector2 point, bool clamp, out int row, out int col)
{
row = col = -1;
if (m_RowHeights == null || m_ColWidths == null || m_RowHeights.Length == 0 || m_ColWidths.Length == 0) return false;
if (m_ResolvedRowHeights == null || m_ResolvedColWidths == null || m_ResolvedRowHeights.Length == 0 || m_ResolvedColWidths.Length == 0) return false;
if (clamp) { point.x = Mathf.Clamp(point.x, 0, Mathf.Max(0, m_TotalWidth - .001f)); point.y = Mathf.Clamp(point.y, 0, Mathf.Max(0, m_TotalHeight - .001f)); }
else if (point.x < 0 || point.y < 0 || point.x >= m_TotalWidth || point.y >= m_TotalHeight) return false;
float cursor = 0; for (col = 0; col < m_ColWidths.Length && point.x >= (cursor += m_ColWidths[col]); col++) { }
cursor = 0; for (row = 0; row < m_RowHeights.Length && point.y >= (cursor += m_RowHeights[row]); row++) { }
if (row >= m_RowHeights.Length || col >= m_ColWidths.Length) return false;
float cursor = 0; for (col = 0; col < m_ResolvedColWidths.Length && point.x >= (cursor += m_ResolvedColWidths[col]); col++) { }
cursor = 0; for (row = 0; row < m_ResolvedRowHeights.Length && point.y >= (cursor += m_ResolvedRowHeights[row]); row++) { }
if (row >= m_ResolvedRowHeights.Length || col >= m_ResolvedColWidths.Length) return false;
if (m_TableData != null && m_TableData.TryGetMergeRect(row, col, out int sr, out int sc, out _, out _)) { row = sr; col = sc; }
return true;
}
@@ -93,7 +93,7 @@ namespace XericUI.XTable.Rendering.Component
float freezeWidth = GetColLeftX(Mathf.Clamp(m_FreezeColCount, 0, m_ColWidths.Length));
float freezeHeight = GetRowTopY(Mathf.Clamp(m_FreezeRowCount, 0, m_RowHeights.Length));
if (x < freezeWidth || y < freezeHeight) return;
float width = rect.width - freezeWidth, height = rect.height - freezeHeight;
float width = Mathf.Max(0f, rect.width - freezeWidth), height = Mathf.Max(0f, rect.height - freezeHeight);
float threshold = Mathf.Min(m_SelectionAutoScrollEdgeThreshold, Mathf.Min(width, height) * .5f);
float sx = GetAutoScrollSpeed(x - freezeWidth, width, threshold), sy = GetAutoScrollSpeed(y - freezeHeight, height, threshold);
if (Mathf.Approximately(sx, 0) && Mathf.Approximately(sy, 0)) return;
@@ -106,6 +106,7 @@ namespace XericUI.XTable.Rendering.Component
private float GetAutoScrollSpeed(float point, float length, float threshold)
{
if (threshold <= 0f) return 0f;
float direction = point < threshold ? -1f : point > length - threshold ? 1f : 0f;
if (direction == 0f) return 0f;
float distance = direction < 0f ? point : length - point;
@@ -108,6 +108,10 @@ namespace XericUI.XTable.Rendering.Component
[System.NonSerialized] private UILineRendererV2 m_FrozenRowBorderRenderer;
[System.NonSerialized] private UILineRendererV2 m_FrozenCornerBorderRenderer;
[NonSerialized] private float[] m_ResolvedRowHeights;
[NonSerialized] private float[] m_ResolvedColWidths;
[NonSerialized] private Vector2 m_LastLayoutBaseSize;
private float m_TotalWidth;
private float m_TotalHeight;
@@ -147,6 +151,8 @@ namespace XericUI.XTable.Rendering.Component
public float[] RowHeights => m_RowHeights;
public float[] ColWidths => m_ColWidths;
public float[] ResolvedRowHeights => m_ResolvedRowHeights;
public float[] ResolvedColWidths => m_ResolvedColWidths;
/// <summary>冻结行数</summary>
public int FreezeRowCount => m_FreezeRowCount;
@@ -278,6 +284,7 @@ namespace XericUI.XTable.Rendering.Component
m_Assembler = new CellAssembler(ContentTransform, m_DefaultStyleNamespace);
m_PrefabPool = new PrefabPrototypePool(ContentTransform);
m_ActivePrefabMap = new Dictionary<(int, int), GameObject>();
UpdateResolvedCellSizes();
RecalculateTotalSize();
EnsureDefaultStyleNamespace();
CreateDefaultSampleData();
@@ -410,6 +417,51 @@ namespace XericUI.XTable.Rendering.Component
[System.NonSerialized] private bool m_IsRefreshingFromScroll;
private void OnRectTransformDimensionsChange()
{
Vector2 layoutBaseSize = GetLayoutBaseSize();
if (layoutBaseSize != m_LastLayoutBaseSize)
{
m_LastLayoutBaseSize = layoutBaseSize;
MarkDirty(TableDirtyType.LayoutChanged);
}
}
private Vector2 GetLayoutBaseSize()
{
RectTransform baseTransform = m_ScrollRect != null && m_ScrollRect.viewport != null
? m_ScrollRect.viewport
: transform.parent as RectTransform;
if (baseTransform == null) baseTransform = rectTransform;
Vector2 size = baseTransform != null ? baseTransform.rect.size : Vector2.zero;
Vector2 selfSize = rectTransform != null ? rectTransform.rect.size : Vector2.zero;
return new Vector2(size.x > 0f ? size.x : (m_LastLayoutBaseSize.x > 0f ? m_LastLayoutBaseSize.x : selfSize.x),
size.y > 0f ? size.y : (m_LastLayoutBaseSize.y > 0f ? m_LastLayoutBaseSize.y : selfSize.y));
}
private void UpdateResolvedCellSizes()
{
Vector2 baseSize = GetLayoutBaseSize();
m_LastLayoutBaseSize = baseSize;
if (m_RowHeights == null || m_ColWidths == null)
{
m_ResolvedRowHeights = null;
m_ResolvedColWidths = null;
return;
}
if (m_ResolvedRowHeights == null || m_ResolvedRowHeights.Length != m_RowHeights.Length)
m_ResolvedRowHeights = new float[m_RowHeights.Length];
if (m_ResolvedColWidths == null || m_ResolvedColWidths.Length != m_ColWidths.Length)
m_ResolvedColWidths = new float[m_ColWidths.Length];
for (int i = 0; i < m_RowHeights.Length; i++)
m_ResolvedRowHeights[i] = XTableCoordinateUtility.ResolveSize(m_RowHeights[i], baseSize.y);
for (int i = 0; i < m_ColWidths.Length; i++)
m_ResolvedColWidths[i] = XTableCoordinateUtility.ResolveSize(m_ColWidths[i], baseSize.x);
}
private void OnScrollValueChanged(Vector2 _)
{
if (IsCheckingUpdate) return;