using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class CollectionView : ScrollRect { public Vector2 sizeOfCells; public float spaceOfH; public float spaceOfV; public delegate CollectionViewCell CellOfIndex(CollectionView view, int index); public delegate int NumberOfCells(CollectionView view); public CellOfIndex delegateCellOfIndex = null; public NumberOfCells delegateNumberOfCells = null; private int countOfCells; private int _curMin = -1; private int _curMax = -1; /// <summary> /// 水平个数 /// </summary> private int numberOfH; /// <summary> /// 竖直个数 /// </summary> private int numberOfV; private Vector2 sizeOfContent; private GameObject _prefab; /// <summary> /// 已显示的Cell /// </summary> private Dictionary<int, CollectionViewCell> dict = new Dictionary<int, CollectionViewCell>(); /// <summary> /// Cell复用缓存 /// </summary> private Queue<GameObject> _poolQueue = new Queue<GameObject>(); protected override void Start() { base.Start(); this.onValueChanged.AddListener(OnMove); } /// <summary> /// 注册预设 /// </summary> /// <param name="prefab"></param> public void register(GameObject prefab) { _prefab = prefab; } public void register(string assetBundleName, AssetBundleType type, string assetName) { _prefab = AssetBundleTool.instance.LoadResource<GameObject>(assetBundleName, type, assetName); } /// <summary> /// /// </summary> /// <param name="identify"></param> /// <returns></returns> public T ReuseDequeue<T>(string identify = "") where T : CollectionViewCell { GameObject obj; if (_poolQueue.Count > 0) { obj = _poolQueue.Dequeue(); obj.SetActive(true); } else obj = Instantiate(_prefab); obj.transform.SetParent(this.content); obj.transform.localScale = Vector3.one; T cell = obj.GetComponent<T>(); if(cell == null) cell = obj.AddComponent<T>(); return cell; } public T CellAtIndex<T>(int index) where T : CollectionViewCell { T cell = null; if (dict.ContainsKey(index)) { cell = dict[index] as T; } return cell; } /// <summary> /// /// </summary> public void reload(bool top = true) { Vector2 pos = top ? Vector2.zero : this.content.anchoredPosition; if (delegateNumberOfCells != null) countOfCells = delegateNumberOfCells(this); float width = 0; float height = 0; if(vertical) { width = Mathf.Max(this.content.sizeDelta.x, 0f); numberOfH = Mathf.FloorToInt(width / sizeOfCells.x); numberOfV = Mathf.CeilToInt((float)countOfCells / (float)numberOfH); height = numberOfV * sizeOfCells.y + (numberOfV - 1) * spaceOfV; } else { height = Mathf.Max(this.content.sizeDelta.y, 0f); numberOfV = Mathf.FloorToInt(height / sizeOfCells.y); numberOfH = Mathf.CeilToInt((float)countOfCells / (float)numberOfV); width = numberOfH * sizeOfCells.x + (numberOfH - 1) * spaceOfH; } sizeOfContent = new Vector2(width, height); this.content.sizeDelta = sizeOfContent; this.content.anchoredPosition = pos; int min, max; Vector2 anchoredPos = indexOfBounder(this.content.anchoredPosition, out min, out max); if(!(_curMin == -1 && _curMax == -1)) { for (int i = _curMin; i <= _curMax; i++) { removeCell(i); } } if(countOfCells != 0) { for (int i = min; i <= max; i++) { addCell(i); } } _curMin = min; _curMax = max; } protected void OnMove(Vector2 value) { if (horizontal && (value.x < 0 || value.x > 1)) return; if (vertical && (value.y < 0 || value.y > 1)) return; if (countOfCells == 0 || dict.Count == 0) return; int min; int max; Vector2 anchoredPos = indexOfBounder(this.content.anchoredPosition, out min, out max); //Debug.LogFormat("{0}-{1} {2}-{3}", _curMin, _curMax, min, max); updateContent(min, max); } private Vector2 indexOfBounder(Vector2 anchoredPosition, out int min, out int max) { Vector2 vector = anchoredPosition; vector = new Vector2(-vector.x, vector.y); vector = new Vector2(Mathf.Min(vector.x, sizeOfContent.x), Mathf.Min(vector.y, sizeOfContent.y)); Vector2 anchoredPos = new Vector2(Mathf.Max(vector.x, 0), Mathf.Max(vector.y, 0)); if (vertical) { min = Mathf.FloorToInt(anchoredPos.y / (sizeOfCells.y + spaceOfV)); max = Mathf.CeilToInt((anchoredPos.y + this.viewport.rect.height) / (sizeOfCells.y + spaceOfV)); min = min * numberOfH; max = Mathf.Min(max * numberOfH - 1, countOfCells - 1); } else { min = Mathf.FloorToInt(anchoredPos.x / (sizeOfCells.x + spaceOfH)); max = Mathf.CeilToInt((anchoredPos.x + this.viewport.rect.width) / (sizeOfCells.x + spaceOfH)); min = min * numberOfV; max = Mathf.Min(max * numberOfV - 1, countOfCells - 1); } return anchoredPos; } private void addCell(int index) { CollectionViewCell cell = delegateCellOfIndex(this, index); cell.show(this, index); RectTransform t = cell.transform as RectTransform; float x = 0; float y = 0; int indexX; int indexY; if (vertical) { indexX = index % numberOfH; indexY = index / numberOfH; } else { indexX = index / numberOfV; indexY = index % numberOfV; } x = indexX * sizeOfCells.x + spaceOfH * indexX; y = -indexY * sizeOfCells.y - spaceOfV * indexY; t.anchoredPosition3D = new Vector3(x, y, 0f); dict[index] = cell; } private void removeCell(int index) { CollectionViewCell cell = dict[index]; cell.hide(this, index); cell.gameObject.SetActive(false); _poolQueue.Enqueue(cell.gameObject); } private void updateContent(int min, int max) { if (_curMin == min && _curMax == max) return; if (_curMin < min) { //remove for (int i = _curMin; i < min; i++) { removeCell(i); } } if (max < _curMax) { //remove for (int i = max + 1; i <= _curMax; i++) { removeCell(i); } } if (min < _curMin) { //add for (int i = min; i < _curMin; i++) { addCell(i); } } if (_curMax < max) { //add for (int i = _curMax + 1; i <= max; i++) { addCell(i); } } _curMin = min; _curMax = max; } }