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