UICircularScrollView.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. //*****************************-》 基类 循环列表 《-****************************
  2. //author kim
  3. //初始化:
  4. // Init(callBackFunc)
  5. //刷新整个列表(首次调用和数量变化时调用):
  6. // ShowList(int = 数量)
  7. //刷新单个项:
  8. // UpdateCell(int = 索引)
  9. //刷新列表数据(无数量变化时调用):
  10. // UpdateList()
  11. //回调:
  12. //Func(GameObject = Cell, int = Index) //刷新列表
  13. using UnityEngine;
  14. using UnityEngine.UI;
  15. using System.Collections.Generic;
  16. using System.Collections;
  17. using UnityEngine.EventSystems;
  18. using System;
  19. namespace CircularScrollView
  20. {
  21. public class UIUtils
  22. {
  23. public static void SetActive(GameObject obj, bool isActive)
  24. {
  25. if (obj != null)
  26. {
  27. obj.SetActive(isActive);
  28. }
  29. }
  30. }
  31. public enum e_Direction
  32. {
  33. Horizontal,
  34. Vertical
  35. }
  36. public class UICircularScrollView : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
  37. {
  38. public GameObject m_PointingFirstArrow;
  39. public GameObject m_PointingEndArrow;
  40. public e_Direction m_Direction = e_Direction.Horizontal;
  41. public bool m_IsShowArrow = true;
  42. public int m_Row = 1;
  43. public float m_Spacing = 0f; //间距
  44. public GameObject m_CellGameObject; //指定的cell
  45. protected Action<GameObject, int> m_FuncCallBackFunc;
  46. protected Action<GameObject, int> m_FuncOnClickCallBack;
  47. protected Action<int, bool, GameObject> m_FuncOnButtonClickCallBack;
  48. protected RectTransform rectTrans;
  49. protected float m_PlaneWidth;
  50. protected float m_PlaneHeight;
  51. protected float m_ContentWidth;
  52. protected float m_ContentHeight;
  53. protected float m_CellObjectWidth;
  54. protected float m_CellObjectHeight;
  55. protected GameObject m_Content;
  56. protected RectTransform m_ContentRectTrans;
  57. private bool m_isInited = false;
  58. //记录 物体的坐标 和 物体
  59. protected struct CellInfo
  60. {
  61. public Vector3 pos;
  62. public GameObject obj;
  63. };
  64. protected CellInfo[] m_CellInfos;
  65. protected bool m_IsInited = false;
  66. protected ScrollRect m_ScrollRect;
  67. protected int m_MaxCount = -1; //列表数量
  68. protected int m_MinIndex = -1;
  69. protected int m_MaxIndex = -1;
  70. protected bool m_IsClearList = false; //是否清空列表
  71. public virtual void Init(Action<GameObject, int> callBack)
  72. {
  73. Init(callBack, null);
  74. }
  75. public virtual void Init(Action<GameObject, int> callBack, Action<GameObject, int> onClickCallBack, Action<int, bool, GameObject> onButtonClickCallBack)
  76. {
  77. if (onButtonClickCallBack != null)
  78. {
  79. m_FuncOnButtonClickCallBack = onButtonClickCallBack;
  80. }
  81. Init(callBack, onClickCallBack);
  82. }
  83. public virtual void Init(Action<GameObject, int> callBack, Action<GameObject, int> onClickCallBack)
  84. {
  85. DisposeAll();
  86. m_FuncCallBackFunc = callBack;
  87. if (onClickCallBack != null)
  88. {
  89. m_FuncOnClickCallBack = onClickCallBack;
  90. }
  91. if (m_isInited)
  92. return;
  93. m_Content = this.GetComponent<ScrollRect>().content.gameObject;
  94. if (m_CellGameObject == null)
  95. {
  96. m_CellGameObject = GameObject.Instantiate(Resources.Load("FY/Prefabs/Item/Cell"))as GameObject;
  97. }
  98. /* Cell 处理 */
  99. //m_CellGameObject.transform.SetParent(m_Content.transform.parent, false);
  100. SetPoolsObj(m_CellGameObject);
  101. RectTransform cellRectTrans = m_CellGameObject.GetComponent<RectTransform>();
  102. cellRectTrans.pivot = new Vector2(0f, 1f);
  103. CheckAnchor(cellRectTrans);
  104. cellRectTrans.anchoredPosition = Vector2.zero;
  105. //记录 Cell 信息
  106. m_CellObjectHeight = cellRectTrans.rect.height;
  107. m_CellObjectWidth = cellRectTrans.rect.width;
  108. //记录 Plane 信息
  109. rectTrans = GetComponent<RectTransform>();
  110. Rect planeRect = rectTrans.rect;
  111. m_PlaneHeight = planeRect.height;
  112. m_PlaneWidth = planeRect.width;
  113. //记录 Content 信息
  114. m_ContentRectTrans = m_Content.GetComponent<RectTransform>();
  115. Rect contentRect = m_ContentRectTrans.rect;
  116. m_ContentHeight = contentRect.height;
  117. m_ContentWidth = contentRect.width;
  118. m_ContentRectTrans.pivot = new Vector2(0f, 1f);
  119. //m_ContentRectTrans.sizeDelta = new Vector2 (planeRect.width, planeRect.height);
  120. //m_ContentRectTrans.anchoredPosition = Vector2.zero;
  121. CheckAnchor(m_ContentRectTrans);
  122. m_ScrollRect = this.GetComponent<ScrollRect>();
  123. m_ScrollRect.onValueChanged.RemoveAllListeners();
  124. //添加滑动事件
  125. m_ScrollRect.onValueChanged.AddListener(delegate (Vector2 value) { ScrollRectListener(value); });
  126. if (m_PointingFirstArrow != null || m_PointingEndArrow != null)
  127. {
  128. m_ScrollRect.onValueChanged.AddListener(delegate (Vector2 value) { OnDragListener(value); });
  129. OnDragListener(Vector2.zero);
  130. }
  131. //InitScrollBarGameObject(); // 废弃
  132. m_isInited = true;
  133. }
  134. //检查 Anchor 是否正确
  135. private void CheckAnchor(RectTransform rectTrans)
  136. {
  137. if (m_Direction == e_Direction.Vertical)
  138. {
  139. if (!((rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(0, 1)) ||
  140. (rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(1, 1))))
  141. {
  142. rectTrans.anchorMin = new Vector2(0, 1);
  143. rectTrans.anchorMax = new Vector2(1, 1);
  144. }
  145. }
  146. else
  147. {
  148. if (!((rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(0, 1)) ||
  149. (rectTrans.anchorMin == new Vector2(0, 0) && rectTrans.anchorMax == new Vector2(0, 1))))
  150. {
  151. rectTrans.anchorMin = new Vector2(0, 0);
  152. rectTrans.anchorMax = new Vector2(0, 1);
  153. }
  154. }
  155. }
  156. //实时刷新列表时用
  157. public virtual void UpdateList()
  158. {
  159. for (int i = 0, length = m_CellInfos.Length; i < length; i++)
  160. {
  161. CellInfo cellInfo = m_CellInfos[i];
  162. if (cellInfo.obj != null)
  163. {
  164. float rangePos = m_Direction == e_Direction.Vertical ? cellInfo.pos.y : cellInfo.pos.x;
  165. if (!IsOutRange(rangePos))
  166. {
  167. Func(m_FuncCallBackFunc, cellInfo.obj, true);
  168. }
  169. }
  170. }
  171. }
  172. //刷新某一项
  173. public void UpdateCell(int index)
  174. {
  175. CellInfo cellInfo = m_CellInfos[index - 1];
  176. if (cellInfo.obj != null)
  177. {
  178. float rangePos = m_Direction == e_Direction.Vertical ? cellInfo.pos.y : cellInfo.pos.x;
  179. if (!IsOutRange(rangePos))
  180. {
  181. Func(m_FuncCallBackFunc, cellInfo.obj);
  182. }
  183. }
  184. }
  185. public virtual void ShowList(string numStr) { }
  186. public virtual void ShowList(int num)
  187. {
  188. m_MinIndex = -1;
  189. m_MaxIndex = -1;
  190. //-> 计算 Content 尺寸
  191. if (m_Direction == e_Direction.Vertical)
  192. {
  193. float contentSize = (m_Spacing + m_CellObjectHeight) * Mathf.CeilToInt((float)num / m_Row);
  194. m_ContentHeight = contentSize;
  195. m_ContentWidth = m_ContentRectTrans.sizeDelta.x;
  196. contentSize = contentSize < rectTrans.rect.height ? rectTrans.rect.height : contentSize;
  197. m_ContentRectTrans.sizeDelta = new Vector2(m_ContentWidth, contentSize);
  198. if (num != m_MaxCount)
  199. {
  200. m_ContentRectTrans.anchoredPosition = new Vector2(m_ContentRectTrans.anchoredPosition.x, 0);
  201. }
  202. }
  203. else
  204. {
  205. float contentSize = (m_Spacing + m_CellObjectWidth) * Mathf.CeilToInt((float)num / m_Row);
  206. m_ContentWidth = contentSize;
  207. m_ContentHeight = m_ContentRectTrans.sizeDelta.x;
  208. contentSize = contentSize < rectTrans.rect.width ? rectTrans.rect.width : contentSize;
  209. m_ContentRectTrans.sizeDelta = new Vector2(contentSize, m_ContentHeight);
  210. if (num != m_MaxCount)
  211. {
  212. m_ContentRectTrans.anchoredPosition = new Vector2(0, m_ContentRectTrans.anchoredPosition.y);
  213. }
  214. }
  215. //-> 计算 开始索引
  216. int lastEndIndex = 0;
  217. //-> 过多的物体 扔到对象池 ( 首次调 ShowList函数时 则无效 )
  218. if (m_IsInited)
  219. {
  220. lastEndIndex = num - m_MaxCount > 0 ? m_MaxCount : num;
  221. lastEndIndex = m_IsClearList ? 0 : lastEndIndex;
  222. int count = m_IsClearList ? m_CellInfos.Length : m_MaxCount;
  223. for (int i = lastEndIndex; i < count; i++)
  224. {
  225. if (m_CellInfos[i].obj != null)
  226. {
  227. SetPoolsObj(m_CellInfos[i].obj);
  228. m_CellInfos[i].obj = null;
  229. }
  230. }
  231. }
  232. //-> 以下四行代码 在for循环所用
  233. CellInfo[] tempCellInfos = m_CellInfos;
  234. m_CellInfos = new CellInfo[num];
  235. //-> 1: 计算 每个Cell坐标并存储 2: 显示范围内的 Cell
  236. for (int i = 0; i < num; i++)
  237. {
  238. // * -> 存储 已有的数据 ( 首次调 ShowList函数时 则无效 )
  239. if (m_MaxCount != -1 && i < lastEndIndex)
  240. {
  241. CellInfo tempCellInfo = tempCellInfos[i];
  242. //-> 计算是否超出范围
  243. float rPos = m_Direction == e_Direction.Vertical ? tempCellInfo.pos.y : tempCellInfo.pos.x;
  244. if (!IsOutRange(rPos))
  245. {
  246. //-> 记录显示范围中的 首位index 和 末尾index
  247. m_MinIndex = m_MinIndex == -1 ? i : m_MinIndex; //首位index
  248. m_MaxIndex = i; // 末尾index
  249. if (tempCellInfo.obj == null)
  250. {
  251. tempCellInfo.obj = GetPoolsObj();
  252. }
  253. tempCellInfo.obj.transform.GetComponent<RectTransform>().anchoredPosition = tempCellInfo.pos;
  254. tempCellInfo.obj.name = i.ToString();
  255. tempCellInfo.obj.SetActive(true);
  256. Func(m_FuncCallBackFunc, tempCellInfo.obj);
  257. }
  258. else
  259. {
  260. SetPoolsObj(tempCellInfo.obj);
  261. tempCellInfo.obj = null;
  262. }
  263. m_CellInfos[i] = tempCellInfo;
  264. continue;
  265. }
  266. CellInfo cellInfo = new CellInfo();
  267. float pos = 0; //坐标( isVertical ? 记录Y : 记录X )
  268. float rowPos = 0; //计算每排里面的cell 坐标
  269. // * -> 计算每个Cell坐标
  270. if (m_Direction == e_Direction.Vertical)
  271. {
  272. pos = m_CellObjectHeight * Mathf.FloorToInt(i / m_Row) + m_Spacing * Mathf.FloorToInt(i / m_Row);
  273. rowPos = m_CellObjectWidth * (i % m_Row) + m_Spacing * (i % m_Row);
  274. cellInfo.pos = new Vector3(rowPos, -pos, 0);
  275. }
  276. else
  277. {
  278. pos = m_CellObjectWidth * Mathf.FloorToInt(i / m_Row) + m_Spacing * Mathf.FloorToInt(i / m_Row);
  279. rowPos = m_CellObjectHeight * (i % m_Row) + m_Spacing * (i % m_Row);
  280. cellInfo.pos = new Vector3(pos, -rowPos, 0);
  281. }
  282. //-> 计算是否超出范围
  283. float cellPos = m_Direction == e_Direction.Vertical ? cellInfo.pos.y : cellInfo.pos.x;
  284. if (IsOutRange(cellPos))
  285. {
  286. cellInfo.obj = null;
  287. m_CellInfos[i] = cellInfo;
  288. continue;
  289. }
  290. //-> 记录显示范围中的 首位index 和 末尾index
  291. m_MinIndex = m_MinIndex == -1 ? i : m_MinIndex; //首位index
  292. m_MaxIndex = i; // 末尾index
  293. //-> 取或创建 Cell
  294. GameObject cell = GetPoolsObj();
  295. cell.transform.GetComponent<RectTransform>().anchoredPosition = cellInfo.pos;
  296. cell.gameObject.name = i.ToString();
  297. //-> 存数据
  298. cellInfo.obj = cell;
  299. m_CellInfos[i] = cellInfo;
  300. //-> 回调 函数
  301. Func(m_FuncCallBackFunc, cell);
  302. }
  303. m_MaxCount = num;
  304. m_IsInited = true;
  305. OnDragListener(Vector2.zero);
  306. }
  307. // 更新滚动区域的大小
  308. public void UpdateSize()
  309. {
  310. Rect rect = GetComponent<RectTransform>().rect;
  311. m_PlaneHeight = rect.height;
  312. m_PlaneWidth = rect.width;
  313. }
  314. //滑动事件
  315. protected virtual void ScrollRectListener(Vector2 value)
  316. {
  317. UpdateCheck();
  318. }
  319. private void UpdateCheck()
  320. {
  321. if (m_CellInfos == null)
  322. return;
  323. //检查超出范围
  324. for (int i = 0, length = m_CellInfos.Length; i < length; i++)
  325. {
  326. CellInfo cellInfo = m_CellInfos[i];
  327. GameObject obj = cellInfo.obj;
  328. Vector3 pos = cellInfo.pos;
  329. float rangePos = m_Direction == e_Direction.Vertical ? pos.y : pos.x;
  330. //判断是否超出显示范围
  331. if (IsOutRange(rangePos))
  332. {
  333. //把超出范围的cell 扔进 poolsObj里
  334. if (obj != null)
  335. {
  336. SetPoolsObj(obj);
  337. m_CellInfos[i].obj = null;
  338. }
  339. }
  340. else
  341. {
  342. if (obj == null)
  343. {
  344. //优先从 poolsObj中 取出 (poolsObj为空则返回 实例化的cell)
  345. GameObject cell = GetPoolsObj();
  346. cell.transform.localPosition = pos;
  347. cell.gameObject.name = i.ToString();
  348. m_CellInfos[i].obj = cell;
  349. Func(m_FuncCallBackFunc, cell);
  350. }
  351. }
  352. }
  353. }
  354. //判断是否超出显示范围
  355. protected bool IsOutRange(float pos)
  356. {
  357. Vector3 listP = m_ContentRectTrans.anchoredPosition;
  358. if (m_Direction == e_Direction.Vertical)
  359. {
  360. if (pos + listP.y > m_CellObjectHeight || pos + listP.y < -rectTrans.rect.height)
  361. {
  362. return true;
  363. }
  364. }
  365. else
  366. {
  367. if (pos + listP.x < -m_CellObjectWidth || pos + listP.x > rectTrans.rect.width)
  368. {
  369. return true;
  370. }
  371. }
  372. return false;
  373. }
  374. //对象池 机制 (存入, 取出) cell
  375. protected Stack<GameObject> poolsObj = new Stack<GameObject>();
  376. //取出 cell
  377. protected virtual GameObject GetPoolsObj()
  378. {
  379. GameObject cell = null;
  380. if (poolsObj.Count > 0)
  381. {
  382. cell = poolsObj.Pop();
  383. }
  384. if (cell == null)
  385. {
  386. cell = Instantiate(m_CellGameObject) as GameObject;
  387. }
  388. cell.transform.SetParent(m_Content.transform);
  389. cell.transform.localScale = Vector3.one;
  390. UIUtils.SetActive(cell, true);
  391. return cell;
  392. }
  393. //存入 cell
  394. protected virtual void SetPoolsObj(GameObject cell)
  395. {
  396. if (cell != null)
  397. {
  398. poolsObj.Push(cell);
  399. UIUtils.SetActive(cell, false);
  400. }
  401. }
  402. //回调
  403. protected void Func(Action<GameObject, int> func, GameObject selectObject, bool isUpdate = false)
  404. {
  405. int num = int.Parse(selectObject.name) + 1;
  406. if (func != null)
  407. {
  408. func(selectObject, num);
  409. }
  410. }
  411. public void DisposeAll()
  412. {
  413. if (m_FuncCallBackFunc != null)
  414. {
  415. m_FuncCallBackFunc = null;
  416. }
  417. if (m_FuncOnClickCallBack != null)
  418. {
  419. m_FuncOnClickCallBack = null;
  420. }
  421. }
  422. protected void OnDestroy()
  423. {
  424. DisposeAll();
  425. }
  426. public virtual void OnClickCell(GameObject cell) { }
  427. //-> ExpandCircularScrollView 函数
  428. public virtual void OnClickExpand(int index) { }
  429. //-> FlipCircularScrollView 函数
  430. public virtual void SetToPageIndex(int index) { }
  431. public virtual void OnBeginDrag(PointerEventData eventData)
  432. {
  433. }
  434. public void OnDrag(PointerEventData eventData)
  435. {
  436. }
  437. public virtual void OnEndDrag(PointerEventData eventData)
  438. {
  439. }
  440. protected void OnDragListener(Vector2 value)
  441. {
  442. float normalizedPos = m_Direction == e_Direction.Vertical ? m_ScrollRect.verticalNormalizedPosition : m_ScrollRect.horizontalNormalizedPosition;
  443. if (m_Direction == e_Direction.Vertical)
  444. {
  445. if (m_ContentHeight - rectTrans.rect.height < 10)
  446. {
  447. UIUtils.SetActive(m_PointingFirstArrow, false);
  448. UIUtils.SetActive(m_PointingEndArrow, false);
  449. return;
  450. }
  451. }
  452. else
  453. {
  454. if (m_ContentWidth - rectTrans.rect.width < 10)
  455. {
  456. UIUtils.SetActive(m_PointingFirstArrow, false);
  457. UIUtils.SetActive(m_PointingEndArrow, false);
  458. return;
  459. }
  460. }
  461. if (normalizedPos >= 0.9)
  462. {
  463. UIUtils.SetActive(m_PointingFirstArrow, false);
  464. UIUtils.SetActive(m_PointingEndArrow, true);
  465. }
  466. else if (normalizedPos <= 0.1)
  467. {
  468. UIUtils.SetActive(m_PointingFirstArrow, true);
  469. UIUtils.SetActive(m_PointingEndArrow, false);
  470. }
  471. else
  472. {
  473. UIUtils.SetActive(m_PointingFirstArrow, true);
  474. UIUtils.SetActive(m_PointingEndArrow, true);
  475. }
  476. }
  477. }
  478. }