com.alicizax.unity.ui.exten.../Runtime/RecyclerView/Layout/MixedLayoutManager.cs
2026-04-15 14:51:57 +08:00

271 lines
7.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Buffers;
using UnityEngine;
namespace AlicizaX.UI
{
[Serializable]
public class MixedLayoutManager : LayoutManager
{
private float[] itemLengths = Array.Empty<float>();
private float[] itemPositions = Array.Empty<float>();
private int rentedArraySize = 0;
private Vector2 firstItemSize = Vector2.zero;
private int cachedItemCount = -1;
private bool positionCacheDirty = true;
public MixedLayoutManager() { }
~MixedLayoutManager()
{
ReturnRentedArrays();
}
private void ReturnRentedArrays()
{
if (rentedArraySize > 0)
{
ArrayPool<float>.Shared.Return(itemLengths);
ArrayPool<float>.Shared.Return(itemPositions);
itemLengths = Array.Empty<float>();
itemPositions = Array.Empty<float>();
rentedArraySize = 0;
}
}
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)
{
// ArrayPool.Rent 返回的数组长度 >= 请求长度rentedArraySize 记录实际容量。
// 仅当 itemCount 超出当前容量时才归还并重新租用,缩容时复用原数组。
if (itemCount > rentedArraySize)
{
ReturnRentedArrays();
if (itemCount > 0)
{
itemLengths = ArrayPool<float>.Shared.Rent(itemCount);
itemPositions = ArrayPool<float>.Shared.Rent(itemCount);
rentedArraySize = itemLengths.Length;
}
}
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;
}
}
}