com.alicizax.unity.ui.exten.../Runtime/RecyclerView/Layout/LayoutManager.cs

368 lines
12 KiB
C#
Raw Normal View History

2025-03-12 20:59:12 +08:00
using UnityEngine;
namespace AlicizaX.UI
2025-03-12 20:59:12 +08:00
{
2026-03-11 14:18:07 +08:00
/// <summary>
/// 布局管理器抽象基类
/// 负责计算和管理 RecyclerView 中列表项的位置、大小和可见性
/// 子类需要实现具体的布局算法(如线性、网格、圆形等)
/// </summary>
2025-12-26 14:22:46 +08:00
[System.Serializable]
2025-03-12 20:59:12 +08:00
public abstract class LayoutManager : ILayoutManager
{
protected Vector2 viewportSize;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取视口大小(可见区域的尺寸)
/// </summary>
2025-03-12 20:59:12 +08:00
public Vector2 ViewportSize
{
get => viewportSize;
private set => viewportSize = value;
}
protected Vector2 contentSize;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取内容总大小(所有列表项占据的总尺寸)
/// </summary>
2025-03-12 20:59:12 +08:00
public Vector2 ContentSize
{
get => contentSize;
private set => contentSize = value;
}
protected Vector2 contentOffset;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取内容偏移量(用于对齐计算)
/// </summary>
2025-03-12 20:59:12 +08:00
public Vector2 ContentOffset
{
get => contentOffset;
private set => contentOffset = value;
}
protected Vector2 viewportOffset;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取视口偏移量(用于对齐计算)
/// </summary>
2025-03-12 20:59:12 +08:00
public Vector2 ViewportOffset
{
get => viewportOffset;
private set => viewportOffset = value;
}
protected IAdapter adapter;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取或设置数据适配器
/// </summary>
2025-03-12 20:59:12 +08:00
public IAdapter Adapter
{
get => adapter;
set => adapter = value;
}
protected ViewProvider viewProvider;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取或设置视图提供器
/// </summary>
2025-03-12 20:59:12 +08:00
public ViewProvider ViewProvider
{
get => viewProvider;
set => viewProvider = value;
}
protected RecyclerView recyclerView;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取或设置关联的 RecyclerView 实例
/// </summary>
2025-03-12 20:59:12 +08:00
public virtual RecyclerView RecyclerView
{
get => recyclerView;
set => recyclerView = value;
}
protected Direction direction;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取或设置滚动方向
/// </summary>
2025-03-12 20:59:12 +08:00
public Direction Direction
{
get => direction;
set => direction = value;
}
protected Alignment alignment;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取或设置对齐方式
/// </summary>
2025-03-12 20:59:12 +08:00
public Alignment Alignment
{
get => alignment;
set => alignment = value;
}
protected Vector2 spacing;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取或设置列表项间距
/// </summary>
2025-03-12 20:59:12 +08:00
public Vector2 Spacing
{
get => spacing;
set => spacing = value;
}
protected Vector2 padding;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取或设置内边距
/// </summary>
2025-03-12 20:59:12 +08:00
public Vector2 Padding
{
get => padding;
set => padding = value;
}
protected int unit = 1;
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取或设置布局单元(用于网格布局等,表示一次处理多少个项)
/// </summary>
2025-03-12 20:59:12 +08:00
public int Unit
{
get => unit;
set => unit = value;
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取当前滚动位置
/// </summary>
2025-03-12 20:59:12 +08:00
public float ScrollPosition => recyclerView.GetScrollPosition();
2025-12-26 14:22:46 +08:00
public LayoutManager() { }
2025-03-12 20:59:12 +08:00
2026-03-11 14:18:07 +08:00
/// <summary>
/// 设置内容大小
/// 计算视口大小、内容大小以及各种偏移量
/// </summary>
2025-03-12 20:59:12 +08:00
public void SetContentSize()
{
viewportSize = recyclerView.GetComponent<RectTransform>().rect.size;
contentSize = CalculateContentSize();
contentOffset = CalculateContentOffset();
viewportOffset = CalculateViewportOffset();
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 更新所有可见 ViewHolder 的布局
/// 遍历所有当前显示的 ViewHolder 并重新计算其位置
/// </summary>
2025-03-12 20:59:12 +08:00
public void UpdateLayout()
{
foreach (var viewHolder in viewProvider.ViewHolders)
{
Layout(viewHolder, viewHolder.Index);
}
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 为指定的 ViewHolder 设置布局位置
/// </summary>
/// <param name="viewHolder">要布局的 ViewHolder</param>
/// <param name="index">ViewHolder 对应的数据索引</param>
2025-03-12 20:59:12 +08:00
public virtual void Layout(ViewHolder viewHolder, int index)
{
Vector2 pos = CalculatePosition(index);
2025-12-26 14:22:46 +08:00
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);
2025-03-12 20:59:12 +08:00
viewHolder.RectTransform.anchoredPosition3D = position;
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 计算内容总大小(抽象方法,由子类实现)
/// </summary>
/// <returns>内容的总尺寸</returns>
2025-03-12 20:59:12 +08:00
public abstract Vector2 CalculateContentSize();
2026-03-11 14:18:07 +08:00
/// <summary>
/// 计算指定索引的 ViewHolder 位置(抽象方法,由子类实现)
/// </summary>
/// <param name="index">数据索引</param>
/// <returns>ViewHolder 的位置</returns>
2025-03-12 20:59:12 +08:00
public abstract Vector2 CalculatePosition(int index);
2026-03-11 14:18:07 +08:00
/// <summary>
/// 计算内容偏移量(抽象方法,由子类实现)
/// </summary>
/// <returns>内容偏移量</returns>
2025-03-12 20:59:12 +08:00
public abstract Vector2 CalculateContentOffset();
2026-03-11 14:18:07 +08:00
/// <summary>
/// 计算视口偏移量(抽象方法,由子类实现)
/// </summary>
/// <returns>视口偏移量</returns>
2025-03-12 20:59:12 +08:00
public abstract Vector2 CalculateViewportOffset();
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取当前可见区域的起始索引(抽象方法,由子类实现)
/// </summary>
/// <returns>起始索引</returns>
2025-03-12 20:59:12 +08:00
public abstract int GetStartIndex();
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取当前可见区域的结束索引(抽象方法,由子类实现)
/// </summary>
/// <returns>结束索引</returns>
2025-03-12 20:59:12 +08:00
public abstract int GetEndIndex();
2026-03-11 14:18:07 +08:00
/// <summary>
/// 将数据索引转换为滚动位置(抽象方法,由子类实现)
/// </summary>
/// <param name="index">数据索引</param>
/// <returns>对应的滚动位置</returns>
2025-03-12 20:59:12 +08:00
public abstract float IndexToPosition(int index);
2026-03-11 14:18:07 +08:00
/// <summary>
/// 将滚动位置转换为数据索引(抽象方法,由子类实现)
/// </summary>
/// <param name="position">滚动位置</param>
/// <returns>对应的数据索引</returns>
2025-03-12 20:59:12 +08:00
public abstract int PositionToIndex(float position);
2026-03-11 14:18:07 +08:00
/// <summary>
/// 执行列表项动画(虚方法,子类可选择性重写)
/// </summary>
2025-12-26 14:22:46 +08:00
public virtual void DoItemAnimation() { }
2025-03-12 20:59:12 +08:00
2026-03-11 14:18:07 +08:00
/// <summary>
/// 判断起始位置的 ViewHolder 是否完全可见
/// </summary>
/// <param name="index">数据索引</param>
/// <returns>如果完全可见返回 true否则返回 false</returns>
2025-03-12 20:59:12 +08:00
public virtual bool IsFullVisibleStart(int index)
{
Vector2 vector2 = CalculatePosition(index);
float position = direction == Direction.Vertical ? vector2.y : vector2.x;
return position + GetOffset() >= 0;
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 判断起始位置的 ViewHolder 是否完全不可见
/// </summary>
/// <param name="index">数据索引</param>
/// <returns>如果完全不可见返回 true否则返回 false</returns>
2025-03-12 20:59:12 +08:00
public virtual bool IsFullInvisibleStart(int index)
{
Vector2 vector2 = CalculatePosition(index + unit);
float position = direction == Direction.Vertical ? vector2.y : vector2.x;
return position + GetOffset() < 0;
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 判断结束位置的 ViewHolder 是否完全可见
/// </summary>
/// <param name="index">数据索引</param>
/// <returns>如果完全可见返回 true否则返回 false</returns>
2025-03-12 20:59:12 +08:00
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;
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 判断结束位置的 ViewHolder 是否完全不可见
/// </summary>
/// <param name="index">数据索引</param>
/// <returns>如果完全不可见返回 true否则返回 false</returns>
2025-03-12 20:59:12 +08:00
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;
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 判断指定索引的 ViewHolder 是否可见(部分或完全)
/// </summary>
/// <param name="index">数据索引</param>
/// <returns>如果可见返回 true否则返回 false</returns>
2025-03-12 20:59:12 +08:00
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;
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取适配内容大小
/// 根据对齐方式计算实际显示的内容长度
/// </summary>
/// <returns>适配后的内容大小</returns>
2025-03-12 20:59:12 +08:00
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;
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 获取偏移量
/// 计算内容偏移和视口偏移的组合值
/// </summary>
/// <returns>总偏移量</returns>
2025-03-12 20:59:12 +08:00
protected virtual float GetOffset()
{
return direction == Direction.Vertical ? -contentOffset.y + viewportOffset.y : -contentOffset.x + viewportOffset.x;
}
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 滚动方向枚举
/// </summary>
2025-03-12 20:59:12 +08:00
public enum Direction
{
2026-03-11 14:18:07 +08:00
/// <summary>垂直滚动</summary>
2025-12-26 14:22:46 +08:00
Vertical = 0,
2026-03-11 14:18:07 +08:00
/// <summary>水平滚动</summary>
2025-12-26 14:22:46 +08:00
Horizontal = 1,
2026-03-11 14:18:07 +08:00
/// <summary>自定义滚动</summary>
2025-12-26 14:22:46 +08:00
Custom = 2
2025-03-12 20:59:12 +08:00
}
2026-03-11 14:18:07 +08:00
/// <summary>
/// 对齐方式枚举
/// </summary>
2025-03-12 20:59:12 +08:00
public enum Alignment
{
2026-03-11 14:18:07 +08:00
/// <summary>左对齐</summary>
2025-03-12 20:59:12 +08:00
Left,
2026-03-11 14:18:07 +08:00
/// <summary>居中对齐</summary>
2025-03-12 20:59:12 +08:00
Center,
2026-03-11 14:18:07 +08:00
/// <summary>顶部对齐</summary>
2025-03-12 20:59:12 +08:00
Top
}
}