TapCommon-Unity/Runtime/Internal/UI/ScrollViewEx/ScrollView/ScrollViewEx.cs

246 lines
7.1 KiB
C#

// -----------------------------------------------------------------------
// <copyright file="ScrollViewEx.cs" company="AillieoTech">
// Copyright (c) AillieoTech. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace TapTap.UI.AillieoTech
{
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
[RequireComponent(typeof(RectTransform))]
[DisallowMultipleComponent]
public class ScrollViewEx : ScrollView
{
[SerializeField]
[FormerlySerializedAs("m_pageSize")]
private int pageSize = 50;
private int startOffset = 0;
private Func<int> realItemCountFunc;
private Vector2 lastPosition;
private bool reloadFlag = false;
public override void SetUpdateFunc(Action<int, RectTransform> func)
{
if (func != null)
{
var f = func;
func = (index, rect) =>
{
f(index + this.startOffset, rect);
};
}
base.SetUpdateFunc(func);
}
public override void SetItemSizeFunc(Func<int, Vector2> func)
{
if (func != null)
{
var f = func;
func = (index) =>
{
return f(index + this.startOffset);
};
}
base.SetItemSizeFunc(func);
}
public override void SetItemCountFunc(Func<int> func)
{
this.realItemCountFunc = func;
if (func != null)
{
var f = func;
func = () => Mathf.Min(f(), this.pageSize);
}
base.SetItemCountFunc(func);
}
public override void OnDrag(PointerEventData eventData)
{
if (this.reloadFlag)
{
this.reloadFlag = false;
this.OnEndDrag(eventData);
this.OnBeginDrag(eventData);
return;
}
base.OnDrag(eventData);
}
protected override void Awake()
{
base.Awake();
this.lastPosition = Vector2.up;
this.onValueChanged.AddListener(this.OnValueChanged);
}
protected override void InternalScrollTo(int index)
{
var count = 0;
if (this.realItemCountFunc != null)
{
count = this.realItemCountFunc();
}
index = Mathf.Clamp(index, 0, count - 1);
this.startOffset = Mathf.Clamp(index - (this.pageSize / 2), 0, count - this.itemCountFunc());
this.UpdateData(true);
base.InternalScrollTo(index - this.startOffset);
}
private void OnValueChanged(Vector2 position)
{
int toShow;
int critical;
bool downward;
int pin;
Vector2 delta = position - this.lastPosition;
this.lastPosition = position;
this.reloadFlag = false;
if (((int)this.layoutType & flagScrollDirection) == 1)
{
// 垂直滚动 只计算y向
if (delta.y < 0)
{
// 向上
toShow = this.criticalItemIndex[CriticalItemType.DownToShow];
critical = this.pageSize - 1;
if (toShow < critical)
{
return;
}
pin = critical - 1;
downward = false;
}
else if (delta.y > 0)
{
// 向下
toShow = this.criticalItemIndex[CriticalItemType.UpToShow];
critical = 0;
if (toShow > critical)
{
return;
}
pin = critical + 1;
downward = true;
}
else
{
return;
}
}
else
{
// = 0
// 水平滚动 只计算x向
if (delta.x > 0)
{
// 向右
toShow = this.criticalItemIndex[CriticalItemType.UpToShow];
critical = 0;
if (toShow > critical)
{
return;
}
pin = critical + 1;
downward = true;
}
else if (delta.x < 0)
{
// 向左
toShow = this.criticalItemIndex[CriticalItemType.DownToShow];
critical = this.pageSize - 1;
if (toShow < critical)
{
return;
}
pin = critical - 1;
downward = false;
}
else
{
return;
}
}
// 该翻页了 翻半页吧
var old = this.startOffset;
if (downward)
{
this.startOffset -= this.pageSize / 2;
}
else
{
this.startOffset += this.pageSize / 2;
}
var realDataCount = 0;
if (this.realItemCountFunc != null)
{
realDataCount = this.realItemCountFunc();
}
this.startOffset = Mathf.Clamp(this.startOffset, 0, Mathf.Max(realDataCount - this.pageSize, 0));
if (old != this.startOffset)
{
this.reloadFlag = true;
// 记录 原先的速度
Vector2 oldVelocity = this.velocity;
// 计算 pin元素的世界坐标
Rect rect = this.GetItemLocalRect(pin);
Vector2 oldWorld = this.content.TransformPoint(rect.position);
var dataCount = 0;
if (this.itemCountFunc != null)
{
dataCount = this.itemCountFunc();
}
if (dataCount > 0)
{
this.EnsureItemRect(0);
if (dataCount > 1)
{
this.EnsureItemRect(dataCount - 1);
}
}
// 根据 pin元素的世界坐标 计算出content的position
var pin2 = pin + old - this.startOffset;
Rect rect2 = this.GetItemLocalRect(pin2);
Vector2 newWorld = this.content.TransformPoint(rect2.position);
Vector2 deltaWorld = newWorld - oldWorld;
Vector2 deltaLocal = this.content.InverseTransformVector(deltaWorld);
this.SetContentAnchoredPosition(this.content.anchoredPosition - deltaLocal);
this.UpdateData(true);
this.velocity = oldVelocity;
}
}
}
}