You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
7.7 KiB
265 lines
7.7 KiB
// ----------------------------------------------------------------------- |
|
// <copyright file="ScrollViewEx.cs" company="AillieoTech"> |
|
// Copyright (c) AillieoTech. All rights reserved. |
|
// </copyright> |
|
// ----------------------------------------------------------------------- |
|
|
|
namespace AillieoUtils |
|
{ |
|
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; |
|
|
|
private int realDataCount = 0; |
|
|
|
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); |
|
} |
|
|
|
protected override void CheckDataCountChange() |
|
{ |
|
base.CheckDataCountChange(); |
|
|
|
var newDataCount = 0; |
|
if (this.realItemCountFunc != null) |
|
{ |
|
newDataCount = this.realItemCountFunc(); |
|
} |
|
|
|
if (newDataCount < this.realDataCount) |
|
{ |
|
this.startOffset = Mathf.Clamp(this.startOffset, 0, Mathf.Max(this.realItemCountFunc() - this.pageSize, 0)); |
|
} |
|
|
|
this.realDataCount = newDataCount; |
|
} |
|
|
|
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; |
|
} |
|
} |
|
} |
|
}
|
|
|