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

204 lines
6.1 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Reflection;
namespace AlicizaX.UI
{
public interface IItemRender
{
void Bind(object data, int index, Action defaultClickAction);
void UpdateSelection(bool selected);
void Unbind();
}
public abstract class ItemRender<TData, THolder> : IItemRender
where THolder : ViewHolder
{
private Action defaultClickAction;
protected ItemRender(THolder holder)
{
Holder = holder ?? throw new ArgumentNullException(nameof(holder));
CurrentIndex = -1;
}
protected THolder Holder { get; }
protected TData CurrentData { get; private set; }
protected int CurrentIndex { get; private set; }
public void Bind(object data, int index, Action defaultClickAction)
{
if (data is not TData itemData)
{
throw new InvalidCastException(
$"ItemRender '{GetType().Name}' expected data '{typeof(TData).Name}', but got '{data?.GetType().Name ?? "null"}'.");
}
CurrentData = itemData;
CurrentIndex = index;
this.defaultClickAction = defaultClickAction;
Holder.SetInteractionCallbacks(HandleClick, HandlePointerEnter, HandlePointerExit);
Bind(itemData, index);
}
public void UpdateSelection(bool selected)
{
OnSelectionChanged(selected);
}
public void Unbind()
{
OnClear();
CurrentData = default;
CurrentIndex = -1;
defaultClickAction = null;
Holder.ClearInteractionCallbacks();
}
protected abstract void Bind(TData data, int index);
protected virtual void OnSelectionChanged(bool selected)
{
}
protected virtual void OnClear()
{
}
protected virtual void OnClick()
{
}
protected virtual void OnPointerEnter()
{
}
protected virtual void OnPointerExit()
{
}
private void HandleClick()
{
defaultClickAction?.Invoke();
OnClick();
}
private void HandlePointerEnter()
{
OnPointerEnter();
}
private void HandlePointerExit()
{
OnPointerExit();
}
}
internal static class ItemRenderResolver
{
internal sealed class ItemRenderDefinition
{
private readonly ConstructorInfo constructor;
public ItemRenderDefinition(Type itemRenderType, Type holderType, ConstructorInfo constructor)
{
ItemRenderType = itemRenderType;
HolderType = holderType;
this.constructor = constructor;
}
public Type ItemRenderType { get; }
public Type HolderType { get; }
public IItemRender Create(ViewHolder viewHolder)
{
if (viewHolder == null)
{
throw new ArgumentNullException(nameof(viewHolder));
}
if (!HolderType.IsInstanceOfType(viewHolder))
{
throw new InvalidOperationException(
$"RecyclerView item render '{ItemRenderType.FullName}' expects holder '{HolderType.FullName}', but got '{viewHolder.GetType().FullName}'.");
}
return (IItemRender)constructor.Invoke(new object[] { viewHolder });
}
}
private static readonly Dictionary<Type, ItemRenderDefinition> Definitions = new();
public static ItemRenderDefinition GetOrCreate(Type itemRenderType)
{
if (itemRenderType == null)
{
throw new ArgumentNullException(nameof(itemRenderType));
}
if (Definitions.TryGetValue(itemRenderType, out ItemRenderDefinition definition))
{
return definition;
}
definition = CreateDefinition(itemRenderType);
Definitions[itemRenderType] = definition;
return definition;
}
private static ItemRenderDefinition CreateDefinition(Type itemRenderType)
{
if (itemRenderType.IsAbstract ||
itemRenderType.IsInterface ||
itemRenderType.ContainsGenericParameters ||
!typeof(IItemRender).IsAssignableFrom(itemRenderType))
{
throw new InvalidOperationException(
$"RecyclerView item render type '{itemRenderType.FullName}' is invalid.");
}
if (!TryGetHolderType(itemRenderType, out Type holderType))
{
throw new InvalidOperationException(
$"RecyclerView item render '{itemRenderType.FullName}' must inherit from ItemRender<TData, THolder>.");
}
ConstructorInfo constructor = itemRenderType.GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null,
new[] { holderType },
null);
if (constructor == null)
{
throw new InvalidOperationException(
$"RecyclerView item render '{itemRenderType.FullName}' must declare a constructor with a single '{holderType.FullName}' parameter.");
}
return new ItemRenderDefinition(itemRenderType, holderType, constructor);
}
private static bool TryGetHolderType(Type itemRenderType, out Type holderType)
{
for (Type current = itemRenderType; current != null && current != typeof(object); current = current.BaseType)
{
if (current.IsGenericType &&
current.GetGenericTypeDefinition() == typeof(ItemRender<,>))
{
Type[] arguments = current.GetGenericArguments();
holderType = arguments[1];
return true;
}
}
holderType = null;
return false;
}
}
}