UIWarpContent1.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine.UI;
  5. using DG.Tweening;
  6. /// <summary>
  7. /// 滚动列表优化
  8. /// </summary>
  9. [DisallowMultipleComponent]
  10. public class UIWarpContent1 : MonoBehaviour
  11. {
  12. /// <summary>
  13. /// 元素初始化
  14. /// </summary>
  15. /// <param name="go"> 游戏对象 </param>
  16. /// <param name="dataIndex"> 数据索引 </param>
  17. public delegate void OnInitializeItem(GameObject go, int dataIndex);
  18. /// <summary>
  19. /// 元素初始化
  20. /// </summary>
  21. public OnInitializeItem onInitializeItem;
  22. /// <summary>
  23. /// 排列
  24. /// </summary>
  25. public enum Arrangement
  26. {
  27. /// <summary>
  28. /// 横
  29. /// </summary>
  30. Horizontal,
  31. /// <summary>
  32. /// 竖
  33. /// </summary>
  34. Vertical,
  35. }
  36. /// <summary>
  37. /// 排列
  38. /// </summary>
  39. public Arrangement arrangement = Arrangement.Horizontal;
  40. /// <summary>
  41. /// Maximum children per line.
  42. /// If the arrangement is horizontal, this denotes the number of columns.
  43. /// If the arrangement is vertical, this stands for the number of rows.
  44. /// </summary>
  45. [Range(1, 50)]
  46. public int maxPerLine = 1;
  47. /// <summary>
  48. /// The width of each of the cells.
  49. /// </summary>
  50. public float cellWidth = 200f;
  51. /// <summary>
  52. /// The height of each of the cells.
  53. /// </summary>
  54. public float cellHeight = 200f;
  55. /// <summary>
  56. /// The Width Space of each of the cells.
  57. /// </summary>
  58. [Range(0, 50)]
  59. public float cellWidthSpace = 0f;
  60. /// <summary>
  61. /// The Height Space of each of the cells.
  62. /// </summary>
  63. [Range(0, 50)]
  64. public float cellHeightSpace = 0f;
  65. /// <summary>
  66. /// 窗口能容纳的 行数 或 列数
  67. /// </summary>
  68. [Range(0, 30)]
  69. public int viewCount = 5;
  70. /// <summary>
  71. /// scroll view 对象
  72. /// </summary>
  73. public ScrollRect scrollRect = null;
  74. /// <summary>
  75. /// content 对象
  76. /// </summary>
  77. public RectTransform content = null;
  78. /// <summary>
  79. /// 元素预制物体
  80. /// </summary>
  81. public GameObject goItemPrefab = null;
  82. /// <summary>
  83. /// 数据的数量
  84. /// </summary>
  85. private int mDataCount = 0;
  86. /// <summary>
  87. /// 当前所处的行数
  88. /// </summary>
  89. private int curScrollPerLineIndex = -1;
  90. /// <summary>
  91. /// 正在使用中的元素
  92. /// </summary>
  93. private List<UIWarpContentItem1> listItem = new List<UIWarpContentItem1>();
  94. /// <summary>
  95. /// 未在使用中的元素
  96. /// </summary>
  97. private Queue<UIWarpContentItem1> unUseItem = new Queue<UIWarpContentItem1>();
  98. /// <summary>
  99. /// 初始化
  100. /// </summary>
  101. private void Awake()
  102. {
  103. }
  104. /// <summary>
  105. /// 初始化
  106. /// </summary>
  107. /// <param name="dataCount"> 数据的数量 </param>
  108. public void Init(int dataCount)
  109. {
  110. if (scrollRect == null || content == null || goItemPrefab == null)
  111. {
  112. Debug.LogError("异常:请检测<" + gameObject.name + ">对象上UIWarpContent对应ScrollRect、Content、GoItemPrefab 是否存在值...." + scrollRect + " _" + content + "_" + goItemPrefab);
  113. return;
  114. }
  115. // 不显示元素预制物体
  116. goItemPrefab.SetActive(false);
  117. scrollRect.onValueChanged.RemoveAllListeners();
  118. scrollRect.onValueChanged.AddListener(onValueChanged);
  119. // 如果数据数量为 0 ,没有初始化的必要
  120. if (dataCount <= 0)
  121. {
  122. return;
  123. }
  124. // 设置数据的数量,并计算 content 的尺寸
  125. setDataCount(dataCount);
  126. unUseItem.Clear();
  127. listItem.Clear();
  128. setUpdateRectItem(0);
  129. }
  130. /// <summary>
  131. /// 标志位
  132. /// </summary>
  133. public bool hasPlayEffect = false;
  134. /// <summary>
  135. /// 特效种类,0就是从下往上,1是从左往右,2是从右往左.
  136. /// </summary>
  137. public int KindOfEffect = 0;
  138. /// <summary>
  139. /// 递加种子
  140. /// </summary>
  141. public int seedNum = 0;
  142. /// <summary>
  143. /// 用了dotween后,在快速连续刷新的时候,会有显示不到位的情况,需要这样重置一下才行
  144. /// 但是用了以后会卡顿,正在调整
  145. /// </summary>
  146. /// <param name="dataCount"></param>
  147. public void DataZero()
  148. {
  149. if (scrollRect == null || content == null || goItemPrefab == null)
  150. {
  151. Debug.LogError("异常:请检测<" + gameObject.name + ">对象上UIWarpContent对应ScrollRect、Content、GoItemPrefab 是否存在值...." + scrollRect + " _" + content + "_" + goItemPrefab);
  152. return;
  153. }
  154. // 因为数据改变了,元素要重新获取数据
  155. for (int i = listItem.Count - 1; i >= 0; i--)
  156. {
  157. UIWarpContentItem1 item = listItem[i];
  158. DelItem(item.Index);
  159. }
  160. seedNum = 0;
  161. hasPlayEffect = false;
  162. //// 计算当前所处的行数,刷新
  163. //int _curScrollPerLineIndex = getCurScrollPerLineIndex();
  164. //setUpdateRectItem(_curScrollPerLineIndex);
  165. }
  166. /// <summary>
  167. /// 数据改变了,比如切换分类功能,全部,前排,中排,后排,元素要重新获取数据
  168. /// </summary>
  169. /// <param name="dataCount"> 数据的数量 </param>
  170. /// <param name="bResetPos"> 是否返回初始位置 </param>
  171. public void DataChange(int dataCount, bool bResetPos)
  172. {
  173. if (scrollRect == null || content == null || goItemPrefab == null)
  174. {
  175. Debug.LogError("异常:请检测<" + gameObject.name + ">对象上UIWarpContent对应ScrollRect、Content、GoItemPrefab 是否存在值...." + scrollRect + " _" + content + "_" + goItemPrefab);
  176. return;
  177. }
  178. // 重新设置数量,重新计算 content 的尺寸
  179. setDataCount(dataCount);
  180. // 因为数据改变了,元素要重新获取数据
  181. for (int i = listItem.Count - 1; i >= 0; i--)
  182. {
  183. UIWarpContentItem1 item = listItem[i];
  184. item.Index = -1;
  185. listItem.Remove(item);
  186. unUseItem.Enqueue(item);
  187. }
  188. seedNum = 0;
  189. hasPlayEffect = false;
  190. if (bResetPos)
  191. {
  192. hasPlayEffect = true;
  193. content.anchoredPosition = Vector3.zero;
  194. }
  195. // 计算当前所处的行数,刷新
  196. int _curScrollPerLineIndex = getCurScrollPerLineIndex();
  197. setUpdateRectItem(_curScrollPerLineIndex);
  198. //============== 2017/1/22 临时 ====================
  199. //if (hasPlayEffect == false)
  200. //{
  201. // hasPlayEffect = true;
  202. //}
  203. //========================================
  204. }
  205. /// <summary>
  206. /// 设置数据的数量,并重新计算 content 的尺寸
  207. /// </summary>
  208. /// <param name="count"> 数据的数量 </param>
  209. private void setDataCount(int count)
  210. {
  211. if (mDataCount == count)
  212. {
  213. return;
  214. }
  215. mDataCount = count;
  216. // 计算 content 的尺寸
  217. setUpdateContentSize();
  218. }
  219. /// <summary>
  220. /// scroll view 拖动时触发
  221. /// </summary>
  222. /// <param name="vt2"> 拖动数值 </param>
  223. private void onValueChanged(Vector2 vt2)
  224. {
  225. int _curScrollPerLineIndex = getCurScrollPerLineIndex();
  226. if (_curScrollPerLineIndex == curScrollPerLineIndex)
  227. {
  228. return;
  229. }
  230. hasPlayEffect = false;
  231. setUpdateRectItem(_curScrollPerLineIndex);
  232. }
  233. /// <summary>
  234. /// 刷新元素
  235. /// </summary>
  236. /// <param name="scrollPerLineIndex"> 当前所处的行数 </param>
  237. private void setUpdateRectItem(int scrollPerLineIndex)
  238. {
  239. if (scrollPerLineIndex < 0)
  240. {
  241. return;
  242. }
  243. curScrollPerLineIndex = scrollPerLineIndex;
  244. int startDataIndex = curScrollPerLineIndex * maxPerLine;
  245. int endDataIndex = (curScrollPerLineIndex + viewCount) * maxPerLine;
  246. // 移除
  247. for (int i = listItem.Count - 1; i >= 0; i--)
  248. {
  249. UIWarpContentItem1 item = listItem[i];
  250. int index = item.Index;
  251. if (index < startDataIndex || index >= endDataIndex)
  252. {
  253. item.Index = -1;
  254. listItem.Remove(item);
  255. unUseItem.Enqueue(item);
  256. }
  257. }
  258. // 显示
  259. for (int dataIndex = startDataIndex; dataIndex < endDataIndex; dataIndex++)
  260. {
  261. if (dataIndex >= mDataCount)
  262. {
  263. continue;
  264. }
  265. if (isExistDataByDataIndex(dataIndex))
  266. {
  267. continue;
  268. }
  269. createItem(dataIndex);
  270. }
  271. }
  272. /// <summary>
  273. /// 添加当前数据索引数据
  274. /// </summary>
  275. /// <param name="dataIndex"> 数据索引 </param>
  276. public void AddItem(int dataIndex)
  277. {
  278. if (dataIndex < 0 || dataIndex > mDataCount)
  279. {
  280. return;
  281. }
  282. // 检测是否需添加gameObject
  283. bool isNeedAdd = false;
  284. for (int i = listItem.Count - 1; i >= 0; i--)
  285. {
  286. UIWarpContentItem1 item = listItem[i];
  287. if (item.Index >= (mDataCount - 1))
  288. {
  289. isNeedAdd = true;
  290. break;
  291. }
  292. }
  293. setDataCount(mDataCount + 1);
  294. if (isNeedAdd)
  295. {
  296. for (int i = 0; i < listItem.Count; i++)
  297. {
  298. UIWarpContentItem1 item = listItem[i];
  299. int oldIndex = item.Index;
  300. if (oldIndex >= dataIndex)
  301. {
  302. item.Index = oldIndex + 1;
  303. }
  304. item = null;
  305. }
  306. setUpdateRectItem(getCurScrollPerLineIndex());
  307. }
  308. else
  309. {
  310. // 重新刷新数据
  311. for (int i = 0; i < listItem.Count; i++)
  312. {
  313. UIWarpContentItem1 item = listItem[i];
  314. int oldIndex = item.Index;
  315. if (oldIndex >= dataIndex)
  316. {
  317. item.Index = oldIndex;
  318. }
  319. item = null;
  320. }
  321. }
  322. }
  323. /// <summary>
  324. /// 删除当前数据索引下数据
  325. /// </summary>
  326. /// <param name="dataIndex"> 数据索引 </param>
  327. public void DelItem(int dataIndex)
  328. {
  329. if (dataIndex < 0 || dataIndex >= mDataCount)
  330. {
  331. return;
  332. }
  333. ////删除item逻辑三种情况
  334. ////1.只更新数据,不销毁gameObject,也不移除gameobject
  335. ////2.更新数据,且移除gameObject,不销毁gameObject
  336. ////3.更新数据,销毁gameObject
  337. bool isNeedDestroyGameObject = (listItem.Count >= mDataCount);
  338. setDataCount(mDataCount - 1);
  339. for (int i = listItem.Count - 1; i >= 0; i--)
  340. {
  341. UIWarpContentItem1 item = listItem[i];
  342. int oldIndex = item.Index;
  343. if (oldIndex == dataIndex)
  344. {
  345. listItem.Remove(item);
  346. if (isNeedDestroyGameObject)
  347. {
  348. GameObject.Destroy(item.gameObject);
  349. }
  350. else
  351. {
  352. item.Index = -1;
  353. unUseItem.Enqueue(item);
  354. }
  355. }
  356. if (oldIndex > dataIndex)
  357. {
  358. item.Index = oldIndex - 1;
  359. }
  360. }
  361. setUpdateRectItem(getCurScrollPerLineIndex());
  362. }
  363. /// <summary>
  364. /// 根据数据索引计算在 content 下的 local position
  365. /// </summary>
  366. /// <param name="index"> 数据索引 </param>
  367. /// <returns> 位置 </returns>
  368. public Vector3 getLocalPositionByIndex(int index)
  369. {
  370. if (index < 0)
  371. {
  372. return new Vector3(-2000.0f, 2000.0f, 0.0f);
  373. }
  374. float x = 0f;
  375. float y = 0f;
  376. float z = 0f;
  377. switch (arrangement)
  378. {
  379. // 水平方向
  380. case Arrangement.Horizontal:
  381. {
  382. x = (index / maxPerLine) * (cellWidth + cellWidthSpace);
  383. y = -(index % maxPerLine) * (cellHeight + cellHeightSpace);
  384. }
  385. break;
  386. // 垂着方向
  387. case Arrangement.Vertical:
  388. {
  389. x = (index % maxPerLine) * (cellWidth + cellWidthSpace);
  390. y = -(index / maxPerLine) * (cellHeight + cellHeightSpace);
  391. }
  392. break;
  393. default:
  394. {
  395. // 不需要处理
  396. }
  397. break;
  398. }
  399. return new Vector3(x, y, z);
  400. }
  401. /// <summary>
  402. /// 创建元素
  403. /// </summary>
  404. /// <param name="dataIndex"> 数据索引 </param>
  405. private void createItem(int dataIndex)
  406. {
  407. UIWarpContentItem1 item = null;
  408. if (unUseItem.Count > 0)
  409. {
  410. item = unUseItem.Dequeue();
  411. }
  412. else
  413. {
  414. GameObject temp = addChild(goItemPrefab, content);
  415. item = temp.GetComponent<UIWarpContentItem1>();
  416. if (item == null)
  417. {
  418. AssemblyHelper.Instance.BindScript("UIWarpContentItem1", temp);
  419. item = temp.GetComponent<UIWarpContentItem1>();
  420. }
  421. }
  422. Vector3 xxV = getLocalPositionByIndex(dataIndex);
  423. item.transform.localPosition = new Vector3(xxV.x, xxV.y - 2000, xxV.z);
  424. item.WarpContent = this;
  425. item.Index = dataIndex;
  426. listItem.Add(item);
  427. }
  428. /// <summary>
  429. /// 当前数据是否存在 List 中
  430. /// </summary>
  431. /// <param name="dataIndex"> 数据索引 </param>
  432. /// <returns> 是否已存在 </returns>
  433. private bool isExistDataByDataIndex(int dataIndex)
  434. {
  435. if (listItem == null || listItem.Count <= 0)
  436. {
  437. return false;
  438. }
  439. for (int i = 0; i < listItem.Count; i++)
  440. {
  441. if (listItem[i].Index == dataIndex)
  442. {
  443. return true;
  444. }
  445. }
  446. return false;
  447. }
  448. /// <summary>
  449. /// 根据Content偏移,计算当前开始显示所在数据列表中的行或列
  450. /// </summary>
  451. /// <returns> 行数 或 列数 </returns>
  452. private int getCurScrollPerLineIndex()
  453. {
  454. int result = 0;
  455. switch (arrangement)
  456. {
  457. // 水平方向
  458. case Arrangement.Horizontal:
  459. {
  460. result = Mathf.FloorToInt(-(content.anchoredPosition.x) / (cellWidth + cellWidthSpace));
  461. }
  462. break;
  463. // 垂着方向
  464. case Arrangement.Vertical:
  465. {
  466. result = Mathf.FloorToInt((content.anchoredPosition.y) / (cellHeight + cellHeightSpace));
  467. }
  468. break;
  469. default:
  470. {
  471. // 不需要处理
  472. }
  473. break;
  474. }
  475. if (result < 0)
  476. {
  477. result = 0;
  478. }
  479. return result;
  480. }
  481. /// <summary>
  482. /// 计算 content 的尺寸
  483. /// </summary>
  484. private void setUpdateContentSize()
  485. {
  486. // 行数,向上取整
  487. int lineCount = Mathf.CeilToInt((float)mDataCount / maxPerLine);
  488. switch (arrangement)
  489. {
  490. case Arrangement.Horizontal:
  491. {
  492. content.sizeDelta = new Vector2((cellWidth * lineCount) + (cellWidthSpace * (lineCount - 1)), content.sizeDelta.y);
  493. }
  494. break;
  495. case Arrangement.Vertical:
  496. {
  497. content.sizeDelta = new Vector2(content.sizeDelta.x, (cellHeight * lineCount) + (cellHeightSpace * (lineCount - 1)));
  498. }
  499. break;
  500. default:
  501. {
  502. // 不需要处理
  503. }
  504. break;
  505. }
  506. }
  507. /// <summary>
  508. /// 实例化预设对象 、添加实例化对象到指定的子对象下
  509. /// </summary>
  510. /// <param name="goPrefab"> 元素预制物体 </param>
  511. /// <param name="parent"> 父节点 </param>
  512. /// <returns> 新创建的元素对象 </returns>
  513. private GameObject addChild(GameObject goPrefab, Transform parent)
  514. {
  515. if (goPrefab == null || parent == null)
  516. {
  517. Debug.LogError("异常。UIWarpContent1.cs addChild(goPrefab = null || parent = null)");
  518. return null;
  519. }
  520. GameObject goChild = GameObject.Instantiate(goPrefab) as GameObject;
  521. goChild.transform.SetParent(parent, false);
  522. goChild.SetActive(true);
  523. return goChild;
  524. }
  525. /// <summary>
  526. /// 销毁
  527. /// </summary>
  528. private void OnDestroy()
  529. {
  530. scrollRect = null;
  531. content = null;
  532. goItemPrefab = null;
  533. onInitializeItem = null;
  534. listItem.Clear();
  535. unUseItem.Clear();
  536. listItem = null;
  537. unUseItem = null;
  538. }
  539. }