11
This commit is contained in:
parent
623edbeed6
commit
2471317801
@ -3,6 +3,10 @@ using System.Collections.Generic;
|
||||
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// RecyclerView 的通用适配器,支持数据绑定、选中状态和列表操作
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据类型,必须实现 ISimpleViewData 接口</typeparam>
|
||||
public class Adapter<T> : IAdapter where T : ISimpleViewData
|
||||
{
|
||||
protected RecyclerView recyclerView;
|
||||
@ -12,20 +16,38 @@ namespace AlicizaX.UI
|
||||
|
||||
protected int choiceIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 当前选中项的索引
|
||||
/// </summary>
|
||||
public int ChoiceIndex
|
||||
{
|
||||
get => choiceIndex;
|
||||
set { SetChoiceIndex(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">RecyclerView 实例</param>
|
||||
public Adapter(RecyclerView recyclerView) : this(recyclerView, new List<T>(), null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">RecyclerView 实例</param>
|
||||
/// <param name="list">数据列表</param>
|
||||
public Adapter(RecyclerView recyclerView, List<T> list) : this(recyclerView, list, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">RecyclerView 实例</param>
|
||||
/// <param name="list">数据列表</param>
|
||||
/// <param name="onItemClick">列表项点击回调</param>
|
||||
public Adapter(RecyclerView recyclerView, List<T> list, Action<T> onItemClick)
|
||||
{
|
||||
this.recyclerView = recyclerView;
|
||||
@ -33,21 +55,33 @@ namespace AlicizaX.UI
|
||||
this.onItemClick = onItemClick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取列表项总数
|
||||
/// </summary>
|
||||
public virtual int GetItemCount()
|
||||
{
|
||||
return list == null ? 0 : list.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取实际数据项数量
|
||||
/// </summary>
|
||||
public virtual int GetRealCount()
|
||||
{
|
||||
return GetItemCount();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定索引位置的视图名称
|
||||
/// </summary>
|
||||
public virtual string GetViewName(int index)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定视图持有者与数据
|
||||
/// </summary>
|
||||
public virtual void OnBindViewHolder(ViewHolder viewHolder, int index)
|
||||
{
|
||||
if (index < 0 || index >= GetItemCount()) return;
|
||||
@ -63,12 +97,19 @@ namespace AlicizaX.UI
|
||||
viewHolder.BindChoiceState(index == choiceIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通知数据已更改
|
||||
/// </summary>
|
||||
public virtual void NotifyDataChanged()
|
||||
{
|
||||
recyclerView.RequestLayout();
|
||||
recyclerView.Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置数据列表并刷新视图
|
||||
/// </summary>
|
||||
/// <param name="list">新的数据列表</param>
|
||||
public virtual void SetList(List<T> list)
|
||||
{
|
||||
this.list = list;
|
||||
@ -76,6 +117,11 @@ namespace AlicizaX.UI
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定索引的数据
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <returns>数据对象,索引无效时返回 default</returns>
|
||||
public T GetData(int index)
|
||||
{
|
||||
if (index < 0 || index >= GetItemCount()) return default;
|
||||
@ -83,36 +129,62 @@ namespace AlicizaX.UI
|
||||
return list[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加单个数据项
|
||||
/// </summary>
|
||||
/// <param name="item">要添加的数据项</param>
|
||||
public void Add(T item)
|
||||
{
|
||||
list.Add(item);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量添加数据项
|
||||
/// </summary>
|
||||
/// <param name="collection">要添加的数据集合</param>
|
||||
public void AddRange(IEnumerable<T> collection)
|
||||
{
|
||||
list.AddRange(collection);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定位置插入数据项
|
||||
/// </summary>
|
||||
/// <param name="index">插入位置</param>
|
||||
/// <param name="item">要插入的数据项</param>
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
list.Insert(index, item);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定位置批量插入数据项
|
||||
/// </summary>
|
||||
/// <param name="index">插入位置</param>
|
||||
/// <param name="collection">要插入的数据集合</param>
|
||||
public void InsertRange(int index, IEnumerable<T> collection)
|
||||
{
|
||||
list.InsertRange(index, collection);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定的数据项
|
||||
/// </summary>
|
||||
/// <param name="item">要移除的数据项</param>
|
||||
public void Remove(T item)
|
||||
{
|
||||
int index = list.IndexOf(item);
|
||||
RemoveAt(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定索引位置的数据项
|
||||
/// </summary>
|
||||
/// <param name="index">要移除的索引</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= GetItemCount()) return;
|
||||
@ -121,47 +193,79 @@ namespace AlicizaX.UI
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定范围的数据项
|
||||
/// </summary>
|
||||
/// <param name="index">起始索引</param>
|
||||
/// <param name="count">移除数量</param>
|
||||
public void RemoveRange(int index, int count)
|
||||
{
|
||||
list.RemoveRange(index, count);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除所有符合条件的数据项
|
||||
/// </summary>
|
||||
/// <param name="match">匹配条件</param>
|
||||
public void RemoveAll(Predicate<T> match)
|
||||
{
|
||||
list.RemoveAll(match);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有数据
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
list.Clear();
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反转指定范围的数据项顺序
|
||||
/// </summary>
|
||||
/// <param name="index">起始索引</param>
|
||||
/// <param name="count">反转数量</param>
|
||||
public void Reverse(int index, int count)
|
||||
{
|
||||
list.Reverse(index, count);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反转所有数据项顺序
|
||||
/// </summary>
|
||||
public void Reverse()
|
||||
{
|
||||
list.Reverse();
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的比较器排序数据
|
||||
/// </summary>
|
||||
/// <param name="comparison">比较器</param>
|
||||
public void Sort(Comparison<T> comparison)
|
||||
{
|
||||
list.Sort(comparison);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置列表项点击回调
|
||||
/// </summary>
|
||||
/// <param name="onItemClick">点击回调</param>
|
||||
public void SetOnItemClick(Action<T> onItemClick)
|
||||
{
|
||||
this.onItemClick = onItemClick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置选中项索引
|
||||
/// </summary>
|
||||
/// <param name="index">要选中的索引</param>
|
||||
protected void SetChoiceIndex(int index)
|
||||
{
|
||||
if (index == choiceIndex) return;
|
||||
@ -185,6 +289,12 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取指定索引的视图持有者
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <param name="viewHolder">输出的视图持有者</param>
|
||||
/// <returns>是否成功获取</returns>
|
||||
private bool TryGetViewHolder(int index, out ViewHolder viewHolder)
|
||||
{
|
||||
viewHolder = recyclerView.ViewProvider.GetViewHolder(index);
|
||||
|
||||
@ -1,15 +1,39 @@
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// RecyclerView 适配器接口,负责提供数据和绑定视图
|
||||
/// </summary>
|
||||
public interface IAdapter
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取列表项总数(包括循环或分组后的虚拟数量)
|
||||
/// </summary>
|
||||
/// <returns>列表项总数</returns>
|
||||
int GetItemCount();
|
||||
|
||||
/// <summary>
|
||||
/// 获取实际数据项数量(不包括循环或分组的虚拟数量)
|
||||
/// </summary>
|
||||
/// <returns>实际数据项数量</returns>
|
||||
int GetRealCount();
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定索引位置的视图名称,用于视图类型区分
|
||||
/// </summary>
|
||||
/// <param name="index">列表项索引</param>
|
||||
/// <returns>视图名称</returns>
|
||||
string GetViewName(int index);
|
||||
|
||||
/// <summary>
|
||||
/// 绑定视图持有者与数据
|
||||
/// </summary>
|
||||
/// <param name="viewHolder">视图持有者</param>
|
||||
/// <param name="index">数据索引</param>
|
||||
void OnBindViewHolder(ViewHolder viewHolder, int index);
|
||||
|
||||
/// <summary>
|
||||
/// 通知数据已更改,触发视图刷新
|
||||
/// </summary>
|
||||
void NotifyDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,23 @@
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// 缓动函数工具类
|
||||
/// 提供各种常用的缓动函数,用于实现平滑的动画效果
|
||||
/// 基于 https://easings.net/ 的标准缓动函数
|
||||
/// </summary>
|
||||
public class EaseUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 正弦缓入函数
|
||||
/// </summary>
|
||||
public static double EaseInSine(float x)
|
||||
{
|
||||
return 1 - Math.Cos(x * Math.PI / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 正弦缓出函数
|
||||
/// </summary>
|
||||
public static double EaseOutSine(float x)
|
||||
{
|
||||
return Math.Sin(x * Math.PI / 2);
|
||||
|
||||
@ -2,10 +2,18 @@ using UnityEngine;
|
||||
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 布局管理器抽象基类
|
||||
/// 负责计算和管理 RecyclerView 中列表项的位置、大小和可见性
|
||||
/// 子类需要实现具体的布局算法(如线性、网格、圆形等)
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public abstract class LayoutManager : ILayoutManager
|
||||
{
|
||||
protected Vector2 viewportSize;
|
||||
/// <summary>
|
||||
/// 获取视口大小(可见区域的尺寸)
|
||||
/// </summary>
|
||||
public Vector2 ViewportSize
|
||||
{
|
||||
get => viewportSize;
|
||||
@ -13,6 +21,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected Vector2 contentSize;
|
||||
/// <summary>
|
||||
/// 获取内容总大小(所有列表项占据的总尺寸)
|
||||
/// </summary>
|
||||
public Vector2 ContentSize
|
||||
{
|
||||
get => contentSize;
|
||||
@ -20,6 +31,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected Vector2 contentOffset;
|
||||
/// <summary>
|
||||
/// 获取内容偏移量(用于对齐计算)
|
||||
/// </summary>
|
||||
public Vector2 ContentOffset
|
||||
{
|
||||
get => contentOffset;
|
||||
@ -27,6 +41,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected Vector2 viewportOffset;
|
||||
/// <summary>
|
||||
/// 获取视口偏移量(用于对齐计算)
|
||||
/// </summary>
|
||||
public Vector2 ViewportOffset
|
||||
{
|
||||
get => viewportOffset;
|
||||
@ -34,6 +51,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected IAdapter adapter;
|
||||
/// <summary>
|
||||
/// 获取或设置数据适配器
|
||||
/// </summary>
|
||||
public IAdapter Adapter
|
||||
{
|
||||
get => adapter;
|
||||
@ -41,6 +61,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected ViewProvider viewProvider;
|
||||
/// <summary>
|
||||
/// 获取或设置视图提供器
|
||||
/// </summary>
|
||||
public ViewProvider ViewProvider
|
||||
{
|
||||
get => viewProvider;
|
||||
@ -48,6 +71,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected RecyclerView recyclerView;
|
||||
/// <summary>
|
||||
/// 获取或设置关联的 RecyclerView 实例
|
||||
/// </summary>
|
||||
public virtual RecyclerView RecyclerView
|
||||
{
|
||||
get => recyclerView;
|
||||
@ -55,6 +81,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected Direction direction;
|
||||
/// <summary>
|
||||
/// 获取或设置滚动方向
|
||||
/// </summary>
|
||||
public Direction Direction
|
||||
{
|
||||
get => direction;
|
||||
@ -62,6 +91,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected Alignment alignment;
|
||||
/// <summary>
|
||||
/// 获取或设置对齐方式
|
||||
/// </summary>
|
||||
public Alignment Alignment
|
||||
{
|
||||
get => alignment;
|
||||
@ -69,6 +101,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected Vector2 spacing;
|
||||
/// <summary>
|
||||
/// 获取或设置列表项间距
|
||||
/// </summary>
|
||||
public Vector2 Spacing
|
||||
{
|
||||
get => spacing;
|
||||
@ -76,6 +111,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected Vector2 padding;
|
||||
/// <summary>
|
||||
/// 获取或设置内边距
|
||||
/// </summary>
|
||||
public Vector2 Padding
|
||||
{
|
||||
get => padding;
|
||||
@ -83,6 +121,9 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
protected int unit = 1;
|
||||
/// <summary>
|
||||
/// 获取或设置布局单元(用于网格布局等,表示一次处理多少个项)
|
||||
/// </summary>
|
||||
public int Unit
|
||||
{
|
||||
get => unit;
|
||||
@ -90,10 +131,17 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前滚动位置
|
||||
/// </summary>
|
||||
public float ScrollPosition => recyclerView.GetScrollPosition();
|
||||
|
||||
public LayoutManager() { }
|
||||
|
||||
/// <summary>
|
||||
/// 设置内容大小
|
||||
/// 计算视口大小、内容大小以及各种偏移量
|
||||
/// </summary>
|
||||
public void SetContentSize()
|
||||
{
|
||||
viewportSize = recyclerView.GetComponent<RectTransform>().rect.size;
|
||||
@ -102,6 +150,10 @@ namespace AlicizaX.UI
|
||||
viewportOffset = CalculateViewportOffset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新所有可见 ViewHolder 的布局
|
||||
/// 遍历所有当前显示的 ViewHolder 并重新计算其位置
|
||||
/// </summary>
|
||||
public void UpdateLayout()
|
||||
{
|
||||
foreach (var viewHolder in viewProvider.ViewHolders)
|
||||
@ -110,6 +162,11 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为指定的 ViewHolder 设置布局位置
|
||||
/// </summary>
|
||||
/// <param name="viewHolder">要布局的 ViewHolder</param>
|
||||
/// <param name="index">ViewHolder 对应的数据索引</param>
|
||||
public virtual void Layout(ViewHolder viewHolder, int index)
|
||||
{
|
||||
Vector2 pos = CalculatePosition(index);
|
||||
@ -119,24 +176,67 @@ namespace AlicizaX.UI
|
||||
viewHolder.RectTransform.anchoredPosition3D = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算内容总大小(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <returns>内容的总尺寸</returns>
|
||||
public abstract Vector2 CalculateContentSize();
|
||||
|
||||
/// <summary>
|
||||
/// 计算指定索引的 ViewHolder 位置(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <returns>ViewHolder 的位置</returns>
|
||||
public abstract Vector2 CalculatePosition(int index);
|
||||
|
||||
/// <summary>
|
||||
/// 计算内容偏移量(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <returns>内容偏移量</returns>
|
||||
public abstract Vector2 CalculateContentOffset();
|
||||
|
||||
/// <summary>
|
||||
/// 计算视口偏移量(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <returns>视口偏移量</returns>
|
||||
public abstract Vector2 CalculateViewportOffset();
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前可见区域的起始索引(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <returns>起始索引</returns>
|
||||
public abstract int GetStartIndex();
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前可见区域的结束索引(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <returns>结束索引</returns>
|
||||
public abstract int GetEndIndex();
|
||||
|
||||
/// <summary>
|
||||
/// 将数据索引转换为滚动位置(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <returns>对应的滚动位置</returns>
|
||||
public abstract float IndexToPosition(int index);
|
||||
|
||||
/// <summary>
|
||||
/// 将滚动位置转换为数据索引(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <param name="position">滚动位置</param>
|
||||
/// <returns>对应的数据索引</returns>
|
||||
public abstract int PositionToIndex(float position);
|
||||
|
||||
/// <summary>
|
||||
/// 执行列表项动画(虚方法,子类可选择性重写)
|
||||
/// </summary>
|
||||
public virtual void DoItemAnimation() { }
|
||||
|
||||
/// <summary>
|
||||
/// 判断起始位置的 ViewHolder 是否完全可见
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <returns>如果完全可见返回 true,否则返回 false</returns>
|
||||
public virtual bool IsFullVisibleStart(int index)
|
||||
{
|
||||
Vector2 vector2 = CalculatePosition(index);
|
||||
@ -144,6 +244,11 @@ namespace AlicizaX.UI
|
||||
return position + GetOffset() >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断起始位置的 ViewHolder 是否完全不可见
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <returns>如果完全不可见返回 true,否则返回 false</returns>
|
||||
public virtual bool IsFullInvisibleStart(int index)
|
||||
{
|
||||
Vector2 vector2 = CalculatePosition(index + unit);
|
||||
@ -151,6 +256,11 @@ namespace AlicizaX.UI
|
||||
return position + GetOffset() < 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断结束位置的 ViewHolder 是否完全可见
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <returns>如果完全可见返回 true,否则返回 false</returns>
|
||||
public virtual bool IsFullVisibleEnd(int index)
|
||||
{
|
||||
Vector2 vector2 = CalculatePosition(index + unit);
|
||||
@ -159,6 +269,11 @@ namespace AlicizaX.UI
|
||||
return position + GetOffset() <= viewLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断结束位置的 ViewHolder 是否完全不可见
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <returns>如果完全不可见返回 true,否则返回 false</returns>
|
||||
public virtual bool IsFullInvisibleEnd(int index)
|
||||
{
|
||||
Vector2 vector2 = CalculatePosition(index);
|
||||
@ -167,6 +282,11 @@ namespace AlicizaX.UI
|
||||
return position + GetOffset() > viewLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定索引的 ViewHolder 是否可见(部分或完全)
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
/// <returns>如果可见返回 true,否则返回 false</returns>
|
||||
public virtual bool IsVisible(int index)
|
||||
{
|
||||
float position, viewLength;
|
||||
@ -189,6 +309,11 @@ namespace AlicizaX.UI
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取适配内容大小
|
||||
/// 根据对齐方式计算实际显示的内容长度
|
||||
/// </summary>
|
||||
/// <returns>适配后的内容大小</returns>
|
||||
protected virtual float GetFitContentSize()
|
||||
{
|
||||
float len;
|
||||
@ -203,23 +328,40 @@ namespace AlicizaX.UI
|
||||
return len;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取偏移量
|
||||
/// 计算内容偏移和视口偏移的组合值
|
||||
/// </summary>
|
||||
/// <returns>总偏移量</returns>
|
||||
protected virtual float GetOffset()
|
||||
{
|
||||
return direction == Direction.Vertical ? -contentOffset.y + viewportOffset.y : -contentOffset.x + viewportOffset.x;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 滚动方向枚举
|
||||
/// </summary>
|
||||
public enum Direction
|
||||
{
|
||||
/// <summary>垂直滚动</summary>
|
||||
Vertical = 0,
|
||||
/// <summary>水平滚动</summary>
|
||||
Horizontal = 1,
|
||||
/// <summary>自定义滚动</summary>
|
||||
Custom = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对齐方式枚举
|
||||
/// </summary>
|
||||
public enum Alignment
|
||||
{
|
||||
/// <summary>左对齐</summary>
|
||||
Left,
|
||||
/// <summary>居中对齐</summary>
|
||||
Center,
|
||||
/// <summary>顶部对齐</summary>
|
||||
Top
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,11 @@ using UnityEngine.UI;
|
||||
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// RecyclerView 核心组件,用于高效显示大量列表数据
|
||||
/// 通过视图回收和复用机制,只渲染可见区域的项目,大幅提升性能
|
||||
/// 支持垂直/水平滚动、网格布局、循环滚动等多种布局模式
|
||||
/// </summary>
|
||||
public class RecyclerView : MonoBehaviour
|
||||
{
|
||||
#region Serialized Fields - Layout Settings
|
||||
@ -57,24 +62,36 @@ namespace AlicizaX.UI
|
||||
|
||||
#region Public Properties - Layout Settings
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置列表的滚动方向(垂直、水平或自定义)
|
||||
/// </summary>
|
||||
public Direction Direction
|
||||
{
|
||||
get => direction;
|
||||
set => direction = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置列表项的对齐方式(起始、居中或结束)
|
||||
/// </summary>
|
||||
public Alignment Alignment
|
||||
{
|
||||
get => alignment;
|
||||
set => alignment = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置列表项之间的间距(X轴和Y轴)
|
||||
/// </summary>
|
||||
public Vector2 Spacing
|
||||
{
|
||||
get => spacing;
|
||||
set => spacing = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置列表内容的内边距(X轴和Y轴)
|
||||
/// </summary>
|
||||
public Vector2 Padding
|
||||
{
|
||||
get => padding;
|
||||
@ -85,6 +102,10 @@ namespace AlicizaX.UI
|
||||
|
||||
#region Public Properties - Scroll Settings
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置是否启用滚动功能
|
||||
/// 启用时会激活 Scroller 组件并显示滚动条(如果配置了)
|
||||
/// </summary>
|
||||
public bool Scroll
|
||||
{
|
||||
get => scroll;
|
||||
@ -116,6 +137,11 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置是否启用吸附功能
|
||||
/// 启用时滚动停止后会自动对齐到最近的列表项
|
||||
/// 注意:此功能依赖于 Scroll 属性,只有在滚动启用时才生效
|
||||
/// </summary>
|
||||
public bool Snap
|
||||
{
|
||||
get => snap;
|
||||
@ -136,6 +162,10 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置滚动速度(范围:1-50)
|
||||
/// 值越大,滚动响应越快
|
||||
/// </summary>
|
||||
public float ScrollSpeed
|
||||
{
|
||||
get => scrollSpeed;
|
||||
@ -151,6 +181,10 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置鼠标滚轮的滚动速度(范围:10-50)
|
||||
/// 值越大,滚轮滚动的距离越大
|
||||
/// </summary>
|
||||
public float WheelSpeed
|
||||
{
|
||||
get => wheelSpeed;
|
||||
@ -171,12 +205,20 @@ namespace AlicizaX.UI
|
||||
|
||||
#region Public Properties - Components
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置 ViewHolder 模板数组
|
||||
/// 用于创建和复用列表项视图
|
||||
/// </summary>
|
||||
public ViewHolder[] Templates
|
||||
{
|
||||
get => templates;
|
||||
set => templates = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取内容容器的 RectTransform
|
||||
/// 所有列表项都会作为此容器的子对象
|
||||
/// </summary>
|
||||
public RectTransform Content
|
||||
{
|
||||
get
|
||||
@ -190,10 +232,21 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取滚动条组件
|
||||
/// </summary>
|
||||
public Scrollbar Scrollbar => scrollbar;
|
||||
|
||||
/// <summary>
|
||||
/// 获取滚动控制器组件
|
||||
/// </summary>
|
||||
public Scroller Scroller => scroller;
|
||||
|
||||
/// <summary>
|
||||
/// 获取视图提供器
|
||||
/// 负责创建、回收和管理 ViewHolder 实例
|
||||
/// 根据模板数量自动选择 SimpleViewProvider 或 MixedViewProvider
|
||||
/// </summary>
|
||||
public ViewProvider ViewProvider
|
||||
{
|
||||
get
|
||||
@ -213,8 +266,15 @@ namespace AlicizaX.UI
|
||||
|
||||
#region Public Properties - State
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置当前绑定的适配器
|
||||
/// 适配器负责提供数据和创建 ViewHolder
|
||||
/// </summary>
|
||||
public IAdapter RecyclerViewAdapter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置当前显示的列表项索引
|
||||
/// </summary>
|
||||
public int CurrentIndex
|
||||
{
|
||||
get => currentIndex;
|
||||
@ -225,7 +285,15 @@ namespace AlicizaX.UI
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// 当前索引改变时触发的事件
|
||||
/// 参数为新的索引值
|
||||
/// </summary>
|
||||
public Action<int> OnIndexChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 滚动位置改变时触发的事件
|
||||
/// </summary>
|
||||
public Action OnScrollValueChanged;
|
||||
|
||||
#endregion
|
||||
@ -287,6 +355,10 @@ namespace AlicizaX.UI
|
||||
|
||||
#region Public Methods - Setup
|
||||
|
||||
/// <summary>
|
||||
/// 设置数据适配器并初始化布局管理器
|
||||
/// </summary>
|
||||
/// <param name="adapter">要绑定的适配器实例</param>
|
||||
public void SetAdapter(IAdapter adapter)
|
||||
{
|
||||
if (adapter == null)
|
||||
@ -308,6 +380,10 @@ namespace AlicizaX.UI
|
||||
layoutManager.Padding = padding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置列表状态
|
||||
/// 清空所有 ViewHolder 并将滚动位置重置为起始位置
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
viewProvider?.Reset();
|
||||
@ -327,6 +403,10 @@ namespace AlicizaX.UI
|
||||
|
||||
#region Public Methods - Layout
|
||||
|
||||
/// <summary>
|
||||
/// 刷新列表显示
|
||||
/// 清空当前所有 ViewHolder 并根据当前滚动位置重新创建可见项
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
ViewProvider.Clear();
|
||||
@ -342,6 +422,10 @@ namespace AlicizaX.UI
|
||||
layoutManager.DoItemAnimation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求重新布局
|
||||
/// 重新计算内容大小、视口大小,并更新滚动条显示状态
|
||||
/// </summary>
|
||||
public void RequestLayout()
|
||||
{
|
||||
layoutManager.SetContentSize();
|
||||
@ -359,11 +443,20 @@ namespace AlicizaX.UI
|
||||
|
||||
#region Public Methods - Scrolling
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前的滚动位置
|
||||
/// </summary>
|
||||
/// <returns>当前滚动位置值,如果没有 Scroller 则返回 0</returns>
|
||||
public float GetScrollPosition()
|
||||
{
|
||||
return scroller != null ? scroller.Position : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 滚动到指定索引的列表项
|
||||
/// </summary>
|
||||
/// <param name="index">目标列表项的索引</param>
|
||||
/// <param name="smooth">是否使用平滑滚动动画</param>
|
||||
public void ScrollTo(int index, bool smooth = false)
|
||||
{
|
||||
if (!scroll || scroller == null) return;
|
||||
@ -378,6 +471,14 @@ namespace AlicizaX.UI
|
||||
UpdateCurrentIndex(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 滚动到指定索引的列表项,并使用指定的对齐方式
|
||||
/// </summary>
|
||||
/// <param name="index">目标列表项的索引</param>
|
||||
/// <param name="alignment">对齐方式(起始、居中或结束)</param>
|
||||
/// <param name="offset">额外的偏移量</param>
|
||||
/// <param name="smooth">是否使用平滑滚动动画</param>
|
||||
/// <param name="duration">滚动动画持续时间(秒)</param>
|
||||
public void ScrollToWithAlignment(int index, ScrollAlignment alignment, float offset = 0f, bool smooth = false, float duration = 0.3f)
|
||||
{
|
||||
if (!scroll || scroller == null) return;
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines how an item should be aligned when scrolling to it
|
||||
/// 定义滚动到列表项时的对齐方式
|
||||
/// </summary>
|
||||
public enum ScrollAlignment
|
||||
{
|
||||
/// <summary>
|
||||
/// Align item to the top/left of the viewport
|
||||
/// 将列表项对齐到视口的顶部/左侧
|
||||
/// </summary>
|
||||
Start,
|
||||
|
||||
/// <summary>
|
||||
/// Align item to the center of the viewport
|
||||
/// 将列表项对齐到视口的中心
|
||||
/// </summary>
|
||||
Center,
|
||||
|
||||
/// <summary>
|
||||
/// Align item to the bottom/right of the viewport
|
||||
/// 将列表项对齐到视口的底部/右侧
|
||||
/// </summary>
|
||||
End
|
||||
}
|
||||
|
||||
@ -2,14 +2,37 @@ using UnityEngine.Events;
|
||||
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 滚动控制器接口
|
||||
/// 定义滚动行为的基本契约
|
||||
/// </summary>
|
||||
public interface IScroller
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置当前滚动位置
|
||||
/// </summary>
|
||||
float Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 滚动到指定位置
|
||||
/// </summary>
|
||||
/// <param name="position">目标位置</param>
|
||||
/// <param name="smooth">是否使用平滑滚动</param>
|
||||
void ScrollTo(float position, bool smooth = false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 滚动位置改变事件
|
||||
/// </summary>
|
||||
public class ScrollerEvent : UnityEvent<float> { }
|
||||
|
||||
/// <summary>
|
||||
/// 滚动停止事件
|
||||
/// </summary>
|
||||
public class MoveStopEvent : UnityEvent { }
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽状态改变事件
|
||||
/// </summary>
|
||||
public class DraggingEvent : UnityEvent<bool> { }
|
||||
}
|
||||
|
||||
@ -3,14 +3,29 @@ using System.Collections.Generic;
|
||||
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// UGList 基类
|
||||
/// 提供简化的列表操作接口,封装 RecyclerView 和 Adapter 的交互
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">数据类型</typeparam>
|
||||
/// <typeparam name="TAdapter">适配器类型</typeparam>
|
||||
public abstract class UGListBase<TData, TAdapter> where TAdapter : Adapter<TData> where TData : ISimpleViewData
|
||||
{
|
||||
protected readonly RecyclerView _recyclerView;
|
||||
|
||||
protected readonly TAdapter _adapter;
|
||||
|
||||
/// <summary>
|
||||
/// 获取关联的 RecyclerView 实例
|
||||
/// </summary>
|
||||
public RecyclerView RecyclerView => _recyclerView;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">RecyclerView 实例</param>
|
||||
/// <param name="adapter">适配器实例</param>
|
||||
/// <param name="onItemClick">列表项点击回调</param>
|
||||
public UGListBase(RecyclerView recyclerView, TAdapter adapter, Action<TData> onItemClick = null)
|
||||
{
|
||||
_recyclerView = recyclerView;
|
||||
@ -27,10 +42,17 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取适配器实例
|
||||
/// </summary>
|
||||
public TAdapter Adapter => _adapter;
|
||||
|
||||
private List<TData> _datas;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置数据列表
|
||||
/// 设置时会自动更新适配器
|
||||
/// </summary>
|
||||
public List<TData> Data
|
||||
{
|
||||
get => _datas;
|
||||
@ -42,49 +64,106 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通用列表类
|
||||
/// 用于显示简单的单一类型数据列表
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">数据类型,必须实现 ISimpleViewData</typeparam>
|
||||
public class UGList<TData> : UGListBase<TData, Adapter<TData>> where TData : ISimpleViewData
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">RecyclerView 实例</param>
|
||||
/// <param name="onItemClick">列表项点击回调</param>
|
||||
public UGList(RecyclerView recyclerView, Action<TData> onItemClick = null)
|
||||
: base(recyclerView, new Adapter<TData>(recyclerView), onItemClick)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分组列表类
|
||||
/// 用于显示带有分组头的列表数据
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">数据类型,必须实现 IGroupViewData</typeparam>
|
||||
public class UGGroupList<TData> : UGListBase<TData, GroupAdapter<TData>> where TData : class, IGroupViewData, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">RecyclerView 实例</param>
|
||||
/// <param name="groupViewName">分组头视图名称</param>
|
||||
/// <param name="onItemClick">列表项点击回调</param>
|
||||
public UGGroupList(RecyclerView recyclerView, string groupViewName, Action<TData> onItemClick = null)
|
||||
: base(recyclerView, new GroupAdapter<TData>(recyclerView, groupViewName), onItemClick)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 循环列表类
|
||||
/// 用于实现无限循环滚动的列表
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">数据类型,必须实现 ISimpleViewData</typeparam>
|
||||
public class UGLoopList<TData> : UGListBase<TData, LoopAdapter<TData>> where TData : ISimpleViewData, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">RecyclerView 实例</param>
|
||||
/// <param name="onItemClick">列表项点击回调</param>
|
||||
public UGLoopList(RecyclerView recyclerView, Action<TData> onItemClick = null)
|
||||
: base(recyclerView, new LoopAdapter<TData>(recyclerView), onItemClick)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 混合列表类
|
||||
/// 用于显示多种不同类型的列表项
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">数据类型,必须实现 IMixedViewData</typeparam>
|
||||
public class UGMixedList<TData> : UGListBase<TData, MixedAdapter<TData>> where TData : IMixedViewData
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">RecyclerView 实例</param>
|
||||
/// <param name="onItemClick">列表项点击回调</param>
|
||||
public UGMixedList(RecyclerView recyclerView, Action<TData> onItemClick = null)
|
||||
: base(recyclerView, new MixedAdapter<TData>(recyclerView), onItemClick)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UGList 创建辅助类
|
||||
/// 提供便捷的静态方法来创建各种类型的列表
|
||||
/// </summary>
|
||||
public static class UGListCreateHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建通用列表
|
||||
/// </summary>
|
||||
public static UGList<TData> Create<TData>(RecyclerView recyclerView, Action<TData> onItemClick = null) where TData : ISimpleViewData
|
||||
=> new UGList<TData>(recyclerView, onItemClick);
|
||||
|
||||
/// <summary>
|
||||
/// 创建分组列表
|
||||
/// </summary>
|
||||
public static UGGroupList<TData> CreateGroup<TData>(RecyclerView recyclerView, string groupViewName, Action<TData> onItemClick = null) where TData : class, IGroupViewData, new()
|
||||
=> new UGGroupList<TData>(recyclerView, groupViewName, onItemClick);
|
||||
|
||||
/// <summary>
|
||||
/// 创建循环列表
|
||||
/// </summary>
|
||||
public static UGLoopList<TData> CreateLoop<TData>(RecyclerView recyclerView, Action<TData> onItemClick = null) where TData : ISimpleViewData, new()
|
||||
=> new UGLoopList<TData>(recyclerView, onItemClick);
|
||||
|
||||
/// <summary>
|
||||
/// 创建混合列表
|
||||
/// </summary>
|
||||
public static UGMixedList<TData> CreateMixed<TData>(RecyclerView recyclerView, Action<TData> onItemClick = null) where TData : IMixedViewData
|
||||
=> new UGMixedList<TData>(recyclerView, onItemClick);
|
||||
}
|
||||
|
||||
@ -4,24 +4,25 @@ using UnityEngine;
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for UGList to provide enhanced scrolling functionality
|
||||
/// UGList 扩展方法类
|
||||
/// 提供增强的滚动功能
|
||||
/// </summary>
|
||||
public static class UGListExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enable debug logging for ScrollTo operations
|
||||
/// 启用 ScrollTo 操作的调试日志
|
||||
/// </summary>
|
||||
public static bool DebugScrollTo { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Scrolls to a specific item with alignment and animation options
|
||||
/// 滚动到指定的列表项,支持对齐方式和动画选项
|
||||
/// </summary>
|
||||
/// <param name="ugList">The UGList instance</param>
|
||||
/// <param name="index">The index of the item to scroll to</param>
|
||||
/// <param name="alignment">How to align the item in the viewport (Start, Center, or End)</param>
|
||||
/// <param name="offset">Additional offset in pixels to apply after alignment</param>
|
||||
/// <param name="smooth">Whether to animate the scroll</param>
|
||||
/// <param name="duration">Animation duration in seconds (only used when smooth is true)</param>
|
||||
/// <param name="ugList">UGList 实例</param>
|
||||
/// <param name="index">要滚动到的列表项索引</param>
|
||||
/// <param name="alignment">列表项在视口中的对齐方式(起始、居中或结束)</param>
|
||||
/// <param name="offset">对齐后额外应用的偏移量(像素)</param>
|
||||
/// <param name="smooth">是否使用动画滚动</param>
|
||||
/// <param name="duration">动画持续时间(秒),仅在 smooth 为 true 时使用</param>
|
||||
public static void ScrollTo<TData, TAdapter>(
|
||||
this UGListBase<TData, TAdapter> ugList,
|
||||
int index,
|
||||
@ -47,7 +48,7 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scrolls to a specific item and aligns it at the start (top/left) of the viewport
|
||||
/// 滚动到指定的列表项并将其对齐到视口的起始位置(顶部/左侧)
|
||||
/// </summary>
|
||||
public static void ScrollToStart<TData, TAdapter>(
|
||||
this UGListBase<TData, TAdapter> ugList,
|
||||
@ -62,7 +63,7 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scrolls to a specific item and aligns it at the center of the viewport
|
||||
/// 滚动到指定的列表项并将其对齐到视口的中心位置
|
||||
/// </summary>
|
||||
public static void ScrollToCenter<TData, TAdapter>(
|
||||
this UGListBase<TData, TAdapter> ugList,
|
||||
@ -77,7 +78,7 @@ namespace AlicizaX.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scrolls to a specific item and aligns it at the end (bottom/right) of the viewport
|
||||
/// 滚动到指定的列表项并将其对齐到视口的结束位置(底部/右侧)
|
||||
/// </summary>
|
||||
public static void ScrollToEnd<TData, TAdapter>(
|
||||
this UGListBase<TData, TAdapter> ugList,
|
||||
|
||||
@ -5,10 +5,16 @@ using UnityEngine.UI;
|
||||
|
||||
namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 视图持有者基类,用于缓存和复用列表项视图
|
||||
/// </summary>
|
||||
public abstract class ViewHolder : MonoBehaviour
|
||||
{
|
||||
private RectTransform rectTransform;
|
||||
|
||||
/// <summary>
|
||||
/// 获取 RectTransform 组件
|
||||
/// </summary>
|
||||
public RectTransform RectTransform
|
||||
{
|
||||
get
|
||||
@ -23,24 +29,55 @@ namespace AlicizaX.UI
|
||||
private set { rectTransform = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视图名称,用于区分不同类型的视图
|
||||
/// </summary>
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前绑定的数据索引
|
||||
/// </summary>
|
||||
public int Index { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 选中状态
|
||||
/// </summary>
|
||||
public bool ChoiseState { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取视图的尺寸
|
||||
/// </summary>
|
||||
public Vector2 SizeDelta => RectTransform.sizeDelta;
|
||||
|
||||
private IButton _button;
|
||||
|
||||
/// <summary>
|
||||
/// 视图首次创建时调用
|
||||
/// </summary>
|
||||
protected internal virtual void OnStart()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视图被回收到对象池时调用
|
||||
/// </summary>
|
||||
protected internal virtual void OnRecycled()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定视图数据(抽象方法,子类必须实现)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据类型</typeparam>
|
||||
/// <param name="data">要绑定的数据</param>
|
||||
public abstract void BindViewData<T>(T data);
|
||||
|
||||
/// <summary>
|
||||
/// 绑定列表项点击事件
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据类型</typeparam>
|
||||
/// <param name="data">数据对象</param>
|
||||
/// <param name="action">点击回调</param>
|
||||
protected internal virtual void BindItemClick<T>(T data, Action<T> action)
|
||||
{
|
||||
if (_button is null && !TryGetComponent(out _button))
|
||||
@ -53,6 +90,10 @@ namespace AlicizaX.UI
|
||||
_button.onClick.AddListener(() => action?.Invoke(data));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定选中状态
|
||||
/// </summary>
|
||||
/// <param name="state">是否选中</param>
|
||||
protected internal void BindChoiceState(bool state)
|
||||
{
|
||||
if (ChoiseState != state)
|
||||
@ -62,6 +103,10 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 选中状态改变时的回调(可在子类中重写)
|
||||
/// </summary>
|
||||
/// <param name="state">是否选中</param>
|
||||
protected internal virtual void OnBindChoiceState(bool state)
|
||||
{
|
||||
}
|
||||
|
||||
@ -5,35 +5,78 @@ namespace AlicizaX.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供和管理 ViewHolder
|
||||
/// 负责 ViewHolder 的创建、回收和复用
|
||||
/// </summary>
|
||||
public abstract class ViewProvider
|
||||
{
|
||||
private readonly List<ViewHolder> viewHolders = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置数据适配器
|
||||
/// </summary>
|
||||
public IAdapter Adapter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置布局管理器
|
||||
/// </summary>
|
||||
public LayoutManager LayoutManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前所有活动的 ViewHolder 列表
|
||||
/// </summary>
|
||||
public List<ViewHolder> ViewHolders => viewHolders;
|
||||
|
||||
protected RecyclerView recyclerView;
|
||||
protected ViewHolder[] templates;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="recyclerView">关联的 RecyclerView 实例</param>
|
||||
/// <param name="templates">ViewHolder 模板数组</param>
|
||||
public ViewProvider(RecyclerView recyclerView, ViewHolder[] templates)
|
||||
{
|
||||
this.recyclerView = recyclerView;
|
||||
this.templates = templates;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据视图名称获取对应的模板(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <param name="viewName">视图名称</param>
|
||||
/// <returns>对应的 ViewHolder 模板</returns>
|
||||
public abstract ViewHolder GetTemplate(string viewName);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有模板(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <returns>所有 ViewHolder 模板数组</returns>
|
||||
public abstract ViewHolder[] GetTemplates();
|
||||
|
||||
/// <summary>
|
||||
/// 从对象池中分配一个 ViewHolder(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <param name="viewName">视图名称</param>
|
||||
/// <returns>分配的 ViewHolder 实例</returns>
|
||||
public abstract ViewHolder Allocate(string viewName);
|
||||
|
||||
/// <summary>
|
||||
/// 将 ViewHolder 回收到对象池(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
/// <param name="viewName">视图名称</param>
|
||||
/// <param name="viewHolder">要回收的 ViewHolder</param>
|
||||
public abstract void Free(string viewName, ViewHolder viewHolder);
|
||||
|
||||
/// <summary>
|
||||
/// 重置 ViewProvider 状态(抽象方法,由子类实现)
|
||||
/// </summary>
|
||||
public abstract void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// 创建指定索引的 ViewHolder
|
||||
/// 从对象池中获取或创建新的 ViewHolder,并进行布局和数据绑定
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
public void CreateViewHolder(int index)
|
||||
{
|
||||
for (int i = index; i < index + LayoutManager.Unit; i++)
|
||||
@ -51,6 +94,11 @@ namespace AlicizaX.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定索引的 ViewHolder
|
||||
/// 将 ViewHolder 从活动列表中移除并回收到对象池
|
||||
/// </summary>
|
||||
/// <param name="index">数据索引</param>
|
||||
public void RemoveViewHolder(int index)
|
||||
{
|
||||
for (int i = index; i < index + LayoutManager.Unit; i++)
|
||||
@ -104,6 +152,10 @@ namespace AlicizaX.UI
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有 ViewHolder
|
||||
/// 将所有活动的 ViewHolder 回收到对象池并清空列表
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var viewHolder in viewHolders)
|
||||
@ -125,6 +177,10 @@ namespace AlicizaX.UI
|
||||
return size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据项总数
|
||||
/// </summary>
|
||||
/// <returns>数据项总数,如果没有适配器则返回 0</returns>
|
||||
public int GetItemCount()
|
||||
{
|
||||
return Adapter == null ? 0 : Adapter.GetItemCount();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user