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
}
}