ScrollView.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. // -----------------------------------------------------------------------
  2. // <copyright file="ScrollView.cs" company="AillieoTech">
  3. // Copyright (c) AillieoTech. All rights reserved.
  4. // </copyright>
  5. // -----------------------------------------------------------------------
  6. namespace TapSDK.UI.AillieoTech
  7. {
  8. using System;
  9. using System.Collections;
  10. using System.Collections.Generic;
  11. using UnityEngine;
  12. using UnityEngine.Serialization;
  13. using UnityEngine.UI;
  14. [RequireComponent(typeof(RectTransform))]
  15. [DisallowMultipleComponent]
  16. public class ScrollView : ScrollRect
  17. {
  18. [Tooltip("默认item尺寸")]
  19. public Vector2 defaultItemSize;
  20. [Tooltip("item的模板")]
  21. public RectTransform itemTemplate;
  22. // 0001
  23. protected const int flagScrollDirection = 1;
  24. [SerializeField]
  25. [FormerlySerializedAs("m_layoutType")]
  26. protected ItemLayoutType layoutType = ItemLayoutType.Vertical;
  27. // 只保存4个临界index
  28. protected int[] criticalItemIndex = new int[4];
  29. // callbacks for items
  30. protected Action<int, RectTransform> updateFunc;
  31. protected Func<int, Vector2> itemSizeFunc;
  32. protected Func<int> itemCountFunc;
  33. protected Func<int, RectTransform> itemGetFunc;
  34. protected Action<RectTransform> itemRecycleFunc;
  35. private readonly List<ScrollItemWithRect> managedItems = new List<ScrollItemWithRect>();
  36. private Rect refRect;
  37. // resource management
  38. private SimpleObjPool<RectTransform> itemPool = null;
  39. private int dataCount = 0;
  40. [Tooltip("初始化时池内item数量")]
  41. [SerializeField]
  42. private int poolSize;
  43. // status
  44. private bool initialized = false;
  45. private int willUpdateData = 0;
  46. private Vector3[] viewWorldConers = new Vector3[4];
  47. private Vector3[] rectCorners = new Vector3[2];
  48. // for hide and show
  49. public enum ItemLayoutType
  50. {
  51. // 最后一位表示滚动方向
  52. Vertical = 0b0001, // 0001
  53. Horizontal = 0b0010, // 0010
  54. VerticalThenHorizontal = 0b0100, // 0100
  55. HorizontalThenVertical = 0b0101, // 0101
  56. }
  57. public virtual void SetUpdateFunc(Action<int, RectTransform> func)
  58. {
  59. this.updateFunc = func;
  60. }
  61. public virtual void SetItemSizeFunc(Func<int, Vector2> func)
  62. {
  63. this.itemSizeFunc = func;
  64. }
  65. public virtual void SetItemCountFunc(Func<int> func)
  66. {
  67. this.itemCountFunc = func;
  68. }
  69. public void SetItemGetAndRecycleFunc(Func<int, RectTransform> getFunc, Action<RectTransform> recycleFunc)
  70. {
  71. if (getFunc != null && recycleFunc != null)
  72. {
  73. this.itemGetFunc = getFunc;
  74. this.itemRecycleFunc = recycleFunc;
  75. }
  76. else
  77. {
  78. this.itemGetFunc = null;
  79. this.itemRecycleFunc = null;
  80. }
  81. }
  82. public void ResetAllDelegates()
  83. {
  84. this.SetUpdateFunc(null);
  85. this.SetItemSizeFunc(null);
  86. this.SetItemCountFunc(null);
  87. this.SetItemGetAndRecycleFunc(null, null);
  88. }
  89. public void UpdateData(bool immediately = true)
  90. {
  91. if (immediately)
  92. {
  93. this.willUpdateData |= 3; // 0011
  94. this.InternalUpdateData();
  95. }
  96. else
  97. {
  98. if (this.willUpdateData == 0 && this.IsActive())
  99. {
  100. this.StartCoroutine(this.DelayUpdateData());
  101. }
  102. this.willUpdateData |= 3;
  103. }
  104. }
  105. public void UpdateDataIncrementally(bool immediately = true)
  106. {
  107. if (immediately)
  108. {
  109. this.willUpdateData |= 1; // 0001
  110. this.InternalUpdateData();
  111. }
  112. else
  113. {
  114. if (this.willUpdateData == 0)
  115. {
  116. this.StartCoroutine(this.DelayUpdateData());
  117. }
  118. this.willUpdateData |= 1;
  119. }
  120. }
  121. public void ScrollTo(int index)
  122. {
  123. this.InternalScrollTo(index);
  124. }
  125. protected override void OnEnable()
  126. {
  127. base.OnEnable();
  128. if (this.willUpdateData != 0)
  129. {
  130. this.StartCoroutine(this.DelayUpdateData());
  131. }
  132. }
  133. protected override void OnDisable()
  134. {
  135. this.initialized = false;
  136. base.OnDisable();
  137. }
  138. protected virtual void InternalScrollTo(int index)
  139. {
  140. index = Mathf.Clamp(index, 0, this.dataCount - 1);
  141. this.EnsureItemRect(index);
  142. Rect r = this.managedItems[index].rect;
  143. var dir = (int)this.layoutType & flagScrollDirection;
  144. if (dir == 1)
  145. {
  146. // vertical
  147. var value = 1 - (-r.yMax / (this.content.sizeDelta.y - this.refRect.height));
  148. this.SetNormalizedPosition(value, 1);
  149. }
  150. else
  151. {
  152. // horizontal
  153. var value = r.xMin / (this.content.sizeDelta.x - this.refRect.width);
  154. this.SetNormalizedPosition(value, 0);
  155. }
  156. }
  157. protected override void SetContentAnchoredPosition(Vector2 position)
  158. {
  159. base.SetContentAnchoredPosition(position);
  160. this.UpdateCriticalItems();
  161. }
  162. protected override void SetNormalizedPosition(float value, int axis)
  163. {
  164. base.SetNormalizedPosition(value, axis);
  165. this.ResetCriticalItems();
  166. }
  167. protected void EnsureItemRect(int index)
  168. {
  169. if (!this.managedItems[index].rectDirty)
  170. {
  171. // 已经是干净的了
  172. return;
  173. }
  174. ScrollItemWithRect firstItem = this.managedItems[0];
  175. if (firstItem.rectDirty)
  176. {
  177. Vector2 firstSize = this.GetItemSize(0);
  178. firstItem.rect = CreateWithLeftTopAndSize(Vector2.zero, firstSize);
  179. firstItem.rectDirty = false;
  180. }
  181. // 当前item之前的最近的已更新的rect
  182. var nearestClean = 0;
  183. for (var i = index; i >= 0; --i)
  184. {
  185. if (!this.managedItems[i].rectDirty)
  186. {
  187. nearestClean = i;
  188. break;
  189. }
  190. }
  191. // 需要更新 从 nearestClean 到 index 的尺寸
  192. Rect nearestCleanRect = this.managedItems[nearestClean].rect;
  193. Vector2 curPos = GetLeftTop(nearestCleanRect);
  194. Vector2 size = nearestCleanRect.size;
  195. this.MovePos(ref curPos, size);
  196. for (var i = nearestClean + 1; i <= index; i++)
  197. {
  198. size = this.GetItemSize(i);
  199. this.managedItems[i].rect = CreateWithLeftTopAndSize(curPos, size);
  200. this.managedItems[i].rectDirty = false;
  201. this.MovePos(ref curPos, size);
  202. }
  203. var range = new Vector2(Mathf.Abs(curPos.x), Mathf.Abs(curPos.y));
  204. switch (this.layoutType)
  205. {
  206. case ItemLayoutType.VerticalThenHorizontal:
  207. range.x += size.x;
  208. range.y = this.refRect.height;
  209. break;
  210. case ItemLayoutType.HorizontalThenVertical:
  211. range.x = this.refRect.width;
  212. if (curPos.x != 0)
  213. {
  214. range.y += size.y;
  215. }
  216. break;
  217. default:
  218. break;
  219. }
  220. this.content.sizeDelta = range;
  221. }
  222. protected override void OnDestroy()
  223. {
  224. if (this.itemPool != null)
  225. {
  226. this.itemPool.Purge();
  227. }
  228. }
  229. protected Rect GetItemLocalRect(int index)
  230. {
  231. if (index >= 0 && index < this.dataCount)
  232. {
  233. this.EnsureItemRect(index);
  234. return this.managedItems[index].rect;
  235. }
  236. return (Rect)default;
  237. }
  238. #if UNITY_EDITOR
  239. protected override void OnValidate()
  240. {
  241. var dir = (int)this.layoutType & flagScrollDirection;
  242. if (dir == 1)
  243. {
  244. // vertical
  245. if (this.horizontalScrollbar != null)
  246. {
  247. this.horizontalScrollbar.gameObject.SetActive(false);
  248. this.horizontalScrollbar = null;
  249. }
  250. }
  251. else
  252. {
  253. // horizontal
  254. if (this.verticalScrollbar != null)
  255. {
  256. this.verticalScrollbar.gameObject.SetActive(false);
  257. this.verticalScrollbar = null;
  258. }
  259. }
  260. base.OnValidate();
  261. }
  262. #endif
  263. private static Vector2 GetLeftTop(Rect rect)
  264. {
  265. Vector2 ret = rect.position;
  266. ret.y += rect.size.y;
  267. return ret;
  268. }
  269. private static Rect CreateWithLeftTopAndSize(Vector2 leftTop, Vector2 size)
  270. {
  271. Vector2 leftBottom = leftTop - new Vector2(0, size.y);
  272. return new Rect(leftBottom, size);
  273. }
  274. private IEnumerator DelayUpdateData()
  275. {
  276. yield return new WaitForEndOfFrame();
  277. this.InternalUpdateData();
  278. }
  279. private void InternalUpdateData()
  280. {
  281. if (!this.IsActive())
  282. {
  283. this.willUpdateData |= 3;
  284. return;
  285. }
  286. if (!this.initialized)
  287. {
  288. this.InitScrollView();
  289. }
  290. var newDataCount = 0;
  291. var keepOldItems = (this.willUpdateData & 2) == 0;
  292. if (this.itemCountFunc != null)
  293. {
  294. newDataCount = this.itemCountFunc();
  295. }
  296. if (newDataCount != this.managedItems.Count)
  297. {
  298. if (this.managedItems.Count < newDataCount)
  299. {
  300. // 增加
  301. if (!keepOldItems)
  302. {
  303. foreach (var itemWithRect in this.managedItems)
  304. {
  305. // 重置所有rect
  306. itemWithRect.rectDirty = true;
  307. }
  308. }
  309. while (this.managedItems.Count < newDataCount)
  310. {
  311. this.managedItems.Add(new ScrollItemWithRect());
  312. }
  313. }
  314. else
  315. {
  316. // 减少 保留空位 避免GC
  317. for (int i = 0, count = this.managedItems.Count; i < count; ++i)
  318. {
  319. if (i < newDataCount)
  320. {
  321. // 重置所有rect
  322. if (!keepOldItems)
  323. {
  324. this.managedItems[i].rectDirty = true;
  325. }
  326. if (i == newDataCount - 1)
  327. {
  328. this.managedItems[i].rectDirty = true;
  329. }
  330. }
  331. // 超出部分 清理回收item
  332. if (i >= newDataCount)
  333. {
  334. this.managedItems[i].rectDirty = true;
  335. if (this.managedItems[i].item != null)
  336. {
  337. this.RecycleOldItem(this.managedItems[i].item);
  338. this.managedItems[i].item = null;
  339. }
  340. }
  341. }
  342. }
  343. }
  344. else
  345. {
  346. if (!keepOldItems)
  347. {
  348. for (int i = 0, count = this.managedItems.Count; i < count; ++i)
  349. {
  350. // 重置所有rect
  351. this.managedItems[i].rectDirty = true;
  352. }
  353. }
  354. }
  355. this.dataCount = newDataCount;
  356. this.ResetCriticalItems();
  357. this.willUpdateData = 0;
  358. }
  359. private void ResetCriticalItems()
  360. {
  361. bool hasItem, shouldShow;
  362. int firstIndex = -1, lastIndex = -1;
  363. for (var i = 0; i < this.dataCount; i++)
  364. {
  365. hasItem = this.managedItems[i].item != null;
  366. shouldShow = this.ShouldItemSeenAtIndex(i);
  367. if (shouldShow)
  368. {
  369. if (firstIndex == -1)
  370. {
  371. firstIndex = i;
  372. }
  373. lastIndex = i;
  374. }
  375. if (hasItem && shouldShow)
  376. {
  377. // 应显示且已显示
  378. this.SetDataForItemAtIndex(this.managedItems[i].item, i);
  379. continue;
  380. }
  381. if (hasItem == shouldShow)
  382. {
  383. // 不应显示且未显示
  384. // if (firstIndex != -1)
  385. // {
  386. // // 已经遍历完所有要显示的了 后边的先跳过
  387. // break;
  388. // }
  389. continue;
  390. }
  391. if (hasItem && !shouldShow)
  392. {
  393. // 不该显示 但是有
  394. this.RecycleOldItem(this.managedItems[i].item);
  395. this.managedItems[i].item = null;
  396. continue;
  397. }
  398. if (shouldShow && !hasItem)
  399. {
  400. // 需要显示 但是没有
  401. RectTransform item = this.GetNewItem(i);
  402. this.OnGetItemForDataIndex(item, i);
  403. this.managedItems[i].item = item;
  404. continue;
  405. }
  406. }
  407. // content.localPosition = Vector2.zero;
  408. this.criticalItemIndex[CriticalItemType.UpToHide] = firstIndex;
  409. this.criticalItemIndex[CriticalItemType.DownToHide] = lastIndex;
  410. this.criticalItemIndex[CriticalItemType.UpToShow] = Mathf.Max(firstIndex - 1, 0);
  411. this.criticalItemIndex[CriticalItemType.DownToShow] = Mathf.Min(lastIndex + 1, this.dataCount - 1);
  412. }
  413. private RectTransform GetCriticalItem(int type)
  414. {
  415. var index = this.criticalItemIndex[type];
  416. if (index >= 0 && index < this.dataCount)
  417. {
  418. return this.managedItems[index].item;
  419. }
  420. return null;
  421. }
  422. private void UpdateCriticalItems()
  423. {
  424. var dirty = true;
  425. while (dirty)
  426. {
  427. dirty = false;
  428. for (int i = CriticalItemType.UpToHide; i <= CriticalItemType.DownToShow; i++)
  429. {
  430. if (i <= CriticalItemType.DownToHide)
  431. {
  432. // 隐藏离开可见区域的item
  433. dirty = dirty || this.CheckAndHideItem(i);
  434. }
  435. else
  436. {
  437. // 显示进入可见区域的item
  438. dirty = dirty || this.CheckAndShowItem(i);
  439. }
  440. }
  441. }
  442. }
  443. private bool CheckAndHideItem(int criticalItemType)
  444. {
  445. RectTransform item = this.GetCriticalItem(criticalItemType);
  446. var criticalIndex = this.criticalItemIndex[criticalItemType];
  447. if (item != null && !this.ShouldItemSeenAtIndex(criticalIndex))
  448. {
  449. this.RecycleOldItem(item);
  450. this.managedItems[criticalIndex].item = null;
  451. if (criticalItemType == CriticalItemType.UpToHide)
  452. {
  453. // 最上隐藏了一个
  454. this.criticalItemIndex[criticalItemType + 2] = Mathf.Max(criticalIndex, this.criticalItemIndex[criticalItemType + 2]);
  455. this.criticalItemIndex[criticalItemType]++;
  456. }
  457. else
  458. {
  459. // 最下隐藏了一个
  460. this.criticalItemIndex[criticalItemType + 2] = Mathf.Min(criticalIndex, this.criticalItemIndex[criticalItemType + 2]);
  461. this.criticalItemIndex[criticalItemType]--;
  462. }
  463. this.criticalItemIndex[criticalItemType] = Mathf.Clamp(this.criticalItemIndex[criticalItemType], 0, this.dataCount - 1);
  464. if (this.criticalItemIndex[CriticalItemType.UpToHide] > this.criticalItemIndex[CriticalItemType.DownToHide])
  465. {
  466. // 偶然的情况 拖拽超出一屏
  467. this.ResetCriticalItems();
  468. return false;
  469. }
  470. return true;
  471. }
  472. return false;
  473. }
  474. private bool CheckAndShowItem(int criticalItemType)
  475. {
  476. RectTransform item = this.GetCriticalItem(criticalItemType);
  477. var criticalIndex = this.criticalItemIndex[criticalItemType];
  478. if (item == null && this.ShouldItemSeenAtIndex(criticalIndex))
  479. {
  480. RectTransform newItem = this.GetNewItem(criticalIndex);
  481. this.OnGetItemForDataIndex(newItem, criticalIndex);
  482. this.managedItems[criticalIndex].item = newItem;
  483. if (criticalItemType == CriticalItemType.UpToShow)
  484. {
  485. // 最上显示了一个
  486. this.criticalItemIndex[criticalItemType - 2] = Mathf.Min(criticalIndex, this.criticalItemIndex[criticalItemType - 2]);
  487. this.criticalItemIndex[criticalItemType]--;
  488. }
  489. else
  490. {
  491. // 最下显示了一个
  492. this.criticalItemIndex[criticalItemType - 2] = Mathf.Max(criticalIndex, this.criticalItemIndex[criticalItemType - 2]);
  493. this.criticalItemIndex[criticalItemType]++;
  494. }
  495. this.criticalItemIndex[criticalItemType] = Mathf.Clamp(this.criticalItemIndex[criticalItemType], 0, this.dataCount - 1);
  496. if (this.criticalItemIndex[CriticalItemType.UpToShow] >= this.criticalItemIndex[CriticalItemType.DownToShow])
  497. {
  498. // 偶然的情况 拖拽超出一屏
  499. this.ResetCriticalItems();
  500. return false;
  501. }
  502. return true;
  503. }
  504. return false;
  505. }
  506. private bool ShouldItemSeenAtIndex(int index)
  507. {
  508. if (index < 0 || index >= this.dataCount)
  509. {
  510. return false;
  511. }
  512. this.EnsureItemRect(index);
  513. return new Rect(this.refRect.position - this.content.anchoredPosition, this.refRect.size).Overlaps(this.managedItems[index].rect);
  514. }
  515. private bool ShouldItemFullySeenAtIndex(int index)
  516. {
  517. if (index < 0 || index >= this.dataCount)
  518. {
  519. return false;
  520. }
  521. this.EnsureItemRect(index);
  522. return this.IsRectContains(new Rect(this.refRect.position - this.content.anchoredPosition, this.refRect.size), this.managedItems[index].rect);
  523. }
  524. private bool IsRectContains(Rect outRect, Rect inRect, bool bothDimensions = false)
  525. {
  526. if (bothDimensions)
  527. {
  528. var xContains = (outRect.xMax >= inRect.xMax) && (outRect.xMin <= inRect.xMin);
  529. var yContains = (outRect.yMax >= inRect.yMax) && (outRect.yMin <= inRect.yMin);
  530. return xContains && yContains;
  531. }
  532. else
  533. {
  534. var dir = (int)this.layoutType & flagScrollDirection;
  535. if (dir == 1)
  536. {
  537. // 垂直滚动 只计算y向
  538. return (outRect.yMax >= inRect.yMax) && (outRect.yMin <= inRect.yMin);
  539. }
  540. else
  541. {
  542. // = 0
  543. // 水平滚动 只计算x向
  544. return (outRect.xMax >= inRect.xMax) && (outRect.xMin <= inRect.xMin);
  545. }
  546. }
  547. }
  548. private void InitPool()
  549. {
  550. var poolNode = new GameObject("POOL");
  551. poolNode.SetActive(false);
  552. poolNode.transform.SetParent(this.transform, false);
  553. this.itemPool = new SimpleObjPool<RectTransform>(
  554. this.poolSize,
  555. (RectTransform item) =>
  556. {
  557. item.transform.SetParent(poolNode.transform, false);
  558. },
  559. () =>
  560. {
  561. GameObject itemObj = Instantiate(this.itemTemplate.gameObject);
  562. RectTransform item = itemObj.GetComponent<RectTransform>();
  563. itemObj.transform.SetParent(poolNode.transform, false);
  564. item.anchorMin = Vector2.up;
  565. item.anchorMax = Vector2.up;
  566. item.pivot = Vector2.zero;
  567. itemObj.SetActive(true);
  568. return item;
  569. });
  570. }
  571. private void OnGetItemForDataIndex(RectTransform item, int index)
  572. {
  573. this.SetDataForItemAtIndex(item, index);
  574. item.transform.SetParent(this.content, false);
  575. }
  576. private void SetDataForItemAtIndex(RectTransform item, int index)
  577. {
  578. if (this.updateFunc != null)
  579. {
  580. this.updateFunc(index, item);
  581. }
  582. this.SetPosForItemAtIndex(item, index);
  583. }
  584. private void SetPosForItemAtIndex(RectTransform item, int index)
  585. {
  586. this.EnsureItemRect(index);
  587. Rect r = this.managedItems[index].rect;
  588. item.localPosition = r.position;
  589. item.sizeDelta = r.size;
  590. }
  591. private Vector2 GetItemSize(int index)
  592. {
  593. if (index >= 0 && index <= this.dataCount)
  594. {
  595. if (this.itemSizeFunc != null)
  596. {
  597. return this.itemSizeFunc(index);
  598. }
  599. }
  600. return this.defaultItemSize;
  601. }
  602. private RectTransform GetNewItem(int index)
  603. {
  604. RectTransform item;
  605. if (this.itemGetFunc != null)
  606. {
  607. item = this.itemGetFunc(index);
  608. }
  609. else
  610. {
  611. item = this.itemPool.Get();
  612. }
  613. return item;
  614. }
  615. private void RecycleOldItem(RectTransform item)
  616. {
  617. if (this.itemRecycleFunc != null)
  618. {
  619. this.itemRecycleFunc(item);
  620. }
  621. else
  622. {
  623. this.itemPool.Recycle(item);
  624. }
  625. }
  626. private void InitScrollView()
  627. {
  628. this.initialized = true;
  629. // 根据设置来控制原ScrollRect的滚动方向
  630. var dir = (int)this.layoutType & flagScrollDirection;
  631. this.vertical = dir == 1;
  632. this.horizontal = dir == 0;
  633. this.content.pivot = Vector2.up;
  634. this.content.anchorMin = Vector2.up;
  635. this.content.anchorMax = Vector2.up;
  636. this.content.anchoredPosition = Vector2.zero;
  637. this.InitPool();
  638. this.UpdateRefRect();
  639. }
  640. // refRect是在Content节点下的 viewport的 rect
  641. private void UpdateRefRect()
  642. {
  643. /*
  644. * WorldCorners
  645. *
  646. * 1 ------- 2
  647. * | |
  648. * | |
  649. * 0 ------- 3
  650. *
  651. */
  652. if (!CanvasUpdateRegistry.IsRebuildingLayout())
  653. {
  654. Canvas.ForceUpdateCanvases();
  655. }
  656. this.viewRect.GetWorldCorners(this.viewWorldConers);
  657. this.rectCorners[0] = this.content.transform.InverseTransformPoint(this.viewWorldConers[0]);
  658. this.rectCorners[1] = this.content.transform.InverseTransformPoint(this.viewWorldConers[2]);
  659. this.refRect = new Rect((Vector2)this.rectCorners[0] - this.content.anchoredPosition, this.rectCorners[1] - this.rectCorners[0]);
  660. }
  661. private void MovePos(ref Vector2 pos, Vector2 size)
  662. {
  663. // 注意 所有的rect都是左下角为基准
  664. switch (this.layoutType)
  665. {
  666. case ItemLayoutType.Vertical:
  667. // 垂直方向 向下移动
  668. pos.y -= size.y;
  669. break;
  670. case ItemLayoutType.Horizontal:
  671. // 水平方向 向右移动
  672. pos.x += size.x;
  673. break;
  674. case ItemLayoutType.VerticalThenHorizontal:
  675. pos.y -= size.y;
  676. if (pos.y <= -this.refRect.height)
  677. {
  678. pos.y = 0;
  679. pos.x += size.x;
  680. }
  681. break;
  682. case ItemLayoutType.HorizontalThenVertical:
  683. pos.x += size.x;
  684. if (pos.x >= this.refRect.width)
  685. {
  686. pos.x = 0;
  687. pos.y -= size.y;
  688. }
  689. break;
  690. default:
  691. break;
  692. }
  693. }
  694. // const int 代替 enum 减少 (int)和(CriticalItemType)转换
  695. protected static class CriticalItemType
  696. {
  697. public static byte UpToHide = 0;
  698. public static byte DownToHide = 1;
  699. public static byte UpToShow = 2;
  700. public static byte DownToShow = 3;
  701. }
  702. private class ScrollItemWithRect
  703. {
  704. // scroll item 身上的 RectTransform组件
  705. public RectTransform item;
  706. // scroll item 在scrollview中的位置
  707. public Rect rect;
  708. // rect 是否需要更新
  709. public bool rectDirty = true;
  710. }
  711. }
  712. }