123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine.UI;
- //瀑布流Scroll,没有居中功能(Todo),使用方法:添加回调onCreate,onUpdate
- public class UILoop : MonoBehaviour
- {
- //返回Index和相应的Gameobject
- public delegate void UILoopCallBack(int globalIndex, GameObject go);
- enum Direction
- {
- Horizontal,
- Vertical
- }
- [SerializeField]
- private RectTransform m_Cell;
- [SerializeField]
- private Vector2 m_CellGap;
- [SerializeField, Range(1, 50)]
- private int m_row = 5;
- [SerializeField, Range(1, 50)]
- private int m_column = 5;
- [SerializeField]
- private Direction direction = Direction.Horizontal;
- [SerializeField]
- private bool autoCollider = true;
- private Vector2 cellSize { get { return m_Cell.sizeDelta + m_CellGap; } }
- private float lineGap { get { return direction == Direction.Horizontal ? cellSize.x : cellSize.y; } }
- private float startPos { get { return direction == Direction.Horizontal ? -cacheRect.anchoredPosition.x : cacheRect.anchoredPosition.y; } }
- private int pageCapacity { get { return m_row * m_column; } }
- private int pageLine { get { return direction == Direction.Horizontal ? m_column : m_row; } }
- private int lineCapacity { get { return direction == Direction.Horizontal ? m_row : m_column; } }
- private int maxLineIndex
- {
- get
- {
- return Mathf.Max(0, (ItemsCount - 1) / lineCapacity);
- }
- }
- private int currentLineStartIndex
- {
- get
- {
- return Mathf.Max(0, Mathf.FloorToInt(startPos / lineGap));
- }
- }
- private int currentLineEndIndex
- {
- get
- {
- return Mathf.Min(currentLineStartIndex + pageLine, maxLineIndex);
- }
- }
- private int currentItemStartIndex
- {
- get
- {
- return currentLineStartIndex * lineCapacity;
- }
- }
- private int currentItemEndIndex
- {
- get
- {
- return Mathf.Min((currentLineEndIndex + 1) * lineCapacity - 1, ItemsCount - 1);
- }
- }
- private int m_ItemsCount = 0;
- public int ItemsCount
- {
- get
- {
- return m_ItemsCount;
- }
- set
- {
- m_ItemsCount = value;
- UpdateAll();
- }
- }
- private int m_lastLineStartIndex = -1;
- //创建每个Item后的回调
- public UILoopCallBack onCreate;
- //更新每个Item后的回调
- public UILoopCallBack onUpdate;
- private List<RectTransform> m_poolItems = new List<RectTransform>();
- private List<RectTransform> m_showItems = new List<RectTransform>();
- private int m_showIndexStart = 0;
- private int m_createIndex = 0;
- private static readonly Vector2 InvalidPos = new Vector2(99999f, 99999f);
- private RectTransform mRect;
- private RectTransform cacheRect
- {
- get
- {
- if (mRect == null)
- {
- mRect = GetComponent<RectTransform>();
- mRect.anchoredPosition = Vector2.zero;
- mRect.anchorMax = Vector2.up;
- mRect.anchorMin = Vector2.up;
- mRect.pivot = Vector2.up;
- }
- return mRect;
- }
- }
- private List<RectTransform> m_delayShowItems = new List<RectTransform>();
- private ScrollRect m_scrollRect;
- private ScrollRect scrollRect
- {
- get
- {
- if (m_scrollRect == null)
- m_scrollRect = this.GetComponentInParent<ScrollRect>();
- return m_scrollRect;
- }
- }
- void Awake()
- {
- if (m_Cell.gameObject.activeSelf)
- m_Cell.gameObject.SetActive(false);
- if (autoCollider)
- {
- Image img = this.GetComponent<Image>();
- if (img == null)
- {
- img = this.gameObject.AddComponent<Image>();
- img.color = new Color(1f, 1f, 1f, 0f);
- }
- }
- ResetBound();
- ScrollToGlobalIndex(0);
- }
- private RectTransform CreateItem()
- {
- RectTransform item = GameObject.Instantiate(m_Cell);
- item.SetParent(cacheRect, false);
- item.anchorMax = Vector2.up;
- item.anchorMin = Vector2.up;
- item.pivot = new Vector2(0.5f, 0.5f);
- item.anchoredPosition3D = Vector3.zero;
- //item.gameObject.SetActive(true);
- m_delayShowItems.Add(item);
- //回调
- if (onCreate != null)
- onCreate(m_createIndex, item.gameObject);
- m_createIndex++;
- return item;
- }
- private RectTransform GetItemFromPool()
- {
- if (m_poolItems.Count > 0)
- {
- RectTransform rt = m_poolItems[m_poolItems.Count - 1];
- m_poolItems.RemoveAt(m_poolItems.Count - 1);
- return rt;
- }
- return CreateItem();
- }
- void Update()
- {
- //Debug.LogWarning(maxLineIndex + " " + currentLineStartIndex + " " + currentLineEndIndex + " " + currentItemStartIndex + " " + currentItemEndIndex);
- if (m_delayShowItems.Count > 0)
- {
- m_delayShowItems[0].gameObject.SetActive(true);
- m_delayShowItems.RemoveAt(0);
- }
- if (m_lastLineStartIndex != currentLineStartIndex)
- {
- m_lastLineStartIndex = currentLineStartIndex;
- int itemStartIndex = currentItemStartIndex;
- int itemEndIndex = currentItemEndIndex;
- int count = itemEndIndex - itemStartIndex + 1;
- if (count > 0)
- {
- int oldShowStartIndex = m_showIndexStart;
- int oldShowEndIndex = m_showIndexStart + m_showItems.Count - 1;
- for (int i = oldShowEndIndex; i >= m_showIndexStart; i--)
- {
- if (i >= itemStartIndex && i <= itemEndIndex)
- continue;
- int localIndex = i - m_showIndexStart;
- RectTransform rt = m_showItems[localIndex];
- m_showItems.RemoveAt(localIndex);
- m_poolItems.Add(rt);
- rt.anchoredPosition = InvalidPos;
- if (i > itemEndIndex)
- oldShowEndIndex--;
- else
- oldShowStartIndex++;
- }
- m_showIndexStart = itemStartIndex;
- for (int i = itemStartIndex; i <= itemEndIndex; i++)
- {
- int localIndex = i - itemStartIndex;
- if (i >= oldShowStartIndex && i <= oldShowEndIndex)
- {
- RectTransform rt = m_showItems[localIndex];
- rt.anchoredPosition = GetPosByGlobalIndex(i);
- }
- else
- {
- RectTransform rt = GetItemFromPool();
- rt.anchoredPosition = GetPosByGlobalIndex(i);
- if (i < oldShowStartIndex)
- {
- m_showItems.Insert(localIndex, rt);
- }
- else
- {
- m_showItems.Add(rt);
- }
- if (onUpdate != null)
- onUpdate(i, rt.gameObject);
- }
- // 测试编号显示
- //Text text = m_showItems[localIndex].GetComponentInChildren<Text>();
- //if (text)
- // text.text = i.ToString();
- }
- }
- }
- }
- private Vector2 GetPosByGlobalIndex(int globalIndex)
- {
- if (globalIndex < 0)
- globalIndex = 0;
- Vector2 nativeSize = m_Cell.sizeDelta;
- float x;
- float y;
- if (direction == Direction.Horizontal)
- {
- x = globalIndex / m_row;
- y = globalIndex % m_row;
- }
- else
- {
- x = globalIndex % m_column;
- y = globalIndex / m_column;
- }
- x = x * cellSize.x + nativeSize.x * 0.5f;
- y = -(y * cellSize.y + nativeSize.y * 0.5f);
- return new Vector2(x, y);
- }
- private void ResetBound()
- {
- int row = m_row;
- int column = m_column;
- if (direction == Direction.Horizontal)
- {
- column = m_ItemsCount / m_row;
- if (m_ItemsCount % m_row != 0)
- column++;
- // perfect line count for drag bound
- column = Mathf.Max(column, m_column - 1);
- }
- else
- {
- row = m_ItemsCount / m_column;
- if (m_ItemsCount % m_column != 0)
- row++;
- // perfect line count for drag bound
- row = Mathf.Max(row, m_row - 1);
- }
- cacheRect.sizeDelta = new Vector2(column * cellSize.x - m_CellGap.x, row * cellSize.y - m_CellGap.y);
- }
- //-------------------------------------对外接口
- //滚动到某个Item位置
- public void ScrollToGlobalIndex(int globalIndex)
- {
- Vector2 pos = GetPosByGlobalIndex(globalIndex);
- Vector2 curPos = cacheRect.anchoredPosition;
- Vector2 nativeSize = m_Cell.sizeDelta;
- // pivot offset
- pos.x -= 0.5f * nativeSize.x;
- pos.y += 0.5f * nativeSize.y;
- if (direction == Direction.Horizontal)
- {
- curPos.x = -pos.x;
- if (scrollRect)
- {
- float maxScroll = cacheRect.sizeDelta.x - (scrollRect.transform as RectTransform).sizeDelta.x;
- if (maxScroll > 0 && curPos.x < -maxScroll)
- curPos.x = -maxScroll;
- else if (maxScroll < 0)
- curPos.x = 0;
- }
- }
- else
- {
- curPos.y = -pos.y;
- if (scrollRect)
- {
- float maxScroll = cacheRect.sizeDelta.y - (scrollRect.transform as RectTransform).sizeDelta.y;
- if (maxScroll > 0 && curPos.y > maxScroll)
- curPos.y = maxScroll;
- else if (maxScroll < 0)
- curPos.y = 0;
- }
- }
- // 为了修正ScrollRect的Bound计算问题
- if (curPos.x == 0)
- curPos.x = 0.01f;
- if (curPos.y == 0)
- curPos.y = -0.01f;
- cacheRect.anchoredPosition = curPos;
- }
- public int GetItemGlobalIndex(GameObject go)
- {
- for (int i = m_showItems.Count - 1; i >= 0; i--)
- {
- if (m_showItems[i].gameObject == go)
- return currentItemStartIndex + i;
- }
- return -1;
- }
- public GameObject GetItemByLocalIndex(int localIndex)
- {
- if (localIndex >= 0 && localIndex < m_showItems.Count)
- {
- return m_showItems[localIndex].gameObject;
- }
- return null;
- }
- public GameObject GetItemByGlobalIndex(int globalIndex)
- {
- int localIndex = globalIndex - currentItemStartIndex;
- if (localIndex >= 0 && localIndex < m_showItems.Count)
- return m_showItems[localIndex].gameObject;
- return null;
- }
- public void UpdateAll()
- {
- m_lastLineStartIndex = -1;
- if (m_showItems.Count > 0)
- {
- for (int i = m_showItems.Count - 1; i >= 0; i--)
- {
- m_showItems[i].anchoredPosition = InvalidPos;
- m_poolItems.Add(m_showItems[i]);
- }
- m_showItems.Clear();
- }
- ResetBound();
- // 确保物件创建出来
- Update();
- }
- }
- #if UNITY_EDITOR
- [UnityEditor.CustomEditor(typeof(UILoop), true)]
- public class UILoopEditor : UnityEditor.Editor
- {
- int itemCount;
- public override void OnInspectorGUI()
- {
- base.OnInspectorGUI();
- var comp = target as UILoop;
- UnityEditor.EditorGUILayout.HelpBox("以下只有编辑器有效", UnityEditor.MessageType.Info);
- UnityEditor.EditorGUILayout.LabelField("Item Count = " + comp.ItemsCount);
- GUILayout.BeginHorizontal();
- itemCount = UnityEditor.EditorGUILayout.IntField("New Count = ", itemCount);
- if (GUILayout.Button("Reset Count"))
- {
- comp.ItemsCount = itemCount;
- }
- GUILayout.EndHorizontal();
- }
- }
- #endif
|