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

569 lines
16 KiB
C#
Raw Normal View History

2025-03-12 20:59:12 +08:00
using System;
using System.Collections.Generic;
namespace AlicizaX.UI
2025-03-12 20:59:12 +08:00
{
internal interface IItemRenderCacheOwner
2025-03-12 20:59:12 +08:00
{
void ReleaseAllItemRenders();
}
2025-03-12 20:59:12 +08:00
internal interface IItemRenderPrewarmer
{
void PrewarmItemRender(ViewHolder viewHolder, string viewName);
}
public class Adapter<T> : IAdapter, IItemRenderCacheOwner, IItemRenderPrewarmer where T : ISimpleViewData
{
protected RecyclerView recyclerView;
2025-03-12 20:59:12 +08:00
protected List<T> list;
protected int choiceIndex = -1;
private readonly Dictionary<string, ItemRenderResolver.ItemRenderDefinition> itemRenderDefinitions = new(StringComparer.Ordinal);
private ItemRenderResolver.ItemRenderDefinition defaultItemRenderDefinition;
2025-03-12 20:59:12 +08:00
public int ChoiceIndex
{
get => choiceIndex;
set => SetChoiceIndex(value);
2025-03-12 20:59:12 +08:00
}
2026-04-01 14:43:37 +08:00
internal Action<int> OnChoiceIndexChanged;
public Adapter(RecyclerView recyclerView) : this(recyclerView, new List<T>())
2025-03-12 20:59:12 +08:00
{
}
public Adapter(RecyclerView recyclerView, List<T> list)
2025-03-12 20:59:12 +08:00
{
this.recyclerView = recyclerView;
this.list = list ?? new List<T>();
2025-03-12 20:59:12 +08:00
}
public virtual int GetItemCount()
{
return list == null ? 0 : list.Count;
}
public virtual int GetRealCount()
{
return GetItemCount();
}
public virtual string GetViewName(int index)
{
return "";
}
public virtual void OnBindViewHolder(ViewHolder viewHolder, int index)
{
if (viewHolder == null) return;
if (!TryGetBindData(index, out var data)) return;
2025-03-12 20:59:12 +08:00
string viewName = GetViewName(index);
viewHolder.AdvanceBindingVersion();
viewHolder.DataIndex = index;
if (TryGetOrCreateItemRender(viewHolder, viewName, out var itemRender))
{
if (itemRender is ITypedItemRender<T> typedItemRender)
{
typedItemRender.BindData(data, index);
}
2026-04-03 15:21:13 +08:00
bool selected = index == choiceIndex;
itemRender.SyncSelection(selected);
return;
}
2025-03-12 20:59:12 +08:00
#if UNITY_EDITOR || DEVELOPMENT_BUILD
UnityEngine.Debug.LogError("RecyclerView item render is missing.");
#endif
}
public virtual void OnRecycleViewHolder(ViewHolder viewHolder)
{
if (viewHolder == null) return;
if (TryGetItemRender(viewHolder, out var itemRender))
2025-03-12 20:59:12 +08:00
{
itemRender.Unbind();
}
2025-03-12 20:59:12 +08:00
}
public virtual void NotifyDataChanged()
{
CoerceChoiceIndex();
2025-03-12 20:59:12 +08:00
recyclerView.RequestLayout();
recyclerView.Refresh();
}
public virtual void SetList(List<T> list)
2025-03-12 20:59:12 +08:00
{
this.list = list ?? new List<T>();
2025-03-12 20:59:12 +08:00
recyclerView.Reset();
NotifyDataChanged();
}
public virtual void NotifyItemChanged(int index, bool relayout = false)
{
if (index < 0 || index >= GetRealCount())
{
return;
}
CoerceChoiceIndex();
if (relayout)
{
recyclerView.RequestLayout();
recyclerView.Refresh();
return;
}
recyclerView.RebindVisibleDataIndex(index);
}
public virtual void NotifyItemRangeChanged(int index, int count, bool relayout = false)
{
if (count <= 0 || index < 0 || index >= GetRealCount())
{
return;
}
CoerceChoiceIndex();
if (relayout)
{
recyclerView.RequestLayout();
recyclerView.Refresh();
return;
}
recyclerView.RebindVisibleDataRange(index, count);
}
2026-04-15 13:53:53 +08:00
public virtual void NotifyItemInserted()
{
CoerceChoiceIndex();
recyclerView.RequestLayout();
recyclerView.Refresh();
}
2026-04-15 13:53:53 +08:00
public virtual void NotifyItemRangeInserted(int count)
{
if (count <= 0)
{
return;
}
CoerceChoiceIndex();
recyclerView.RequestLayout();
recyclerView.Refresh();
}
2026-04-15 13:53:53 +08:00
public virtual void NotifyItemRemoved()
{
CoerceChoiceIndex();
recyclerView.RequestLayout();
recyclerView.Refresh();
}
2026-04-15 13:53:53 +08:00
public virtual void NotifyItemRangeRemoved(int count)
{
if (count <= 0)
{
return;
}
CoerceChoiceIndex();
recyclerView.RequestLayout();
recyclerView.Refresh();
}
public void RegisterItemRender<TItemRender>(string viewName = "") where TItemRender : ItemRenderBase
{
RegisterItemRender(typeof(TItemRender), viewName);
}
public void RegisterItemRender(Type itemRenderType, string viewName = "")
{
var definition = ItemRenderResolver.GetOrCreate(itemRenderType);
if (string.IsNullOrEmpty(viewName))
{
ReleaseCachedItemRenders(string.Empty);
defaultItemRenderDefinition = definition;
return;
}
ReleaseCachedItemRenders(viewName);
itemRenderDefinitions[viewName] = definition;
}
public bool UnregisterItemRender(string viewName = "")
{
if (string.IsNullOrEmpty(viewName))
{
bool hadDefault = defaultItemRenderDefinition != null;
defaultItemRenderDefinition = null;
if (hadDefault)
{
ReleaseCachedItemRenders(string.Empty);
}
return hadDefault;
}
bool removed = itemRenderDefinitions.Remove(viewName);
if (removed)
{
ReleaseCachedItemRenders(viewName);
}
return removed;
}
public bool UnregisterItemRender(Type itemRenderType)
{
if (itemRenderType == null)
{
return false;
}
bool removed = false;
if (defaultItemRenderDefinition != null && defaultItemRenderDefinition.ItemRenderType == itemRenderType)
{
defaultItemRenderDefinition = null;
ReleaseCachedItemRenders(string.Empty);
removed = true;
}
string removedViewName = null;
foreach (var pair in itemRenderDefinitions)
{
if (pair.Value != null && pair.Value.ItemRenderType == itemRenderType)
{
removedViewName = pair.Key;
break;
}
}
if (removedViewName != null)
{
itemRenderDefinitions.Remove(removedViewName);
ReleaseCachedItemRenders(removedViewName);
removed = true;
}
return removed;
}
public void ClearItemRenderRegistrations()
{
ReleaseAllItemRenders();
itemRenderDefinitions.Clear();
defaultItemRenderDefinition = null;
}
2025-03-12 20:59:12 +08:00
public T GetData(int index)
{
if (index < 0 || index >= GetItemCount()) return default;
return list[index];
}
public void Add(T item)
{
if (list == null)
{
list = new List<T>();
}
2025-03-12 20:59:12 +08:00
list.Add(item);
2026-04-15 13:53:53 +08:00
NotifyItemInserted();
2025-03-12 20:59:12 +08:00
}
public void AddRange(IEnumerable<T> collection)
{
if (collection == null)
{
return;
}
2025-03-12 20:59:12 +08:00
list.AddRange(collection);
if (collection is ICollection<T> itemCollection)
{
2026-04-15 13:53:53 +08:00
NotifyItemRangeInserted(itemCollection.Count);
return;
}
2025-03-12 20:59:12 +08:00
NotifyDataChanged();
}
public void Insert(int index, T item)
{
list.Insert(index, item);
2026-04-15 13:53:53 +08:00
NotifyItemInserted();
2025-03-12 20:59:12 +08:00
}
public void InsertRange(int index, IEnumerable<T> collection)
{
if (collection == null)
{
return;
}
2025-03-12 20:59:12 +08:00
list.InsertRange(index, collection);
if (collection is ICollection<T> itemCollection)
{
2026-04-15 13:53:53 +08:00
NotifyItemRangeInserted(itemCollection.Count);
return;
}
2025-03-12 20:59:12 +08:00
NotifyDataChanged();
}
public void Remove(T item)
{
int index = list.IndexOf(item);
RemoveAt(index);
}
public void RemoveAt(int index)
{
if (index < 0 || index >= GetItemCount()) return;
list.RemoveAt(index);
2026-04-15 13:53:53 +08:00
NotifyItemRemoved();
2025-03-12 20:59:12 +08:00
}
public void RemoveRange(int index, int count)
{
list.RemoveRange(index, count);
2026-04-15 13:53:53 +08:00
NotifyItemRangeRemoved(count);
2025-03-12 20:59:12 +08:00
}
public void RemoveAll(Predicate<T> match)
{
list.RemoveAll(match);
NotifyDataChanged();
}
public void Clear()
{
if (list == null || list.Count == 0)
{
return;
}
int count = list.Count;
2025-03-12 20:59:12 +08:00
list.Clear();
2026-04-15 13:53:53 +08:00
NotifyItemRangeRemoved(count);
2025-03-12 20:59:12 +08:00
}
public void Reverse(int index, int count)
{
list.Reverse(index, count);
NotifyDataChanged();
}
public void Reverse()
{
list.Reverse();
NotifyDataChanged();
}
public void Sort(Comparison<T> comparison)
{
list.Sort(comparison);
NotifyDataChanged();
}
protected void SetChoiceIndex(int index)
2025-03-12 20:59:12 +08:00
{
int itemCount = GetRealCount();
if (itemCount <= 0)
{
index = -1;
}
else if (index >= itemCount)
{
index = itemCount - 1;
}
else if (index < -1)
{
index = -1;
}
if (index == choiceIndex) return;
2026-04-03 15:21:13 +08:00
int previousChoice = choiceIndex;
if (choiceIndex != -1 && TryGetViewHolder(choiceIndex, out var oldHolder))
{
UpdateSelectionState(oldHolder, false);
}
choiceIndex = index;
if (choiceIndex != -1 && TryGetViewHolder(choiceIndex, out var newHolder))
{
UpdateSelectionState(newHolder, true);
}
2026-04-01 14:43:37 +08:00
OnChoiceIndexChanged?.Invoke(choiceIndex);
2025-03-12 20:59:12 +08:00
}
protected virtual bool TryGetBindData(int index, out T data)
{
if (list == null || index < 0 || index >= list.Count)
{
data = default;
return false;
}
data = list[index];
return true;
}
private bool TryGetViewHolder(int index, out ViewHolder viewHolder)
{
viewHolder = recyclerView.ViewProvider.GetViewHolderByDataIndex(index);
return viewHolder != null;
}
private static bool TryGetItemRender(ViewHolder viewHolder, out IItemRender itemRender)
{
itemRender = viewHolder != null ? viewHolder.CachedItemRender : null;
return itemRender != null;
}
private static void ReleaseItemRender(ViewHolder viewHolder)
{
IItemRender itemRender = viewHolder != null ? viewHolder.CachedItemRender : null;
if (itemRender == null)
{
return;
}
itemRender.Unbind();
if (itemRender is ItemRenderBase itemRenderBase)
{
itemRenderBase.Detach();
}
viewHolder.CachedItemRender = null;
viewHolder.CachedItemRenderViewName = null;
}
private void UpdateSelectionState(ViewHolder viewHolder, bool selected)
{
if (TryGetItemRender(viewHolder, out IItemRender itemRender))
{
itemRender.UpdateSelection(selected);
}
}
private bool TryGetItemRenderDefinition(string viewName, out ItemRenderResolver.ItemRenderDefinition definition)
{
if (!string.IsNullOrEmpty(viewName) && itemRenderDefinitions.TryGetValue(viewName, out definition))
{
return definition != null;
}
definition = defaultItemRenderDefinition;
return definition != null;
}
private bool TryGetOrCreateItemRender(ViewHolder viewHolder, string viewName, out IItemRender itemRender)
{
if (viewHolder == null)
{
itemRender = null;
return false;
}
if (viewHolder.CachedItemRender != null)
{
if (string.Equals(viewHolder.CachedItemRenderViewName, viewName, StringComparison.Ordinal))
{
itemRender = viewHolder.CachedItemRender;
return true;
}
ReleaseItemRender(viewHolder);
}
2025-03-12 20:59:12 +08:00
if (!TryGetItemRenderDefinition(viewName, out var definition))
2025-03-12 20:59:12 +08:00
{
itemRender = null;
return false;
}
itemRender = definition.Create(viewHolder, recyclerView, this, SetChoiceIndex);
if (itemRender == null)
{
return false;
}
viewHolder.CachedItemRender = itemRender;
viewHolder.CachedItemRenderViewName = viewName;
return true;
}
void IItemRenderCacheOwner.ReleaseAllItemRenders()
{
ReleaseAllItemRenders();
}
void IItemRenderPrewarmer.PrewarmItemRender(ViewHolder viewHolder, string viewName)
{
TryGetOrCreateItemRender(viewHolder, viewName, out _);
}
2025-03-12 20:59:12 +08:00
private void ReleaseAllItemRenders()
{
if (recyclerView?.ViewProvider == null)
{
return;
}
for (int i = 0; i < recyclerView.ViewProvider.VisibleCount; i++)
{
ReleaseItemRender(recyclerView.ViewProvider.GetVisibleViewHolder(i));
2025-03-12 20:59:12 +08:00
}
}
private void ReleaseCachedItemRenders(string viewName)
2025-03-12 20:59:12 +08:00
{
if (recyclerView?.ViewProvider == null)
{
return;
}
for (int i = 0; i < recyclerView.ViewProvider.VisibleCount; i++)
{
ViewHolder viewHolder = recyclerView.ViewProvider.GetVisibleViewHolder(i);
if (viewHolder == null || !string.Equals(viewHolder.CachedItemRenderViewName, viewName, StringComparison.Ordinal))
{
continue;
}
ReleaseItemRender(viewHolder);
}
}
private void CoerceChoiceIndex()
{
int itemCount = GetRealCount();
if (itemCount <= 0)
{
SetChoiceIndex(-1);
return;
}
if (choiceIndex >= itemCount)
{
SetChoiceIndex(itemCount - 1);
}
2025-03-12 20:59:12 +08:00
}
}
}