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

376 lines
10 KiB
C#
Raw Normal View History

2025-03-12 20:59:12 +08:00
using System;
using UnityEngine;
2025-05-30 10:32:08 +08:00
using UnityEngine.Serialization;
2025-03-12 20:59:12 +08:00
using UnityEngine.UI;
namespace AlicizaX.UI.RecyclerView
{
public class RecyclerView : MonoBehaviour
{
[SerializeField] private Direction direction;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public Direction Direction
{
get => direction;
set => direction = value;
}
[SerializeField] private Alignment alignment;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public Alignment Alignment
{
get => alignment;
set => alignment = value;
}
2025-05-27 15:40:22 +08:00
[SerializeField] private RectTransform content;
public RectTransform Content
{
get
{
if (content == null)
{
content = transform.GetChild(0).GetComponent<RectTransform>();
}
return content;
}
}
2025-03-12 20:59:12 +08:00
[SerializeField] private Vector2 spacing;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public Vector2 Spacing
{
get => spacing;
set => spacing = value;
}
[SerializeField] private Vector2 padding;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public Vector2 Padding
{
get => padding;
set => padding = value;
}
[SerializeField] private bool scroll;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public bool Scroll
{
get => scroll;
set => scroll = value;
}
[SerializeField] private bool snap;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public bool Snap
{
get => snap;
set => snap = value & scroll;
}
[SerializeField, Range(1f, 50f)] private float scrollSpeed = 7f;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public float ScrollSpeed
{
get => scrollSpeed;
set => scrollSpeed = value;
}
[SerializeField, Range(10f, 50f)] private float wheelSpeed = 30f;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public float WheeelSpeed
{
get => wheelSpeed;
set => wheelSpeed = value;
}
[SerializeField] private ViewHolder[] templates;
2025-04-02 14:53:08 +08:00
2025-03-12 20:59:12 +08:00
public ViewHolder[] Templates
{
get => templates;
set => templates = value;
}
2025-05-30 10:32:08 +08:00
[SerializeField] private string _scrollerTypeName;
[SerializeReference] private Scroller _scroller;
[SerializeField] private bool _showScrollBar;
[SerializeReference] private Scrollbar _scrollbar;
2025-03-12 20:59:12 +08:00
private ViewProvider viewProvider;
2025-05-30 10:32:08 +08:00
[SerializeField] private string _layoutManagerTypeName;
[SerializeReference] private LayoutManager _layoutManager;
2025-03-12 20:59:12 +08:00
private int startIndex, endIndex;
private int currentIndex;
2025-04-02 14:53:08 +08:00
2025-05-30 19:40:12 +08:00
public Scroller Scroller => _scroller;
2025-03-12 20:59:12 +08:00
public int CurrentIndex
{
get => currentIndex;
}
public bool CanScroll => true;
public ViewProvider ViewProvider
{
get
{
viewProvider ??= templates.Length > 1 ? new MixedViewProvider(this, templates) : new SimpleViewProvider(this, templates);
return viewProvider;
}
}
2025-04-02 14:53:08 +08:00
2025-05-30 10:32:08 +08:00
public Scrollbar Scrollbar => _scrollbar;
2025-03-12 20:59:12 +08:00
2025-05-30 13:43:08 +08:00
private IAdapter _adapter;
2025-03-12 20:59:12 +08:00
2025-05-30 10:32:08 +08:00
public LayoutManager LayoutManager => _layoutManager;
2025-03-12 20:59:12 +08:00
public Action<int> OnIndexChanged;
public Action OnScrollValueChanged;
private void OnValidate()
{
2025-05-30 10:32:08 +08:00
if (_scroller != null)
2025-03-12 20:59:12 +08:00
{
2025-05-30 10:32:08 +08:00
_scroller.ScrollSpeed = scrollSpeed;
_scroller.WheelSpeed = wheelSpeed;
2025-03-12 20:59:12 +08:00
}
}
private void OnScrollChanged(float pos)
{
2025-05-30 10:32:08 +08:00
_layoutManager.UpdateLayout();
2025-03-12 20:59:12 +08:00
if (Scrollbar != null)
{
2025-05-30 10:32:08 +08:00
Scrollbar.SetValueWithoutNotify(pos / _scroller.MaxPosition);
2025-03-12 20:59:12 +08:00
}
2025-05-30 10:32:08 +08:00
if (_layoutManager.IsFullInvisibleStart(startIndex))
2025-03-12 20:59:12 +08:00
{
viewProvider.RemoveViewHolder(startIndex);
2025-05-30 10:32:08 +08:00
startIndex += _layoutManager.Unit;
2025-03-12 20:59:12 +08:00
}
2025-05-30 10:32:08 +08:00
else if (_layoutManager.IsFullVisibleStart(startIndex))
2025-03-12 20:59:12 +08:00
{
if (startIndex == 0)
{
// TODO Do something, eg: Refresh
}
else
{
2025-05-30 10:32:08 +08:00
startIndex -= _layoutManager.Unit;
2025-03-12 20:59:12 +08:00
viewProvider.CreateViewHolder(startIndex);
}
}
2025-05-30 10:32:08 +08:00
if (_layoutManager.IsFullInvisibleEnd(endIndex))
2025-03-12 20:59:12 +08:00
{
viewProvider.RemoveViewHolder(endIndex);
2025-05-30 10:32:08 +08:00
endIndex -= _layoutManager.Unit;
2025-03-12 20:59:12 +08:00
}
2025-05-30 10:32:08 +08:00
else if (_layoutManager.IsFullVisibleEnd(endIndex))
2025-03-12 20:59:12 +08:00
{
2025-05-30 10:32:08 +08:00
if (endIndex >= viewProvider.GetItemCount() - _layoutManager.Unit)
2025-03-12 20:59:12 +08:00
{
// TODO Do something, eg: Load More
}
else
{
2025-05-30 10:32:08 +08:00
endIndex += _layoutManager.Unit;
2025-03-12 20:59:12 +08:00
viewProvider.CreateViewHolder(endIndex);
}
}
// 使用滚动条快速定位时,刷新整个列表
2025-05-30 10:32:08 +08:00
if (!_layoutManager.IsVisible(startIndex) || !_layoutManager.IsVisible(endIndex))
2025-03-12 20:59:12 +08:00
{
Refresh();
}
2025-05-30 10:32:08 +08:00
_layoutManager.DoItemAnimation();
2025-03-12 20:59:12 +08:00
OnScrollValueChanged?.Invoke();
}
private void OnMoveStoped()
{
if (Snap)
{
SnapTo();
}
}
private void OnScrollbarChanged(float ratio)
{
2025-05-30 10:32:08 +08:00
_scroller.ScrollToRatio(ratio);
2025-03-12 20:59:12 +08:00
}
private void OnScrollbarDragEnd()
{
2025-05-30 10:32:08 +08:00
if (_scroller.Position < _scroller.MaxPosition)
2025-03-12 20:59:12 +08:00
{
if (Snap)
{
SnapTo();
}
}
}
2025-04-02 14:53:08 +08:00
private void Awake()
{
for (int i = 0; i < templates.Length; i++)
{
templates[i].gameObject.SetActive(false);
}
2025-05-30 10:32:08 +08:00
ConfigScroller();
ConfigScrollbar();
2025-04-02 14:53:08 +08:00
}
2025-04-01 15:21:02 +08:00
private void OnDestroy()
{
viewProvider?.Dispose();
}
2025-03-12 20:59:12 +08:00
public void Reset()
{
viewProvider?.Reset();
2025-05-30 10:32:08 +08:00
if (_scroller != null)
2025-03-12 20:59:12 +08:00
{
2025-05-30 10:32:08 +08:00
_scroller.Position = 0;
2025-03-12 20:59:12 +08:00
}
2025-04-02 14:53:08 +08:00
2025-05-30 10:32:08 +08:00
if (_scrollbar != null)
2025-03-12 20:59:12 +08:00
{
2025-05-30 10:32:08 +08:00
_scrollbar.SetValueWithoutNotify(0);
2025-03-12 20:59:12 +08:00
}
}
2025-05-30 10:32:08 +08:00
public void SetAdapter(IAdapter adapter)
2025-03-12 20:59:12 +08:00
{
2025-05-30 13:43:08 +08:00
_adapter = adapter;
ViewProvider.Adapter = _adapter;
2025-05-30 10:32:08 +08:00
ViewProvider.LayoutManager = _layoutManager;
ViewProvider.LayoutManager = _layoutManager;
_layoutManager.RecyclerView = this;
2025-05-30 13:43:08 +08:00
_layoutManager.Adapter = _adapter;
2025-05-30 10:32:08 +08:00
_layoutManager.ViewProvider = viewProvider;
_layoutManager.Direction = direction;
_layoutManager.Alignment = alignment;
_layoutManager.Spacing = spacing;
_layoutManager.Padding = padding;
_layoutManager.CanScroll = CanScroll;
2025-03-12 20:59:12 +08:00
}
2025-05-30 10:32:08 +08:00
private void ConfigScroller()
{
if (_scroller != null)
2025-03-12 20:59:12 +08:00
{
2025-05-30 10:32:08 +08:00
_scroller.ScrollSpeed = scrollSpeed;
_scroller.WheelSpeed = wheelSpeed;
_scroller.Snap = Snap;
_scroller.OnValueChanged.AddListener(OnScrollChanged);
_scroller.OnMoveStoped.AddListener(OnMoveStoped);
2025-03-12 20:59:12 +08:00
}
}
2025-05-30 10:32:08 +08:00
private void ConfigScrollbar()
2025-03-12 20:59:12 +08:00
{
2025-05-30 10:32:08 +08:00
if (_showScrollBar && _scrollbar != null)
{
_scrollbar.gameObject.SetActive(scroll);
_scrollbar.onValueChanged.AddListener(OnScrollbarChanged);
_scrollbar.gameObject.AddComponent<ScrollbarEx>().OnDragEnd = OnScrollbarDragEnd;
}
2025-03-12 20:59:12 +08:00
}
public void Refresh()
{
ViewProvider.Clear();
2025-05-30 10:32:08 +08:00
startIndex = _layoutManager.GetStartIndex();
endIndex = _layoutManager.GetEndIndex();
for (int i = startIndex; i <= endIndex; i += _layoutManager.Unit)
2025-03-12 20:59:12 +08:00
{
ViewProvider.CreateViewHolder(i);
}
2025-05-30 10:32:08 +08:00
_layoutManager.DoItemAnimation();
2025-03-12 20:59:12 +08:00
}
public void RequestLayout()
{
2025-05-30 10:32:08 +08:00
_layoutManager.SetContentSize();
2025-03-12 20:59:12 +08:00
2025-05-30 10:32:08 +08:00
if (_scroller == null) return;
2025-03-12 20:59:12 +08:00
2025-05-30 10:32:08 +08:00
_scroller.Direction = direction;
_scroller.ViewSize = _layoutManager.ViewportSize;
_scroller.ContentSize = _layoutManager.ContentSize;
2025-03-12 20:59:12 +08:00
2025-05-30 10:32:08 +08:00
if (Scrollbar != null && _scroller.ContentSize != Vector2.zero)
2025-03-12 20:59:12 +08:00
{
2025-05-30 10:32:08 +08:00
if ((direction == Direction.Vertical && _layoutManager.ContentSize.y <= _layoutManager.ViewportSize.y) ||
(direction == Direction.Horizontal && _layoutManager.ContentSize.x <= _layoutManager.ViewportSize.x) ||
2025-03-12 20:59:12 +08:00
(direction == Direction.Custom))
{
Scrollbar.gameObject.SetActive(false);
}
else
{
Scrollbar.gameObject.SetActive(true);
2025-04-02 14:53:08 +08:00
Scrollbar.direction = direction == Direction.Vertical ? Scrollbar.Direction.TopToBottom : Scrollbar.Direction.LeftToRight;
2025-05-30 10:32:08 +08:00
Scrollbar.size = direction == Direction.Vertical ? _scroller.ViewSize.y / _scroller.ContentSize.y : _scroller.ViewSize.x / _scroller.ContentSize.x;
2025-03-12 20:59:12 +08:00
}
}
}
public float GetScrollPosition()
{
2025-05-30 10:32:08 +08:00
return _scroller ? _scroller.Position : 0;
2025-03-12 20:59:12 +08:00
}
public void ScrollTo(int index, bool smooth = false)
{
if (!scroll) return;
2025-05-30 10:32:08 +08:00
_scroller.ScrollTo(_layoutManager.IndexToPosition(index), smooth);
2025-03-12 20:59:12 +08:00
if (!smooth)
{
Refresh();
}
2025-05-30 13:43:08 +08:00
index %= _adapter.GetItemCount();
index = index < 0 ? _adapter.GetItemCount() + index : index;
2025-03-12 20:59:12 +08:00
if (currentIndex != index)
{
currentIndex = index;
OnIndexChanged?.Invoke(currentIndex);
}
}
2025-05-30 19:40:12 +08:00
2025-03-12 20:59:12 +08:00
private void SnapTo()
{
2025-05-30 10:32:08 +08:00
var index = _layoutManager.PositionToIndex(GetScrollPosition());
2025-03-12 20:59:12 +08:00
ScrollTo(index, true);
}
}
}