com.alicizax.unity.ui.exten.../Runtime/RecyclerView/ViewProvider/ViewProvider.cs

242 lines
7.5 KiB
C#
Raw Normal View History

2025-03-12 20:59:12 +08:00
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
2025-03-12 20:59:12 +08:00
namespace AlicizaX.UI
2025-03-12 20:59:12 +08:00
{
2025-12-26 14:22:46 +08:00
public abstract class ViewProvider
2025-03-12 20:59:12 +08:00
{
private readonly List<ViewHolder> viewHolders = new();
private readonly Dictionary<int, ViewHolder> viewHoldersByIndex = new();
private readonly Dictionary<int, List<ViewHolder>> viewHoldersByDataIndex = new();
private readonly Dictionary<int, int> viewHolderPositions = new();
2025-03-12 20:59:12 +08:00
public IAdapter Adapter { get; set; }
2026-03-11 14:18:07 +08:00
2025-03-12 20:59:12 +08:00
public LayoutManager LayoutManager { get; set; }
public IReadOnlyList<ViewHolder> ViewHolders => viewHolders;
public abstract string PoolStats { get; }
2025-03-12 20:59:12 +08:00
protected RecyclerView recyclerView;
protected ViewHolder[] templates;
public ViewProvider(RecyclerView recyclerView, ViewHolder[] templates)
{
this.recyclerView = recyclerView;
this.templates = templates;
}
public abstract ViewHolder GetTemplate(string viewName);
public abstract ViewHolder[] GetTemplates();
public abstract ViewHolder Allocate(string viewName);
public abstract void Free(string viewName, ViewHolder viewHolder);
public abstract void Reset();
public abstract void PreparePool();
2025-03-12 20:59:12 +08:00
public void CreateViewHolder(int index)
{
for (int i = index; i < index + LayoutManager.Unit; i++)
{
if (i > Adapter.GetItemCount() - 1) break;
string viewName = Adapter.GetViewName(i);
var viewHolder = Allocate(viewName);
viewHolder.Name = viewName;
viewHolder.Index = i;
viewHolder.DataIndex = i;
viewHolder.RecyclerView = recyclerView;
2025-03-12 20:59:12 +08:00
viewHolders.Add(viewHolder);
RegisterViewHolder(viewHolder);
2025-12-26 14:22:46 +08:00
2025-03-12 20:59:12 +08:00
LayoutManager.Layout(viewHolder, i);
Adapter.OnBindViewHolder(viewHolder, i);
}
}
public void RemoveViewHolder(int index)
{
for (int i = index; i < index + LayoutManager.Unit; i++)
{
if (i > Adapter.GetItemCount() - 1) break;
int viewHolderIndex = GetViewHolderIndex(i);
if (viewHolderIndex < 0 || viewHolderIndex >= viewHolders.Count) return;
var viewHolder = viewHolders[viewHolderIndex];
string viewName = viewHolder.Name;
2025-03-12 20:59:12 +08:00
viewHolders.RemoveAt(viewHolderIndex);
UnregisterViewHolder(viewHolder);
RebuildViewHolderPositions(viewHolderIndex);
Adapter?.OnRecycleViewHolder(viewHolder);
2025-05-30 13:43:08 +08:00
viewHolder.OnRecycled();
ClearSelectedState(viewHolder);
Free(viewName, viewHolder);
2025-03-12 20:59:12 +08:00
}
}
public ViewHolder GetViewHolder(int index)
{
return viewHoldersByIndex.TryGetValue(index, out ViewHolder viewHolder)
? viewHolder
: null;
}
2025-04-01 15:21:02 +08:00
public ViewHolder GetViewHolderByDataIndex(int dataIndex)
{
return viewHoldersByDataIndex.TryGetValue(dataIndex, out List<ViewHolder> holders) &&
holders is { Count: > 0 }
? holders[0]
: null;
2025-03-12 20:59:12 +08:00
}
public bool TryGetViewHoldersByDataIndex(int dataIndex, out IReadOnlyList<ViewHolder> holders)
2025-03-12 20:59:12 +08:00
{
if (viewHoldersByDataIndex.TryGetValue(dataIndex, out List<ViewHolder> list) && list.Count > 0)
2025-03-12 20:59:12 +08:00
{
holders = list;
return true;
2025-03-12 20:59:12 +08:00
}
2025-04-01 15:21:02 +08:00
holders = null;
return false;
}
public int GetViewHolderIndex(int index)
{
return viewHolderPositions.TryGetValue(index, out int viewHolderIndex)
? viewHolderIndex
: -1;
2025-03-12 20:59:12 +08:00
}
public void Clear()
{
foreach (var viewHolder in viewHolders)
{
string viewName = viewHolder.Name;
Adapter?.OnRecycleViewHolder(viewHolder);
UnregisterViewHolder(viewHolder);
viewHolder.OnRecycled();
ClearSelectedState(viewHolder);
Free(viewName, viewHolder);
2025-03-12 20:59:12 +08:00
}
2025-04-01 15:21:02 +08:00
2025-03-12 20:59:12 +08:00
viewHolders.Clear();
viewHoldersByIndex.Clear();
viewHoldersByDataIndex.Clear();
viewHolderPositions.Clear();
2025-03-12 20:59:12 +08:00
}
public Vector2 CalculateViewSize(int index)
{
Vector2 size = GetTemplate(Adapter.GetViewName(index)).SizeDelta;
return size;
}
public int GetItemCount()
{
return Adapter == null ? 0 : Adapter.GetItemCount();
}
protected int GetRecommendedWarmCount()
{
if (Adapter == null || LayoutManager == null)
{
return 0;
}
int itemCount = Adapter.GetItemCount();
if (itemCount <= 0)
{
return 0;
}
int start = Mathf.Max(0, LayoutManager.GetStartIndex());
int end = Mathf.Max(start, LayoutManager.GetEndIndex());
int visibleCount = end - start + 1;
int bufferCount = Mathf.Max(1, LayoutManager.Unit);
return Mathf.Min(itemCount, visibleCount + bufferCount);
}
private void RegisterViewHolder(ViewHolder viewHolder)
{
if (viewHolder == null)
{
return;
}
viewHoldersByIndex[viewHolder.Index] = viewHolder;
viewHolderPositions[viewHolder.Index] = viewHolders.Count - 1;
if (!viewHoldersByDataIndex.TryGetValue(viewHolder.DataIndex, out List<ViewHolder> holders))
{
holders = new List<ViewHolder>();
viewHoldersByDataIndex[viewHolder.DataIndex] = holders;
}
holders.Add(viewHolder);
}
private void UnregisterViewHolder(ViewHolder viewHolder)
{
if (viewHolder == null)
{
return;
}
viewHoldersByIndex.Remove(viewHolder.Index);
viewHolderPositions.Remove(viewHolder.Index);
if (!viewHoldersByDataIndex.TryGetValue(viewHolder.DataIndex, out List<ViewHolder> holders))
{
return;
}
holders.Remove(viewHolder);
if (holders.Count == 0)
{
viewHoldersByDataIndex.Remove(viewHolder.DataIndex);
}
}
private void RebuildViewHolderPositions(int startIndex)
{
for (int i = startIndex; i < viewHolders.Count; i++)
{
ViewHolder holder = viewHolders[i];
if (holder != null)
{
viewHolderPositions[holder.Index] = i;
}
}
}
private static void ClearSelectedState(ViewHolder viewHolder)
{
if (viewHolder == null)
{
return;
}
EventSystem eventSystem = EventSystem.current;
if (eventSystem == null)
{
return;
}
GameObject selected = eventSystem.currentSelectedGameObject;
if (selected != null && selected.transform.IsChildOf(viewHolder.transform))
{
eventSystem.SetSelectedGameObject(null);
}
}
2025-03-12 20:59:12 +08:00
}
}