This commit is contained in:
陈思海 2026-03-30 15:40:00 +08:00
parent 40080636d8
commit cddf34948d

View File

@ -13,25 +13,28 @@ namespace AlicizaX.UI
void Unbind(); void Unbind();
} }
internal interface IItemRenderInitializer
{
void Reset(ViewHolder viewHolder);
}
public abstract class ItemRender<TData, THolder> : IItemRender public abstract class ItemRender<TData, THolder> : IItemRender
, IItemRenderInitializer
where THolder : ViewHolder where THolder : ViewHolder
{ {
private Action defaultClickAction; private Action defaultClickAction;
protected THolder Holder { get; private set; }
protected ItemRender(THolder holder)
{
Holder = holder ?? throw new ArgumentNullException(nameof(holder));
CurrentIndex = -1;
}
protected THolder Holder { get; }
protected TData CurrentData { get; private set; } protected TData CurrentData { get; private set; }
protected int CurrentIndex { get; private set; } protected int CurrentIndex { get; private set; } = -1;
protected bool IsSelected { get; private set; }
public void Bind(object data, int index, Action defaultClickAction) public void Bind(object data, int index, Action defaultClickAction)
{ {
EnsureHolder();
if (data is not TData itemData) if (data is not TData itemData)
{ {
throw new InvalidCastException( throw new InvalidCastException(
@ -47,20 +50,22 @@ namespace AlicizaX.UI
public void UpdateSelection(bool selected) public void UpdateSelection(bool selected)
{ {
EnsureHolder();
IsSelected = selected;
OnSelectionChanged(selected); OnSelectionChanged(selected);
} }
public void Unbind() public void Unbind()
{ {
OnClear(); ResetState();
CurrentData = default;
CurrentIndex = -1;
defaultClickAction = null;
Holder.ClearInteractionCallbacks();
} }
protected abstract void Bind(TData data, int index); protected abstract void Bind(TData data, int index);
protected virtual void OnHolderChanged()
{
}
protected virtual void OnSelectionChanged(bool selected) protected virtual void OnSelectionChanged(bool selected)
{ {
} }
@ -96,19 +101,63 @@ namespace AlicizaX.UI
{ {
OnPointerExit(); OnPointerExit();
} }
void IItemRenderInitializer.Reset(ViewHolder viewHolder)
{
if (viewHolder == null)
{
throw new ArgumentNullException(nameof(viewHolder));
}
if (viewHolder is not THolder holder)
{
throw new InvalidOperationException(
$"RecyclerView item render '{GetType().FullName}' expects holder '{typeof(THolder).FullName}', but got '{viewHolder.GetType().FullName}'.");
}
ResetState();
Holder = holder;
OnHolderChanged();
}
private void EnsureHolder()
{
if (Holder == null)
{
throw new InvalidOperationException(
$"RecyclerView item render '{GetType().FullName}' has not been initialized with a holder.");
}
}
private void ResetState()
{
if (Holder != null)
{
if (IsSelected)
{
IsSelected = false;
OnSelectionChanged(false);
}
OnClear();
Holder.ClearInteractionCallbacks();
}
CurrentData = default;
CurrentIndex = -1;
IsSelected = false;
defaultClickAction = null;
}
} }
internal static class ItemRenderResolver internal static class ItemRenderResolver
{ {
internal sealed class ItemRenderDefinition internal sealed class ItemRenderDefinition
{ {
private readonly ConstructorInfo constructor; public ItemRenderDefinition(Type itemRenderType, Type holderType)
public ItemRenderDefinition(Type itemRenderType, Type holderType, ConstructorInfo constructor)
{ {
ItemRenderType = itemRenderType; ItemRenderType = itemRenderType;
HolderType = holderType; HolderType = holderType;
this.constructor = constructor;
} }
public Type ItemRenderType { get; } public Type ItemRenderType { get; }
@ -128,7 +177,20 @@ namespace AlicizaX.UI
$"RecyclerView item render '{ItemRenderType.FullName}' expects holder '{HolderType.FullName}', but got '{viewHolder.GetType().FullName}'."); $"RecyclerView item render '{ItemRenderType.FullName}' expects holder '{HolderType.FullName}', but got '{viewHolder.GetType().FullName}'.");
} }
return (IItemRender)constructor.Invoke(new object[] { viewHolder }); if (Activator.CreateInstance(ItemRenderType, true) is not IItemRender itemRender)
{
throw new InvalidOperationException(
$"RecyclerView item render '{ItemRenderType.FullName}' could not be created.");
}
if (itemRender is not IItemRenderInitializer initializer)
{
throw new InvalidOperationException(
$"RecyclerView item render '{ItemRenderType.FullName}' must inherit from ItemRender<TData, THolder>.");
}
initializer.Reset(viewHolder);
return itemRender;
} }
} }
@ -171,16 +233,16 @@ namespace AlicizaX.UI
ConstructorInfo constructor = itemRenderType.GetConstructor( ConstructorInfo constructor = itemRenderType.GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null, null,
new[] { holderType }, Type.EmptyTypes,
null); null);
if (constructor == null) if (constructor == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"RecyclerView item render '{itemRenderType.FullName}' must declare a constructor with a single '{holderType.FullName}' parameter."); $"RecyclerView item render '{itemRenderType.FullName}' must have a parameterless constructor.");
} }
return new ItemRenderDefinition(itemRenderType, holderType, constructor); return new ItemRenderDefinition(itemRenderType, holderType);
} }
private static bool TryGetHolderType(Type itemRenderType, out Type holderType) private static bool TryGetHolderType(Type itemRenderType, out Type holderType)