UIWarpContent.cs 16 KB

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