using UnityEngine; namespace AlicizaX.UI { /// /// 布局管理器抽象基类 /// 负责计算和管理 RecyclerView 中列表项的位置、大小和可见性 /// 子类需要实现具体的布局算法(如线性、网格、圆形等) /// [System.Serializable] public abstract class LayoutManager : ILayoutManager { protected Vector2 viewportSize; /// /// 获取视口大小(可见区域的尺寸) /// public Vector2 ViewportSize { get => viewportSize; private set => viewportSize = value; } protected Vector2 contentSize; /// /// 获取内容总大小(所有列表项占据的总尺寸) /// public Vector2 ContentSize { get => contentSize; private set => contentSize = value; } protected Vector2 contentOffset; /// /// 获取内容偏移量(用于对齐计算) /// public Vector2 ContentOffset { get => contentOffset; private set => contentOffset = value; } protected Vector2 viewportOffset; /// /// 获取视口偏移量(用于对齐计算) /// public Vector2 ViewportOffset { get => viewportOffset; private set => viewportOffset = value; } protected IAdapter adapter; /// /// 获取或设置数据适配器 /// public IAdapter Adapter { get => adapter; set => adapter = value; } protected ViewProvider viewProvider; /// /// 获取或设置视图提供器 /// public ViewProvider ViewProvider { get => viewProvider; set => viewProvider = value; } protected RecyclerView recyclerView; /// /// 获取或设置关联的 RecyclerView 实例 /// public virtual RecyclerView RecyclerView { get => recyclerView; set => recyclerView = value; } protected Direction direction; /// /// 获取或设置滚动方向 /// public Direction Direction { get => direction; set => direction = value; } protected Alignment alignment; /// /// 获取或设置对齐方式 /// public Alignment Alignment { get => alignment; set => alignment = value; } protected Vector2 spacing; /// /// 获取或设置列表项间距 /// public Vector2 Spacing { get => spacing; set => spacing = value; } protected Vector2 padding; /// /// 获取或设置内边距 /// public Vector2 Padding { get => padding; set => padding = value; } protected int unit = 1; /// /// 获取或设置布局单元(用于网格布局等,表示一次处理多少个项) /// public int Unit { get => unit; set => unit = value; } /// /// 获取当前滚动位置 /// public float ScrollPosition => recyclerView.GetScrollPosition(); public LayoutManager() { } /// /// 设置内容大小 /// 计算视口大小、内容大小以及各种偏移量 /// public void SetContentSize() { viewportSize = recyclerView.GetComponent().rect.size; contentSize = CalculateContentSize(); contentOffset = CalculateContentOffset(); viewportOffset = CalculateViewportOffset(); } /// /// 更新所有可见 ViewHolder 的布局 /// 遍历所有当前显示的 ViewHolder 并重新计算其位置 /// public void UpdateLayout() { foreach (var viewHolder in viewProvider.ViewHolders) { Layout(viewHolder, viewHolder.Index); } } /// /// 为指定的 ViewHolder 设置布局位置 /// /// 要布局的 ViewHolder /// ViewHolder 对应的数据索引 public virtual void Layout(ViewHolder viewHolder, int index) { Vector2 pos = CalculatePosition(index); Vector3 position = direction == Direction.Vertical ? new Vector3(pos.x - contentOffset.x, -pos.y + contentOffset.y, 0) : new Vector3(pos.x - contentOffset.x, -pos.y + contentOffset.y, 0); 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); float position = direction == Direction.Vertical ? vector2.y : vector2.x; return position + GetOffset() >= 0; } /// /// 判断起始位置的 ViewHolder 是否完全不可见 /// /// 数据索引 /// 如果完全不可见返回 true,否则返回 false public virtual bool IsFullInvisibleStart(int index) { Vector2 vector2 = CalculatePosition(index + unit); float position = direction == Direction.Vertical ? vector2.y : vector2.x; return position + GetOffset() < 0; } /// /// 判断结束位置的 ViewHolder 是否完全可见 /// /// 数据索引 /// 如果完全可见返回 true,否则返回 false public virtual bool IsFullVisibleEnd(int index) { Vector2 vector2 = CalculatePosition(index + unit); float position = direction == Direction.Vertical ? vector2.y : vector2.x; float viewLength = direction == Direction.Vertical ? viewportSize.y : viewportSize.x; return position + GetOffset() <= viewLength; } /// /// 判断结束位置的 ViewHolder 是否完全不可见 /// /// 数据索引 /// 如果完全不可见返回 true,否则返回 false public virtual bool IsFullInvisibleEnd(int index) { Vector2 vector2 = CalculatePosition(index); float position = direction == Direction.Vertical ? vector2.y : vector2.x; float viewLength = direction == Direction.Vertical ? viewportSize.y : viewportSize.x; return position + GetOffset() > viewLength; } /// /// 判断指定索引的 ViewHolder 是否可见(部分或完全) /// /// 数据索引 /// 如果可见返回 true,否则返回 false public virtual bool IsVisible(int index) { float position, viewLength; viewLength = direction == Direction.Vertical ? viewportSize.y : viewportSize.x; Vector2 vector2 = CalculatePosition(index); position = direction == Direction.Vertical ? vector2.y : vector2.x; if (position + GetOffset() > 0 && position + GetOffset() <= viewLength) { return true; } vector2 = CalculatePosition(index + unit); position = direction == Direction.Vertical ? vector2.y : vector2.x; if (position + GetOffset() > 0 && position + GetOffset() <= viewLength) { return true; } return false; } /// /// 获取适配内容大小 /// 根据对齐方式计算实际显示的内容长度 /// /// 适配后的内容大小 protected virtual float GetFitContentSize() { float len; if (direction == Direction.Vertical) { len = alignment == Alignment.Center ? Mathf.Min(contentSize.y, viewportSize.y) : viewportSize.y; } else { len = alignment == Alignment.Center ? Mathf.Min(contentSize.x, viewportSize.x) : viewportSize.x; } 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 } }