ScrollablePanel.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. using CommonLang;
  2. using CommonLang.Property;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using UnityEngine;
  8. using UnityEngine.EventSystems;
  9. using UnityEngine.UI;
  10. namespace CommonUnity3D.UGUI
  11. { //----------------------------------------------------------------------------------------------------------------------------------
  12. /// <summary>
  13. /// 普通滑动控件
  14. /// </summary>
  15. public class ScrollablePanel : DisplayNode
  16. {
  17. protected readonly DisplayNode mContent;
  18. protected readonly RectMask2D mRectMask;
  19. protected readonly Image mMaskGraphics;
  20. protected readonly ScrollRectInteractive mMaskScrollRect;
  21. //protected readonly Mask mMask;
  22. public ScrollRectInteractive Scroll
  23. {
  24. get { return mMaskScrollRect; }
  25. }
  26. public Rect ScrollRect2D
  27. {
  28. get { return mMaskScrollRect.ScrollRect2D; }
  29. }
  30. public DisplayNode Container
  31. {
  32. get { return mContent; }
  33. }
  34. public bool ShowSlider
  35. {
  36. get { return mMaskScrollRect.ShowSlider; }
  37. set { mMaskScrollRect.ShowSlider = value; }
  38. }
  39. public float ScrollFadeTimeMaxMS
  40. {
  41. get { return mMaskScrollRect.ScrollFadeTimeMaxMS; }
  42. set { mMaskScrollRect.ScrollFadeTimeMaxMS = value; }
  43. }
  44. public ScrollablePanel(string name = null) : base(name)
  45. {
  46. this.EnableChildren = true;
  47. this.mContent = new DisplayNode("container");
  48. this.mContent.EnableChildren = true;
  49. this.AddChild(mContent);
  50. this.mMaskGraphics = mGameObject.AddComponent<InteractiveDummyGraphics>();
  51. this.mMaskGraphics.color = Color.white;
  52. this.mMaskGraphics.type = Image.Type.Filled;
  53. this.mMaskScrollRect = mGameObject.AddComponent<ScrollRectInteractive>();
  54. this.mMaskScrollRect.movementType = ScrollRectInteractive.MovementType.Elastic;
  55. this.mMaskScrollRect.content = mContent.Transform;
  56. this.mMaskScrollRect.viewport = this.Transform;
  57. this.mMaskScrollRect.event_Scrolled += DoScrolled;
  58. this.mMaskScrollRect.event_OnEndDrag += DoEndDrag;
  59. this.mRectMask = mGameObject.AddComponent<RectMask2D>();
  60. // this.mMask = mGameObject.AddComponent<Mask>();
  61. // this.mMask.showMaskGraphic = false;
  62. this.IsInteractive = true;
  63. this.Enable = true;
  64. }
  65. public bool IsInViewRect(ref Rect src, ref Rect dst)
  66. {
  67. return mMaskScrollRect.IsInViewRect(ref src, ref dst);
  68. }
  69. public void LookAt(Vector2 pos)
  70. {
  71. mMaskScrollRect.LookAt(pos, false);
  72. }
  73. public void LookAt(Vector2 pos, bool scroll)
  74. {
  75. mMaskScrollRect.LookAt(pos, scroll);
  76. }
  77. public void SetScrollBarPair(DisplayNode scrollh, DisplayNode scrollv)
  78. {
  79. mMaskScrollRect.SetScrollBarPair(scrollh, scrollv);
  80. }
  81. //-----------------------------------------------------------------------------------------------------------------------
  82. protected override void OnUpdate()
  83. {
  84. base.OnUpdate();
  85. if (!Scroll.AutoUpdateContentSize)
  86. {
  87. this.OnUpdateContentSize();
  88. }
  89. }
  90. protected virtual void OnUpdateContentSize()
  91. {
  92. }
  93. #region _Events_
  94. protected override void OnDisposeEvents()
  95. {
  96. this.event_Scrolled = null;
  97. this.event_OnEndDrag = null;
  98. base.OnDisposeEvents();
  99. }
  100. private void DoEndDrag(DisplayNode sender, PointerEventData e)
  101. {
  102. if (event_OnEndDrag != null)
  103. {
  104. event_OnEndDrag(sender, e);
  105. }
  106. }
  107. private void DoScrolled(Vector2 value)
  108. {
  109. OnScrolled(value);
  110. if (event_Scrolled != null)
  111. {
  112. event_Scrolled.Invoke(this, value);
  113. }
  114. }
  115. protected virtual void OnScrolled(Vector2 value) { }
  116. public delegate void ScrollEventHandler(DisplayNode sender, Vector2 e);
  117. public ScrollEventHandler event_Scrolled;
  118. public DisplayNode.PointerEventHandler event_OnEndDrag;
  119. public event ScrollEventHandler Scrolled { add { event_Scrolled += value; } remove { event_Scrolled -= value; } }
  120. public event DisplayNode.PointerEventHandler OnEndDragEvent { add { event_OnEndDrag += value; } remove { event_OnEndDrag -= value; } }
  121. #endregion
  122. }
  123. //----------------------------------------------------------------------------------------------------------------------------------
  124. /// <summary>
  125. /// 静态网格滚动控件
  126. /// </summary>
  127. public class GridScrollablePanel : ScrollablePanel
  128. {
  129. private int mRowCount = 0, mColumnCount = 0;
  130. private Vector2 mCellSize;
  131. private Vector2 mContentSize;
  132. private DisplayNode[,] mGridMatrix;
  133. public GridScrollablePanel(string name = null) : base(name)
  134. {
  135. Scroll.AutoUpdateContentSize = false;
  136. }
  137. public Vector2 CellSize { get { return mCellSize; } }
  138. public int RowCount { get { return mRowCount; } }
  139. public int ColumnCount { get { return mColumnCount; } }
  140. private void CleanMatrix()
  141. {
  142. if (mGridMatrix != null)
  143. {
  144. for (int r = 0; r < mRowCount; r++)
  145. {
  146. for (int c = 0; c < mColumnCount; c++)
  147. {
  148. DisplayNode cell = mGridMatrix[c, r];
  149. this.mContent.RemoveChild(cell, true);
  150. }
  151. }
  152. mGridMatrix = null;
  153. }
  154. }
  155. public void Initialize(DisplayNode[,] cells, Vector2 cellSize)
  156. {
  157. CleanMatrix();
  158. if (mGridMatrix == null)
  159. {
  160. mGridMatrix = cells;
  161. mColumnCount = cells.GetLength(0);
  162. mRowCount = cells.GetLength(1);
  163. mCellSize = cellSize;
  164. mContentSize = new Vector2(cellSize.x * mColumnCount, cellSize.y * mRowCount);
  165. for (int r = 0; r < mRowCount; r++)
  166. {
  167. for (int c = 0; c < mColumnCount; c++)
  168. {
  169. DisplayNode cell = mGridMatrix[c, r];
  170. cell.Bounds2D = new Rect(c * cellSize.x, r * cellSize.y, cellSize.x, cellSize.y);
  171. mGridMatrix[c, r] = cell;
  172. this.mContent.AddChild(cell);
  173. }
  174. }
  175. }
  176. }
  177. public void Initialize(int columns, int rows, Vector2 cellSize, CreateCellItemHandler createCell)
  178. {
  179. CleanMatrix();
  180. if (mGridMatrix == null)
  181. {
  182. if (rows < 1 || columns < 1) throw new Exception("rows columns must be lager than 1.");
  183. if (cellSize.x < 0 || cellSize.y < 0) throw new Exception("unitWidth unitHeight must be lager than 0.");
  184. mGridMatrix = new DisplayNode[columns, rows];
  185. mColumnCount = columns;
  186. mRowCount = rows;
  187. mCellSize = cellSize;
  188. mContentSize = new Vector2(cellSize.x * mColumnCount, cellSize.y * mRowCount);
  189. for (int r = 0; r < mRowCount; r++)
  190. {
  191. for (int c = 0; c < mColumnCount; c++)
  192. {
  193. DisplayNode cell = createCell(this, c, r);
  194. cell.Bounds2D = new Rect(c * cellSize.x, r * cellSize.y, cellSize.x, cellSize.y);
  195. mGridMatrix[c, r] = cell;
  196. this.mContent.AddChild(cell);
  197. }
  198. }
  199. }
  200. }
  201. public DisplayNode GetCell(int column, int row)
  202. {
  203. if (mGridMatrix != null && CMath.isInRange(row, mRowCount) && CMath.isInRange(column, mColumnCount))
  204. {
  205. return mGridMatrix[column, row];
  206. }
  207. return null;
  208. }
  209. protected override void OnUpdateContentSize()
  210. {
  211. base.OnUpdateContentSize();
  212. this.mContent.Size2D = mContentSize;
  213. }
  214. public delegate DisplayNode CreateCellItemHandler(GridScrollablePanel panel, int column, int row);
  215. }
  216. //----------------------------------------------------------------------------------------------------------------------------------
  217. /// <summary>
  218. /// 网格滚动控件(缓冲模式)
  219. /// 适合大量固定数量数据的列表
  220. /// </summary>
  221. public class CachedGridScrollablePanel : ScrollablePanel
  222. {
  223. private Vector2 mCellSize = Vector2.zero;
  224. private Vector2 mContentSize = Vector2.one;
  225. private int mCacheSize = 2;
  226. private int mRowCount = 0, mColumnCount = 0;
  227. private int mViewRowCount = 0, mViewColumnCount = 0;
  228. private Cell[,] mGridMatrix;
  229. private LinkedList<Cell> mViewList = new LinkedList<Cell>();
  230. private LinkedList<Cell> mHideList = new LinkedList<Cell>();
  231. private List<Cell> mHidingList = new List<Cell>();
  232. public CachedGridScrollablePanel(string name = null)
  233. : base(name)
  234. {
  235. base.Scroll.AutoUpdateContentSize = false;
  236. Gap = new Vector2();
  237. Border = new Vector2();
  238. }
  239. public Vector2 CellSize { get { return mCellSize; } }
  240. public int RowCount { get { return mRowCount; } }
  241. public int ColumnCount { get { return mColumnCount; } }
  242. public int ViewRowCount { get { return mViewRowCount; } }
  243. public int ViewColumnCount { get { return mViewColumnCount; } }
  244. public bool IsUseCache { get { return mCacheSize > -1; } }
  245. public Vector2 Gap { get; set; }
  246. public Vector2 Border { get; set; }
  247. /// <summary>
  248. /// 初始化滚动控件.CacheSize = -1时候为非托管模式,需要自己维护销毁节点.
  249. /// </summary>
  250. /// <param name="columns"></param>
  251. /// <param name="rows"></param>
  252. /// <param name="cellSize"></param>
  253. /// <param name="cacheSize"></param>
  254. public void Initialize(int columns, int rows, Vector2 cellSize, int cacheSize = 1)
  255. {
  256. ClearGrid();
  257. if (mGridMatrix == null)
  258. {
  259. if (cellSize.x < 0 || cellSize.y < 0) throw new Exception("unitWidth unitHeight must be lager than 0.");
  260. this.mCacheSize = cacheSize;
  261. this.mCellSize = cellSize;
  262. CalViewContent(columns, rows, true);
  263. }
  264. }
  265. public void Reset(int columns, int rows)
  266. {
  267. if (mGridMatrix != null)
  268. {
  269. for (int r = 0; r < mRowCount; r++)
  270. {
  271. for (int c = 0; c < mColumnCount; c++)
  272. {
  273. Cell cell = mGridMatrix[c, r];
  274. if (cell != null)
  275. {
  276. OnHideCell(cell);
  277. }
  278. }
  279. }
  280. mGridMatrix = null;
  281. }
  282. if (mGridMatrix == null)
  283. {
  284. CalViewContent(columns, rows, false);
  285. }
  286. }
  287. public DisplayNode GetCell(int column, int row)
  288. {
  289. if (mGridMatrix != null && CMath.isInRange(row, mRowCount) && CMath.isInRange(column, mColumnCount))
  290. {
  291. Cell cell = mGridMatrix[column, row];
  292. if (cell != null)
  293. {
  294. return cell.Node;
  295. }
  296. }
  297. return null;
  298. }
  299. protected override void OnUpdateContentSize()
  300. {
  301. this.mContent.Size2D = this.mContentSize;
  302. this.UpdateGrid();
  303. }
  304. /// <summary>
  305. /// 清理所有滚动并销毁节点.
  306. /// </summary>
  307. public void ClearGrid()
  308. {
  309. bool isCache = IsUseCache;
  310. if (mGridMatrix != null)
  311. {
  312. for (int r = 0; r < mRowCount; r++)
  313. {
  314. for (int c = 0; c < mColumnCount; c++)
  315. {
  316. Cell cell = mGridMatrix[c, r];
  317. if (cell != null && cell.Node != null)
  318. {
  319. if (isCache)
  320. {
  321. //缓存托管模式主动释放销毁节点.
  322. cell.Node.Dispose();
  323. }
  324. else
  325. {
  326. //非托管模式需要自己释放销毁节点.
  327. cell.Node.RemoveFromParent(false);
  328. }
  329. mGridMatrix[c, r] = null;
  330. }
  331. }
  332. }
  333. mGridMatrix = null;
  334. }
  335. if (mHideList != null)
  336. {
  337. if (isCache)
  338. {
  339. for (LinkedListNode<Cell> it = mHideList.First; it != null; it = it.Next)
  340. {
  341. Cell cell = it.Value;
  342. if (cell != null && cell.Node != null)
  343. {
  344. cell.Node.Dispose();
  345. }
  346. }
  347. }
  348. mHideList.Clear();
  349. }
  350. if (mViewList != null)
  351. {
  352. mViewList.Clear();
  353. }
  354. mColumnCount = 0;
  355. mRowCount = 0;
  356. mGridMatrix = null;
  357. }
  358. /// <summary>
  359. /// 计算需要创建的节点个数.
  360. /// </summary>
  361. private void CalViewContent(int columns, int rows, bool isCreate)
  362. {
  363. if (columns < 1 || rows < 1) { return; }
  364. Vector2 viewSize = this.Size2D;
  365. this.mGridMatrix = new Cell[columns, rows];
  366. this.mRowCount = rows;
  367. this.mColumnCount = columns;
  368. this.mContentSize = new Vector2(Border.x + columns * (mCellSize.x + Gap.x), Border.y + rows * (mCellSize.y + Gap.y));
  369. this.mContent.Position2D = Vector2.zero;
  370. this.mContent.Size2D = this.mContentSize;
  371. if (IsUseCache == true)
  372. {
  373. this.mViewRowCount = (rows == 1) ? 1 : (int)(viewSize.y / mCellSize.y) + mCacheSize;
  374. this.mViewRowCount = Math.Min(mRowCount, mViewRowCount);
  375. this.mViewColumnCount = (columns == 1) ? 1 : (int)(viewSize.x / mCellSize.x) + mCacheSize;
  376. this.mViewColumnCount = Math.Min(mColumnCount, mViewColumnCount);
  377. }
  378. else
  379. {
  380. this.mViewRowCount = rows;
  381. this.mViewColumnCount = columns;
  382. }
  383. for (int r = 0; r < mViewRowCount; r++)
  384. {
  385. for (int c = 0; c < mViewColumnCount; c++)
  386. {
  387. //这里的Create应该注释掉,否则会在下一个update才会正真初始化完成。如果一定要区分create和show,建议重构一下.
  388. //if (isCreate)
  389. //{
  390. // OnCreateCell();
  391. //}
  392. //else
  393. {
  394. OnShowCell(c, r);
  395. }
  396. }
  397. }
  398. }
  399. //-----------------------------------------------------------------------------------------------------------------------
  400. #region _Events_
  401. protected override void OnDisposeEvents()
  402. {
  403. this.event_CreateCell = null;
  404. this.event_HideCell = null;
  405. this.event_ShowCell = null;
  406. base.OnDisposeEvents();
  407. }
  408. public delegate DisplayNode CreateCellItemHandler(CachedGridScrollablePanel panel);
  409. public delegate void HideCellItemHandler(CachedGridScrollablePanel panel, int column, int row, DisplayNode cell);
  410. public delegate void ShowCellItemHandler(CachedGridScrollablePanel panel, int column, int row, DisplayNode cell);
  411. [Desc("请求创建节点")]
  412. public CreateCellItemHandler event_CreateCell;
  413. [Desc("节点被隐藏")]
  414. public HideCellItemHandler event_HideCell;
  415. [Desc("节点将显示")]
  416. public ShowCellItemHandler event_ShowCell;
  417. #endregion
  418. //-----------------------------------------------------------------------------------------------------------------------
  419. #region _Cells_
  420. private Cell OnCreateCell()
  421. {
  422. DisplayNode node = null;
  423. if (event_CreateCell != null)
  424. {
  425. node = event_CreateCell.Invoke(this);
  426. }
  427. else
  428. {
  429. node = new DisplayNode("default cell");
  430. }
  431. //实际上代价很大,setactive会产生大量的gc,这里由rect2d来保证clip
  432. //node.VisibleInParent = false;
  433. Cell cell = new Cell(node);
  434. mContent.AddChild(node);
  435. mHideList.AddLast(cell);
  436. return cell;
  437. }
  438. private Cell OnShowCell(int column, int row)
  439. {
  440. Cell cell = mGridMatrix[column, row];
  441. if (cell == null)
  442. {
  443. if (mHideList.Count == 0)
  444. {
  445. cell = OnCreateCell();
  446. }
  447. cell = mHideList.First.Value;
  448. mHideList.RemoveFirst();
  449. mViewList.AddFirst(cell);
  450. mGridMatrix[column, row] = cell;
  451. cell.column = column;
  452. cell.row = row;
  453. cell.Node.Position2D = new Vector2(Border.x + (column * (mCellSize.x + Gap.x)), Border.y + row * (mCellSize.y+Gap.y));
  454. //cell.Node.VisibleInParent = true;
  455. if (event_ShowCell != null)
  456. {
  457. event_ShowCell.Invoke(this, column, row, cell.Node);
  458. }
  459. }
  460. return cell;
  461. }
  462. private void OnHideCell(Cell cell)
  463. {
  464. mGridMatrix[cell.column, cell.row] = null;
  465. mHideList.AddFirst(cell);
  466. mViewList.Remove(cell);
  467. cell.Node.Position2D = new Vector2(10000, 10000);
  468. //cell.Node.VisibleInParent = false;
  469. if (event_HideCell != null)
  470. {
  471. event_HideCell.Invoke(this, cell.column, cell.row, cell.Node);
  472. }
  473. }
  474. private void UpdateGrid()
  475. {
  476. if (mGridMatrix != null)
  477. {
  478. Rect scroll_rect = this.ScrollRect2D;
  479. Vector2 vsize = this.Size2D;
  480. int sx1 = (int)((scroll_rect.x - Border.x) / (mCellSize.x + Gap.x));
  481. int sx2 = (int)((scroll_rect.x - Border.x + scroll_rect.width) / (mCellSize.x + Gap.x));
  482. int sy1 = (int)((scroll_rect.y - Border.y) / (mCellSize.y + Gap.y));
  483. int sy2 = (int)((scroll_rect.y - Border.y + scroll_rect.height) / (mCellSize.y + Gap.y));
  484. sx1 = Math.Max(sx1, 0);
  485. sy1 = Math.Max(sy1, 0);
  486. sx2 = Math.Min(sx2, this.mColumnCount - 1);
  487. sy2 = Math.Min(sy2, this.mRowCount - 1);
  488. //隐藏超出视野的//
  489. mHidingList.Clear();
  490. for (LinkedListNode<Cell> it = mViewList.First; it != null; it = it.Next)
  491. {
  492. Cell cell = it.Value;
  493. if (!CMath.isIncludeEqual(cell.column, sx1, sx2) || !CMath.isIncludeEqual(cell.row, sy1, sy2))
  494. {
  495. mHidingList.Add(cell);
  496. }
  497. }
  498. if (mHidingList.Count > 0)
  499. {
  500. for (int i = mHidingList.Count - 1; i >= 0; --i)
  501. {
  502. OnHideCell(mHidingList[i]);
  503. }
  504. mHidingList.Clear();
  505. }
  506. //显示进入视野//
  507. for (int x = sx1; x <= sx2; x++)
  508. {
  509. for (int y = sy1; y <= sy2; y++)
  510. {
  511. OnShowCell(x, y);
  512. }
  513. }
  514. }
  515. }
  516. public void RefreshShowCell()
  517. {
  518. if (mGridMatrix != null && mMaskScrollRect.Binding != null)
  519. {
  520. Rect scroll_rect = this.ScrollRect2D;
  521. int sx1 = (int)((scroll_rect.x) / mCellSize.x);
  522. int sx2 = (int)((scroll_rect.x + scroll_rect.width) / mCellSize.x);
  523. int sy1 = (int)((scroll_rect.y) / mCellSize.y);
  524. int sy2 = (int)((scroll_rect.y + scroll_rect.height) / mCellSize.y);
  525. sx1 = Math.Max(sx1, 0);
  526. sy1 = Math.Max(sy1, 0);
  527. sx2 = Math.Min(sx2, this.mColumnCount - 1);
  528. sy2 = Math.Min(sy2, this.mRowCount - 1);
  529. //显示进入视野//
  530. for (int x = sx1; x <= sx2; x++)
  531. {
  532. for (int y = sy1; y <= sy2; y++)
  533. {
  534. Cell cell = mGridMatrix[x, y];
  535. if (event_ShowCell != null)
  536. {
  537. event_ShowCell.Invoke(this, x, y, cell.Node);
  538. }
  539. }
  540. }
  541. }
  542. }
  543. class Cell
  544. {
  545. public DisplayNode Node;
  546. public int column, row;
  547. public Cell(DisplayNode node)
  548. {
  549. this.Node = node;
  550. }
  551. }
  552. #endregion
  553. }
  554. //-----------------------------------------------------------------------------------------------------------------------
  555. /// <summary>
  556. /// 分页式滚动控件
  557. /// </summary>
  558. public class PagedScrollablePanel : ScrollablePanel
  559. {
  560. private readonly PagedScrollableSnap mScrollSnap;
  561. private int mPageCount;
  562. private Vector2 mPageSize;
  563. private Vector2 mContentSize;
  564. private DisplayNode[] mPageList;
  565. public PagedScrollablePanel(string name = null)
  566. : base(name)
  567. {
  568. this.mScrollSnap = mGameObject.AddComponent<PagedScrollableSnap>();
  569. this.Scroll.inertia = false;
  570. this.Scroll.movementType = ScrollRectInteractive.MovementType.Clamped;
  571. this.Scroll.horizontal = true;
  572. this.Scroll.vertical = false;
  573. this.Scroll.AutoUpdateContentSize = true;
  574. }
  575. public Vector2 ContentSize { get { return mContentSize; } }
  576. public Vector2 PageSize { get { return mPageSize; } }
  577. public int PageCount { get { return mPageCount; } }
  578. public PagedScrollableSnap ScrollSnap { get { return mScrollSnap; } }
  579. private void CleanPages()
  580. {
  581. if (mPageList != null)
  582. {
  583. for (int i = 0; i < mPageCount; i++)
  584. {
  585. DisplayNode cell = mPageList[i];
  586. this.mContent.RemoveChild(cell, true);
  587. }
  588. mPageList = null;
  589. }
  590. }
  591. public void Initialize(DisplayNode[] pages, Vector2 pageSize)
  592. {
  593. CleanPages();
  594. if (mPageList == null)
  595. {
  596. mPageList = pages;
  597. mPageCount = pages.Length;
  598. mPageSize = pageSize;
  599. mContentSize = new Vector2(pageSize.x * mPageCount, pageSize.y);
  600. for (int i = 0; i < mPageCount; i++)
  601. {
  602. DisplayNode cell = mPageList[i];
  603. cell.Bounds2D = new Rect(i * pageSize.x, 0, pageSize.x, pageSize.y);
  604. mPageList[i] = cell;
  605. this.mContent.AddChild(cell);
  606. }
  607. this.mContent.Size2D = mContentSize;
  608. this.mScrollSnap.Reset();
  609. }
  610. }
  611. public void Initialize(int pages, Vector2 pageSize, CreatePageItemHandler createPage)
  612. {
  613. CleanPages();
  614. if (mPageList == null)
  615. {
  616. if (pages < 1) throw new Exception("rows columns must be lager than 1.");
  617. if (pageSize.x < 0 || pageSize.y < 0) throw new Exception("unitWidth unitHeight must be lager than 0.");
  618. mPageList = new DisplayNode[pages];
  619. mPageCount = pages;
  620. mPageSize = pageSize;
  621. mContentSize = new Vector2(pageSize.x * mPageCount, pageSize.y);
  622. for (int i = 0; i < mPageCount; i++)
  623. {
  624. DisplayNode page = createPage(this, i);
  625. page.Bounds2D = new Rect(i * pageSize.x, 0, pageSize.x, pageSize.y);
  626. mPageList[i] = page;
  627. this.mContent.AddChild(page);
  628. }
  629. this.mContent.Size2D = mContentSize;
  630. this.mScrollSnap.Reset();
  631. }
  632. }
  633. public DisplayNode GetPage(int index)
  634. {
  635. if (mPageList != null && CMath.isInRange(index, mPageCount))
  636. {
  637. return mPageList[index];
  638. }
  639. return null;
  640. }
  641. protected override void OnUpdateContentSize()
  642. {
  643. base.OnUpdateContentSize();
  644. this.mContent.Size2D = mContentSize;
  645. }
  646. public delegate DisplayNode CreatePageItemHandler(PagedScrollablePanel panel, int index);
  647. }
  648. [RequireComponent(typeof(ScrollRect))]
  649. public class PagedScrollableSnap : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
  650. {
  651. private PagedScrollablePanel _owner;
  652. private Transform _screensContainer;
  653. private int _screens = 1;
  654. private int _startingScreen = 1;
  655. private System.Collections.Generic.List<Vector3> _positions;
  656. private ScrollRect _scroll_rect;
  657. private Vector3 _lerp_target;
  658. private bool _lerp;
  659. private float _containerSize;
  660. private bool _startDrag = true;
  661. private Vector3 _startPosition = new Vector3();
  662. private int _currentScreen;
  663. public float LerpSpeed = 10;
  664. public float DockDistance = 1;
  665. /// <summary>
  666. /// If set to something above zero, it will be possible to move to the next page after dragging past the specified threshold.
  667. /// </summary>
  668. public float nextPageThreshold = 0f;
  669. // Use this for initialization
  670. public void Reset()
  671. {
  672. _owner = DisplayNode.AsDisplayNode(gameObject) as PagedScrollablePanel;
  673. _scroll_rect = _owner.Scroll;
  674. _screensContainer = _owner.Container.Transform;
  675. _screens = _owner.PageCount;
  676. _lerp = false;
  677. _positions = new System.Collections.Generic.List<Vector3>();
  678. if (_screens > 0)
  679. {
  680. float step = _owner.PageSize.x;
  681. for (int i = 0; i < _screens; ++i)
  682. {
  683. _scroll_rect.horizontalNormalizedPosition = (float)i / (float)(_screens - 1);
  684. Vector3 pos = _screensContainer.localPosition;
  685. pos.x = (int)(pos.x);
  686. _positions.Add(pos);
  687. }
  688. }
  689. _scroll_rect.horizontalNormalizedPosition = (float)(_startingScreen - 1) / (float)(_screens - 1);
  690. _containerSize = _owner.ContentSize.x;
  691. }
  692. void Update()
  693. {
  694. if (_lerp)
  695. {
  696. float lpspeed = Mathf.Max(0.1f, LerpSpeed);
  697. float dock = Mathf.Max(0.005f, DockDistance);
  698. _screensContainer.localPosition = Vector3.Lerp(_screensContainer.localPosition, _lerp_target, lpspeed * Time.deltaTime);
  699. if (Vector3.Distance(_screensContainer.localPosition, _lerp_target) < dock)
  700. {
  701. _screensContainer.localPosition = _lerp_target;
  702. _lerp = false;
  703. }
  704. }
  705. }
  706. //Function for switching screens with buttons
  707. public void NextScreen()
  708. {
  709. if (CurrentScreen() < _screens - 1)
  710. {
  711. _lerp = true;
  712. _lerp_target = _positions[CurrentScreen() + 1];
  713. }
  714. }
  715. //Function for switching screens with buttons
  716. public void PreviousScreen()
  717. {
  718. if (CurrentScreen() > 0)
  719. {
  720. _lerp = true;
  721. _lerp_target = _positions[CurrentScreen() - 1];
  722. }
  723. }
  724. //Because the CurrentScreen function is not so reliable, these are the functions used for swipes
  725. private void NextScreenCommand()
  726. {
  727. if (_currentScreen < _screens - 1)
  728. {
  729. _lerp = true;
  730. _lerp_target = _positions[_currentScreen + 1];
  731. }
  732. }
  733. //Because the CurrentScreen function is not so reliable, these are the functions used for swipes
  734. private void PrevScreenCommand()
  735. {
  736. if (_currentScreen > 0)
  737. {
  738. _lerp = true;
  739. _lerp_target = _positions[_currentScreen - 1];
  740. }
  741. }
  742. //find the closest registered point to the releasing point
  743. private Vector3 FindClosestFrom(Vector3 start, System.Collections.Generic.List<Vector3> positions)
  744. {
  745. Vector3 closest = Vector3.zero;
  746. float distance = Mathf.Infinity;
  747. foreach (Vector3 position in _positions)
  748. {
  749. if (Vector3.Distance(start, position) < distance)
  750. {
  751. distance = Vector3.Distance(start, position);
  752. closest = position;
  753. }
  754. }
  755. return closest;
  756. }
  757. private Vector3 FindMatchCondition(Vector3 start)
  758. {
  759. float delta = 0f;
  760. if (_scroll_rect.horizontal)
  761. {
  762. delta = start.x - _positions[_currentScreen].x;
  763. }
  764. else if(_scroll_rect.vertical)
  765. {
  766. delta = start.y - _positions[_currentScreen].y;
  767. }
  768. if (Mathf.Abs(delta) > nextPageThreshold)
  769. {
  770. if (delta < -nextPageThreshold)
  771. {
  772. // Next page
  773. if (_currentScreen < _positions.Count - 1)
  774. {
  775. _currentScreen = _currentScreen + 1;
  776. }
  777. }
  778. else if (delta > nextPageThreshold)
  779. {
  780. // Previous page
  781. if (_currentScreen > 0)
  782. {
  783. _currentScreen = _currentScreen - 1;
  784. }
  785. }
  786. }
  787. return _positions[_currentScreen];
  788. }
  789. //returns the current screen that the is seeing
  790. public int CurrentScreen()
  791. {
  792. float absPoz = Math.Abs((_screensContainer as RectTransform).offsetMin.x);
  793. absPoz = Mathf.Clamp(absPoz, 1, _containerSize - 1);
  794. //0.5f为4舍5入的附加值,防止6.9被转成6的情况
  795. float calc = (absPoz / _containerSize) * _screens + 0.5f;
  796. return (int)calc;
  797. }
  798. #region Interfaces
  799. public void OnBeginDrag(PointerEventData eventData)
  800. {
  801. if (_owner.EnableTouchInParents)
  802. {
  803. _startPosition = _screensContainer.localPosition;
  804. _currentScreen = CurrentScreen();
  805. }
  806. }
  807. public void OnEndDrag(PointerEventData eventData)
  808. {
  809. if (_owner.EnableTouchInParents)
  810. {
  811. _startDrag = true;
  812. if (_scroll_rect.horizontal)
  813. {
  814. _lerp = true;
  815. // If we have a touch in progress and the next page threshold set
  816. if (nextPageThreshold > 0f)
  817. {
  818. _lerp_target = FindMatchCondition(_screensContainer.localPosition);
  819. }
  820. else
  821. {
  822. _lerp_target = FindClosestFrom(_screensContainer.localPosition, _positions);
  823. }
  824. }
  825. }
  826. }
  827. public void OnDrag(PointerEventData eventData)
  828. {
  829. _lerp = false;
  830. if (_startDrag)
  831. {
  832. OnBeginDrag(eventData);
  833. _startDrag = false;
  834. }
  835. }
  836. #endregion
  837. }
  838. //----------------------------------------------------------------------------------------------------------------------------------
  839. }