ScrollRect重写 循环利用

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;
    }
}
上一篇:创建和使用 ScriptableObject


下一篇:100-days: twenty-one