UIWrapScrollList.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.UI;
  6. namespace Assets.UI
  7. {
  8. /// <summary>
  9. /// Introduction: 无限列表
  10. /// Content上禁止挂载ContentSizeFilter和LayOutGroup之类组件
  11. /// Author: Cheng
  12. /// Time:
  13. /// </summary>
  14. [DisallowMultipleComponent]
  15. [RequireComponent(typeof (ScrollRect))]
  16. public class UIWrapScrollList : MonoBehaviour
  17. {
  18. public delegate void OnItemRender(int index, Transform child);
  19. public OnItemRender onItemRender;
  20. /// <summary>
  21. /// 排序方式
  22. /// </summary>
  23. public enum Arrangement
  24. {
  25. /// <summary>
  26. /// 横排
  27. /// </summary>
  28. Horizontal = 0,
  29. /// <summary>
  30. /// 竖排
  31. /// </summary>
  32. Vertical,
  33. }
  34. /// <summary>
  35. /// 水平对齐
  36. /// </summary>
  37. public enum HorizontalAlign
  38. {
  39. /// <summary>
  40. /// 居左
  41. /// </summary>
  42. Left,
  43. /// <summary>
  44. /// 居中
  45. /// </summary>
  46. Middle,
  47. /// <summary>
  48. /// 局右
  49. /// </summary>
  50. Right,
  51. }
  52. /// <summary>
  53. /// 垂直对齐
  54. /// </summary>
  55. public enum VerticalAlign
  56. {
  57. /// <summary>
  58. /// 居上
  59. /// </summary>
  60. Top,
  61. /// <summary>
  62. /// 居中
  63. /// </summary>
  64. Middle,
  65. /// <summary>
  66. /// 局下
  67. /// </summary>
  68. Bottom,
  69. }
  70. public Arrangement arrangement = Arrangement.Vertical;
  71. /// <summary>
  72. /// 当选择水平或垂直流动是有用,指每行/列最大个数
  73. /// </summary>
  74. public int MaxPerLine
  75. {
  76. get { return maxPerLine; }
  77. set { SetMaxPerLine(value); }
  78. }
  79. /// <summary>
  80. /// 行距
  81. /// </summary>
  82. public float rowSpace = 0;
  83. /// <summary>
  84. /// 列距
  85. /// </summary>
  86. public float columuSpace = 0;
  87. public HorizontalAlign horizontalAlign = HorizontalAlign.Left;
  88. public VerticalAlign verticalAlign = VerticalAlign.Top;
  89. /// <summary>
  90. /// 边缘留空 上
  91. /// </summary>
  92. public float marginTop = 0;
  93. /// <summary>
  94. /// 边缘留空 下
  95. /// </summary>
  96. public float marginBottom = 0;
  97. /// <summary>
  98. /// 边缘留空 左
  99. /// </summary>
  100. public float marginLeft = 0;
  101. /// <summary>
  102. /// 边缘留空 右
  103. /// </summary>
  104. public float marginRight = 0;
  105. /// <summary>
  106. /// 渲染子节点
  107. /// </summary>
  108. public GameObject Child
  109. {
  110. get { return item; }
  111. set { SetItem(value); }
  112. }
  113. /// <summary>
  114. /// 总个数
  115. /// </summary>
  116. public int ChildCount
  117. {
  118. get { return childCount; }
  119. set { SetChildCount(value, true); }
  120. }
  121. /// <summary>
  122. /// 设置显示窗口大小
  123. /// </summary>
  124. public Vector2 ViewPort
  125. {
  126. get { return viewPort; }
  127. set { SetViewPort(value); }
  128. }
  129. public GameObject item;
  130. ScrollRect scrollRect;
  131. Vector2 viewPort;
  132. RectTransform content;
  133. Vector2 itemSize;
  134. List<Transform> items;
  135. Dictionary<int, int> contains;
  136. List<int> outOfContains;
  137. int childCount; //需要渲染的总数据个数
  138. int scrollLineIndex; //当前第一个元素索引
  139. int totalCount; //在UI中显示的个数(不乘以maxPerLine)
  140. Vector2 startPos; //第一个元素所在位置
  141. int startIndex; //当前渲染起始坐标
  142. int endIndex; //当前渲染结束坐标
  143. int maxPerLine;
  144. void Start()
  145. {
  146. maxPerLine = maxPerLine == 0 ? 1 : maxPerLine;
  147. items = new List<Transform>();
  148. contains = new Dictionary<int, int>();
  149. outOfContains = new List<int>();
  150. scrollRect = transform.GetComponent<ScrollRect>();
  151. content = scrollRect.content;
  152. if (content == null)
  153. {
  154. Debug.Log("ScrollRect " + scrollRect.gameObject.name + " Has No Content, Please Check And Retry.");
  155. return;
  156. }
  157. viewPort = scrollRect.viewport.rect.size;
  158. content.anchorMax = new Vector2(0, 1);
  159. content.anchorMin = new Vector2(0, 1);
  160. content.pivot = new Vector2(0, 1);
  161. ReBuild();
  162. }
  163. /// <summary>
  164. /// 当子节点、Mask、maxPerLine
  165. /// </summary>
  166. public void ReBuild()
  167. {
  168. if (scrollRect == null || content == null || item == null) return;
  169. ResetChildren();
  170. Vector2 maskSize = viewPort;
  171. int count = 0;
  172. if (arrangement == Arrangement.Horizontal)
  173. {
  174. count = Mathf.CeilToInt(maskSize.x/itemSize.x) + 1; //横向列数
  175. startPos = Vector2.zero;
  176. startPos.x = marginLeft;
  177. if (verticalAlign == VerticalAlign.Top)
  178. {
  179. startPos.y = -marginTop;
  180. }
  181. else if (verticalAlign == VerticalAlign.Middle)
  182. {
  183. startPos.y = -(maskSize.y*0.5f - (itemSize.y*maxPerLine + (maxPerLine - 1)*rowSpace)*0.5f);
  184. }
  185. else if (verticalAlign == VerticalAlign.Bottom)
  186. {
  187. startPos.y = -(maskSize.y - marginBottom - itemSize.y*maxPerLine - rowSpace*(maxPerLine - 1));
  188. }
  189. //优化:不在一开始生产所有的可见格子
  190. //for (int i = 0; i < count; i++)
  191. //{
  192. // for (int j = 0; j < maxPerLine; j++)
  193. // {
  194. // RectTransform child = CreateItem(i*maxPerLine + j);
  195. // child.localPosition = startPos +
  196. // new Vector2(i*itemSize.x + i*columuSpace, -j*itemSize.y - j*rowSpace);
  197. // }
  198. //}
  199. }
  200. else if (arrangement == Arrangement.Vertical)
  201. {
  202. count = Mathf.CeilToInt(maskSize.y/itemSize.y) + 1; //竖向行数
  203. startPos = Vector2.zero;
  204. startPos.y = -marginTop; //重置开始节点位置
  205. if (horizontalAlign == HorizontalAlign.Left)
  206. {
  207. startPos.x = marginLeft;
  208. }
  209. else if (horizontalAlign == HorizontalAlign.Middle)
  210. {
  211. startPos.x = (maskSize.x*0.5f - (itemSize.x*maxPerLine + (maxPerLine - 1)*columuSpace)*0.5f);
  212. }
  213. else if (horizontalAlign == HorizontalAlign.Right)
  214. {
  215. startPos.x = maskSize.x - marginRight - itemSize.x*maxPerLine - columuSpace*(maxPerLine - 1);
  216. }
  217. //for (int i = 0; i < count; i++)
  218. //{
  219. // for (int j = 0; j < maxPerLine; j++)
  220. // {
  221. // RectTransform child = CreateItem(i*maxPerLine + j);
  222. // child.localPosition = startPos +
  223. // new Vector2(j*itemSize.x + j*columuSpace, -i*itemSize.y - i*rowSpace);
  224. // }
  225. //}
  226. }
  227. totalCount = count;
  228. Debug.Log(count + " child count is " + childCount );
  229. SetChildCount(childCount, true);
  230. BackTop();
  231. scrollRect.onValueChanged.RemoveAllListeners();
  232. scrollRect.onValueChanged.AddListener(OnValueChanged);
  233. }
  234. /// <summary>
  235. /// 列表滚动
  236. /// </summary>
  237. /// <param name="vec"></param>
  238. private void OnValueChanged(Vector2 vec)
  239. {
  240. switch (arrangement)
  241. {
  242. case Arrangement.Horizontal:
  243. // if (vec.x < 0.0f || vec.x >= 1.0f)
  244. // return;
  245. vec.x = Mathf.Clamp(vec.x, 0, 1);
  246. break;
  247. case Arrangement.Vertical:
  248. // if (vec.y <= 0.0f || vec.y >= 1.0f)
  249. // return;
  250. vec.y = Mathf.Clamp(vec.y, 0, 1);
  251. break;
  252. }
  253. int curLineIndex = GetCurLineIndex();
  254. if (curLineIndex != scrollLineIndex)
  255. UpdateRectItem(curLineIndex, false);
  256. }
  257. /// <summary>
  258. /// 获取页面第一行索引
  259. /// </summary>
  260. /// <returns></returns>
  261. private int GetCurLineIndex()
  262. {
  263. switch (arrangement)
  264. {
  265. case Arrangement.Horizontal:
  266. return
  267. Mathf.FloorToInt(Mathf.Abs(content.anchoredPosition.x < 0.1f? content.anchoredPosition.x : 0.1f - marginLeft)/
  268. (columuSpace + itemSize.x));
  269. case Arrangement.Vertical:
  270. return
  271. Mathf.FloorToInt(Mathf.Abs(content.anchoredPosition.y>-0.1f?content.anchoredPosition.y:-0.1f - marginTop)/
  272. (rowSpace + itemSize.y));
  273. }
  274. return 0;
  275. }
  276. /// <summary>
  277. /// 更新数据(待修改问出现的才刷新)
  278. /// </summary>
  279. /// <param name="curLineIndex"></param>
  280. /// <param name="forceRender"></param>
  281. private void UpdateRectItem(int curLineIndex, bool forceRender)
  282. {
  283. if (curLineIndex < 0)
  284. return;
  285. startIndex = curLineIndex*maxPerLine;
  286. endIndex = (curLineIndex + totalCount)*maxPerLine;
  287. if (endIndex >= childCount)
  288. endIndex = childCount;
  289. contains.Clear(); //渲染序号
  290. outOfContains.Clear(); //items的索引
  291. for (int i = 0; i < items.Count; i++)//如果当前已渲染的item中包含
  292. {
  293. int index = int.Parse(items[i].gameObject.name);
  294. if (index < startIndex || index >= endIndex)
  295. {
  296. outOfContains.Add(i);
  297. items[i].gameObject.SetActive(false);
  298. }
  299. else
  300. {
  301. items[i].gameObject.SetActive(true);
  302. contains.Add(index, i);
  303. }
  304. }
  305. // *************更改渲染****************
  306. for (int i = startIndex; i < endIndex; i++)
  307. {
  308. if (!contains.ContainsKey(i))
  309. {
  310. Transform child = items[outOfContains[0]];
  311. outOfContains.RemoveAt(0);
  312. child.gameObject.SetActive(true);
  313. int row = i/maxPerLine;
  314. int col = i%maxPerLine;
  315. if (arrangement == Arrangement.Vertical)
  316. child.localPosition = startPos +
  317. new Vector2(col*itemSize.x + (col)*columuSpace,
  318. -row*itemSize.y - (row)*rowSpace);
  319. else
  320. child.localPosition = startPos +
  321. new Vector2(row*itemSize.x + (row)*columuSpace,
  322. -col*itemSize.y - (col)*rowSpace);
  323. child.gameObject.name = i.ToString();
  324. // Debug.LogWarning("item changed and update ");
  325. if (onItemRender != null){
  326. onItemRender(i, child);
  327. }
  328. }
  329. else if (forceRender)
  330. {
  331. // Debug.LogWarning("item changed and update ");
  332. if (onItemRender != null){
  333. onItemRender(i, items[contains[i]]);
  334. }
  335. }
  336. }
  337. scrollLineIndex = curLineIndex;
  338. }
  339. /// <summary>
  340. /// 移除当前所有
  341. /// </summary>
  342. private void ResetChildren()
  343. {
  344. items.Clear();
  345. for (int i = 0; i < content.childCount; i++)
  346. {
  347. Transform child = content.GetChild(i);
  348. child.gameObject.SetActive(false);
  349. }
  350. }
  351. /// <summary>
  352. /// 创建新节点
  353. /// </summary>
  354. /// <param name="index"></param>
  355. private RectTransform CreateItem(int index)
  356. {
  357. Transform child;
  358. // Debug.Log("create item is " + index );
  359. if (content.childCount > index)
  360. {
  361. child = content.GetChild(index);
  362. }
  363. else
  364. {
  365. // Debug.Log("create item item " + item.name );
  366. GameObject obj = GameObject.Instantiate(item) as GameObject;
  367. obj.transform.SetParent(content);
  368. obj.transform.localScale = Vector3.one;
  369. child = obj.transform;
  370. }
  371. child.gameObject.name = index.ToString();
  372. items.Add(child);
  373. return child as RectTransform;
  374. }
  375. /// <summary>
  376. /// 设置资源
  377. /// </summary>
  378. /// <param name="child"></param>
  379. public void SetItem(GameObject child)
  380. {
  381. if (child == null) return;
  382. this.item = child;
  383. RectTransform itemTrans = child.transform as RectTransform;
  384. itemTrans.pivot = new Vector2(0, 1);
  385. itemSize = itemTrans.sizeDelta;
  386. ReBuild();
  387. }
  388. /// <summary>
  389. /// 更新需要渲染的个数
  390. /// </summary>
  391. /// <param name="value"></param>
  392. public void SetChildCount(int value, bool forceRender)
  393. {
  394. if (value < 0) childCount = 0;
  395. else childCount = value;
  396. if(totalCount <= 0)//还未初始化
  397. return;
  398. if (value > items.Count && items.Count < maxPerLine * totalCount)
  399. {
  400. //当前格子数量少于应生成的数量
  401. int count = items.Count;
  402. int max = value < maxPerLine*totalCount ? value : maxPerLine*totalCount;
  403. for (int i = count; i < max; i++)
  404. {
  405. int row = i / maxPerLine;
  406. int col = i % maxPerLine;
  407. RectTransform child = CreateItem(i);
  408. if (arrangement == Arrangement.Vertical)
  409. child.localPosition = startPos +
  410. new Vector2(col * itemSize.x + (col) * columuSpace,
  411. -row * itemSize.y - (row) * rowSpace);
  412. else
  413. child.localPosition = startPos +
  414. new Vector2(row * itemSize.x + (row) * columuSpace,
  415. -col * itemSize.y - (col) * rowSpace);
  416. }
  417. }
  418. if (content == null) return;
  419. int rc = Mathf.CeilToInt((float) childCount/(float) maxPerLine); //设置content的大小
  420. if (arrangement == Arrangement.Horizontal)
  421. {
  422. content.sizeDelta = new Vector2(marginLeft + marginRight + itemSize.x*rc + columuSpace*(rc - 1),
  423. viewPort.y);
  424. if (content.sizeDelta.x > viewPort.x && content.anchoredPosition.x < viewPort.x - content.sizeDelta.x)
  425. content.anchoredPosition = new Vector2(viewPort.x - content.sizeDelta.x, content.anchoredPosition.y);
  426. }
  427. else
  428. {
  429. content.sizeDelta = new Vector2(viewPort.x, marginTop + marginBottom + itemSize.y*rc + rowSpace*(rc - 1));
  430. if (content.sizeDelta.y > viewPort.y && content.anchoredPosition.y > content.sizeDelta.y - viewPort.y)
  431. content.anchoredPosition = new Vector2(content.anchoredPosition.x, content.sizeDelta.y - viewPort.y);
  432. }
  433. UpdateRectItem(GetCurLineIndex(), true);
  434. }
  435. /// <summary>
  436. /// 添加子节点
  437. /// </summary>
  438. /// <param name="index"></param>
  439. public void AddChild(int index)
  440. {
  441. if (index < 0) return;
  442. startIndex = scrollLineIndex*maxPerLine;
  443. endIndex = (scrollLineIndex + totalCount)*maxPerLine;
  444. SetChildCount(childCount + 1, index >= startIndex && index < endIndex);
  445. }
  446. /// <summary>
  447. /// 删除子节点
  448. /// </summary>
  449. /// <param name="index"></param>
  450. public void RemoveChild(int index)
  451. {
  452. if (index < 0 || index >= childCount) return;
  453. startIndex = scrollLineIndex*maxPerLine;
  454. endIndex = (scrollLineIndex + totalCount)*maxPerLine;
  455. SetChildCount(childCount - 1, index >= startIndex && index < endIndex);
  456. }
  457. /// <summary>
  458. /// 设置显示窗口大小(现在貌似可以废弃了)
  459. /// </summary>
  460. /// <param name="port"></param>
  461. public void SetViewPort(Vector2 port)
  462. {
  463. if (port == viewPort) return;
  464. viewPort = port;
  465. ReBuild();
  466. }
  467. /// <summary>
  468. /// 设置行列最大
  469. /// </summary>
  470. /// <param name="max"></param>
  471. public void SetMaxPerLine(int max)
  472. {
  473. maxPerLine = max;
  474. ReBuild();
  475. }
  476. /// <summary>
  477. /// 返回顶部
  478. /// </summary>
  479. public void BackTop()
  480. {
  481. content.localPosition = Vector3.zero;
  482. UpdateRectItem(0, true);
  483. }
  484. /// <summary>
  485. /// 返回底部
  486. /// </summary>
  487. public void BackBottom()
  488. {
  489. if (arrangement == Arrangement.Vertical)
  490. {
  491. content.localPosition = new Vector3(0, -viewPort.y + content.sizeDelta.y, 0);
  492. }
  493. else
  494. {
  495. content.localPosition = new Vector3(viewPort.x - content.sizeDelta.x, 0);
  496. }
  497. UpdateRectItem(Mathf.CeilToInt((float) childCount/(float) maxPerLine) - totalCount + 1, true);
  498. }
  499. public void RefreshViewItem()
  500. {
  501. UpdateRectItem(scrollLineIndex, true);
  502. }
  503. public void SetArrangement(int arr)
  504. {
  505. arrangement = (Arrangement) arr;
  506. }
  507. public void SetHorizontal(int h)
  508. {
  509. horizontalAlign = (HorizontalAlign) h;
  510. }
  511. public void SetVerticle(int v)
  512. {
  513. verticalAlign = (VerticalAlign) v;
  514. }
  515. }
  516. }