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 : 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 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."); } 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; } } }