com.alicizax.unity.ui.exten.../Runtime/RecyclerView/Layout/MixedLayoutManager.cs
陈思海 dc8c840d69 RecyclerView 大优化
优化RecycleView 渲染架构
优化RecyclerView 渲染性能 增加 缓存 异步
增加Navagation导航锚点相关
2026-03-31 15:18:50 +08:00

244 lines
7.0 KiB
C#

using System;
using UnityEngine;
namespace AlicizaX.UI
{
[Serializable]
public class MixedLayoutManager : LayoutManager
{
private float[] itemLengths = Array.Empty<float>();
private float[] itemPositions = Array.Empty<float>();
private Vector2 firstItemSize = Vector2.zero;
private int cachedItemCount = -1;
private bool positionCacheDirty = true;
public MixedLayoutManager() { }
public override Vector2 CalculateContentSize()
{
positionCacheDirty = true;
EnsurePositionCache();
float totalLength = cachedItemCount > 0
? itemPositions[cachedItemCount - 1] + itemLengths[cachedItemCount - 1]
: 0f;
float paddingLength = direction == Direction.Vertical ? padding.y * 2 : padding.x * 2;
return direction == Direction.Vertical
? new Vector2(contentSize.x, cachedItemCount > 0 ? totalLength + paddingLength : paddingLength)
: new Vector2(cachedItemCount > 0 ? totalLength + paddingLength : paddingLength, contentSize.y);
}
public override Vector2 CalculatePosition(int index)
{
EnsurePositionCache();
float position = GetItemPosition(index) - ScrollPosition;
return direction == Direction.Vertical
? new Vector2(0, position + padding.y)
: new Vector2(position + padding.x, 0);
}
public override Vector2 CalculateContentOffset()
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return Vector2.zero;
}
float len = GetFitContentSize();
if (direction == Direction.Vertical)
{
return new Vector2(0, (len - firstItemSize.y) / 2);
}
return new Vector2((len - firstItemSize.x) / 2, 0);
}
public override Vector2 CalculateViewportOffset()
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return Vector2.zero;
}
if (direction == Direction.Vertical)
{
return new Vector2(0, (viewportSize.y - firstItemSize.y) / 2);
}
return new Vector2((viewportSize.x - firstItemSize.x) / 2, 0);
}
public override int GetStartIndex()
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return 0;
}
int index = FindFirstItemEndingAfter(ScrollPosition);
return index >= 0 ? index : 0;
}
public override int GetEndIndex()
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return -1;
}
float viewLength = direction == Direction.Vertical ? viewportSize.y : viewportSize.x;
int index = FindFirstItemEndingAfter(ScrollPosition + viewLength);
return index >= 0 ? Mathf.Min(index, cachedItemCount - 1) : cachedItemCount - 1;
}
public override float IndexToPosition(int index)
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return 0f;
}
float position = GetItemPosition(index);
if (direction == Direction.Vertical)
{
return Mathf.Clamp(position, 0f, Mathf.Max(contentSize.y - viewportSize.y, 0f));
}
return Mathf.Clamp(position, 0f, Mathf.Max(contentSize.x - viewportSize.x, 0f));
}
public override int PositionToIndex(float position)
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return 0;
}
int index = FindFirstItemEndingAtOrAfter(position);
return index >= 0 ? index : cachedItemCount - 1;
}
private void EnsurePositionCache()
{
int itemCount = adapter != null ? adapter.GetItemCount() : 0;
if (itemCount < 0)
{
itemCount = 0;
}
if (!positionCacheDirty && cachedItemCount == itemCount)
{
return;
}
RebuildPositionCache(itemCount);
}
private void RebuildPositionCache(int itemCount)
{
if (itemLengths.Length != itemCount)
{
itemLengths = itemCount > 0 ? new float[itemCount] : Array.Empty<float>();
itemPositions = itemCount > 0 ? new float[itemCount] : Array.Empty<float>();
}
firstItemSize = itemCount > 0 ? viewProvider.CalculateViewSize(0) : Vector2.zero;
float position = 0f;
for (int i = 0; i < itemCount; i++)
{
itemPositions[i] = position;
itemLengths[i] = GetLength(i, itemCount);
position += itemLengths[i];
}
cachedItemCount = itemCount;
positionCacheDirty = false;
}
private float GetItemPosition(int index)
{
if (index <= 0 || cachedItemCount <= 0)
{
return 0f;
}
if (index >= cachedItemCount)
{
return itemPositions[cachedItemCount - 1] + itemLengths[cachedItemCount - 1];
}
return itemPositions[index];
}
private int FindFirstItemEndingAfter(float position)
{
int low = 0;
int high = cachedItemCount - 1;
int result = -1;
while (low <= high)
{
int mid = low + ((high - low) / 2);
if (GetItemEndPosition(mid) > position)
{
result = mid;
high = mid - 1;
}
else
{
low = mid + 1;
}
}
return result;
}
private int FindFirstItemEndingAtOrAfter(float position)
{
int low = 0;
int high = cachedItemCount - 1;
int result = -1;
while (low <= high)
{
int mid = low + ((high - low) / 2);
if (GetItemEndPosition(mid) >= position)
{
result = mid;
high = mid - 1;
}
else
{
low = mid + 1;
}
}
return result;
}
private float GetItemEndPosition(int index)
{
return itemPositions[index] + itemLengths[index];
}
private float GetLength(int index, int itemCount)
{
Vector2 size = viewProvider.CalculateViewSize(index);
if (index < itemCount - 1)
{
size += spacing;
}
return direction == Direction.Vertical ? size.y : size.x;
}
}
}