ScrollViewEx.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // -----------------------------------------------------------------------
  2. // <copyright file="ScrollViewEx.cs" company="AillieoTech">
  3. // Copyright (c) AillieoTech. All rights reserved.
  4. // </copyright>
  5. // -----------------------------------------------------------------------
  6. namespace TapSDK.UI.AillieoTech
  7. {
  8. using System;
  9. using UnityEngine;
  10. using UnityEngine.EventSystems;
  11. using UnityEngine.Serialization;
  12. [RequireComponent(typeof(RectTransform))]
  13. [DisallowMultipleComponent]
  14. public class ScrollViewEx : ScrollView
  15. {
  16. [SerializeField]
  17. [FormerlySerializedAs("m_pageSize")]
  18. private int pageSize = 50;
  19. private int startOffset = 0;
  20. private Func<int> realItemCountFunc;
  21. private Vector2 lastPosition;
  22. private bool reloadFlag = false;
  23. public override void SetUpdateFunc(Action<int, RectTransform> func)
  24. {
  25. if (func != null)
  26. {
  27. var f = func;
  28. func = (index, rect) =>
  29. {
  30. f(index + this.startOffset, rect);
  31. };
  32. }
  33. base.SetUpdateFunc(func);
  34. }
  35. public override void SetItemSizeFunc(Func<int, Vector2> func)
  36. {
  37. if (func != null)
  38. {
  39. var f = func;
  40. func = (index) =>
  41. {
  42. return f(index + this.startOffset);
  43. };
  44. }
  45. base.SetItemSizeFunc(func);
  46. }
  47. public override void SetItemCountFunc(Func<int> func)
  48. {
  49. this.realItemCountFunc = func;
  50. if (func != null)
  51. {
  52. var f = func;
  53. func = () => Mathf.Min(f(), this.pageSize);
  54. }
  55. base.SetItemCountFunc(func);
  56. }
  57. public override void OnDrag(PointerEventData eventData)
  58. {
  59. if (this.reloadFlag)
  60. {
  61. this.reloadFlag = false;
  62. this.OnEndDrag(eventData);
  63. this.OnBeginDrag(eventData);
  64. return;
  65. }
  66. base.OnDrag(eventData);
  67. }
  68. protected override void Awake()
  69. {
  70. base.Awake();
  71. this.lastPosition = Vector2.up;
  72. this.onValueChanged.AddListener(this.OnValueChanged);
  73. }
  74. protected override void InternalScrollTo(int index)
  75. {
  76. var count = 0;
  77. if (this.realItemCountFunc != null)
  78. {
  79. count = this.realItemCountFunc();
  80. }
  81. index = Mathf.Clamp(index, 0, count - 1);
  82. this.startOffset = Mathf.Clamp(index - (this.pageSize / 2), 0, count - this.itemCountFunc());
  83. this.UpdateData(true);
  84. base.InternalScrollTo(index - this.startOffset);
  85. }
  86. private void OnValueChanged(Vector2 position)
  87. {
  88. int toShow;
  89. int critical;
  90. bool downward;
  91. int pin;
  92. Vector2 delta = position - this.lastPosition;
  93. this.lastPosition = position;
  94. this.reloadFlag = false;
  95. if (((int)this.layoutType & flagScrollDirection) == 1)
  96. {
  97. // 垂直滚动 只计算y向
  98. if (delta.y < 0)
  99. {
  100. // 向上
  101. toShow = this.criticalItemIndex[CriticalItemType.DownToShow];
  102. critical = this.pageSize - 1;
  103. if (toShow < critical)
  104. {
  105. return;
  106. }
  107. pin = critical - 1;
  108. downward = false;
  109. }
  110. else if (delta.y > 0)
  111. {
  112. // 向下
  113. toShow = this.criticalItemIndex[CriticalItemType.UpToShow];
  114. critical = 0;
  115. if (toShow > critical)
  116. {
  117. return;
  118. }
  119. pin = critical + 1;
  120. downward = true;
  121. }
  122. else
  123. {
  124. return;
  125. }
  126. }
  127. else
  128. {
  129. // = 0
  130. // 水平滚动 只计算x向
  131. if (delta.x > 0)
  132. {
  133. // 向右
  134. toShow = this.criticalItemIndex[CriticalItemType.UpToShow];
  135. critical = 0;
  136. if (toShow > critical)
  137. {
  138. return;
  139. }
  140. pin = critical + 1;
  141. downward = true;
  142. }
  143. else if (delta.x < 0)
  144. {
  145. // 向左
  146. toShow = this.criticalItemIndex[CriticalItemType.DownToShow];
  147. critical = this.pageSize - 1;
  148. if (toShow < critical)
  149. {
  150. return;
  151. }
  152. pin = critical - 1;
  153. downward = false;
  154. }
  155. else
  156. {
  157. return;
  158. }
  159. }
  160. // 该翻页了 翻半页吧
  161. var old = this.startOffset;
  162. if (downward)
  163. {
  164. this.startOffset -= this.pageSize / 2;
  165. }
  166. else
  167. {
  168. this.startOffset += this.pageSize / 2;
  169. }
  170. var realDataCount = 0;
  171. if (this.realItemCountFunc != null)
  172. {
  173. realDataCount = this.realItemCountFunc();
  174. }
  175. this.startOffset = Mathf.Clamp(this.startOffset, 0, Mathf.Max(realDataCount - this.pageSize, 0));
  176. if (old != this.startOffset)
  177. {
  178. this.reloadFlag = true;
  179. // 记录 原先的速度
  180. Vector2 oldVelocity = this.velocity;
  181. // 计算 pin元素的世界坐标
  182. Rect rect = this.GetItemLocalRect(pin);
  183. Vector2 oldWorld = this.content.TransformPoint(rect.position);
  184. var dataCount = 0;
  185. if (this.itemCountFunc != null)
  186. {
  187. dataCount = this.itemCountFunc();
  188. }
  189. if (dataCount > 0)
  190. {
  191. this.EnsureItemRect(0);
  192. if (dataCount > 1)
  193. {
  194. this.EnsureItemRect(dataCount - 1);
  195. }
  196. }
  197. // 根据 pin元素的世界坐标 计算出content的position
  198. var pin2 = pin + old - this.startOffset;
  199. Rect rect2 = this.GetItemLocalRect(pin2);
  200. Vector2 newWorld = this.content.TransformPoint(rect2.position);
  201. Vector2 deltaWorld = newWorld - oldWorld;
  202. Vector2 deltaLocal = this.content.InverseTransformVector(deltaWorld);
  203. this.SetContentAnchoredPosition(this.content.anchoredPosition - deltaLocal);
  204. this.UpdateData(true);
  205. this.velocity = oldVelocity;
  206. }
  207. }
  208. }
  209. }