高性能的Unity3DScrollView组件,仅适用部分特定的滑块需求

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using XLua;

///


/// 高性能ScrollView
///

[RequireComponent(typeof(ScrollRect))]
public class GridScroller : MonoBehaviour
{
    public enum Movement
    {
        Horizontal,
        Vertical
    }
    public delegate void MoveToEndCallback();
    public delegate void OnScrollViewItemChanged(Transform trans, int itemIdx);

    private List _cacheList = new List();
    private Vector2 _cellSize = Vector3.zero;
    private int _col;
    private HashSet _currentItemIdxSet = new HashSet();
    private Dictionary _currentVisibleItemIdxRTMap = new Dictionary();
    private RectTransform _grid;
    private bool _hasChanged;
    private Stack _itemPool = new Stack();
    private GameObject _itemPrefab;
    private Movement _moveType;
    private HashSet _nextVisibleItemSet = new HashSet();
    private OnScrollViewItemChanged _onNewItemBecomeVisibleCallback;
    private int _row;
    private ScrollRect _scroller;
    private Rect _scrollerRect;
    private Vector2 _spacing = Vector3.zero;
    private int _totalItemCount;
    private int _visibleItemCount;
    private float cachedPos;
    public MoveToEndCallback moveToEndCallBack;
    public bool moveToEndLock;

    public Vector2 ItemSize
    {
        get
        {
            return _spacing + _cellSize;
        }
    }
    public Movement MoveType
    {
        get
        {
            return _moveType;
        }
        set
        {
            _moveType = value;
        }
    }

    public RectTransform Grid
    {
        set
        {
            _grid = value;
        }
    }

    public GameObject ItemPrefab
    {
        set
        {
            _itemPrefab = value;
        }
    }

    private Vector2 CalcPosByIndex(int index)
    {
        if (_moveType == Movement.Horizontal)
        {
            return new Vector2(ItemSize.x * (index / _row), -ItemSize.y * (index % _row));
        }
        return new Vector2(ItemSize.x * (index % _col), -ItemSize.y * (index / _col));
    }

    private void CheckMoveToEnd()
    {
        if ((moveToEndCallBack != null) && !moveToEndLock)
        {
            bool flag = false;
            if (_moveType == Movement.Horizontal)
            {
                if (_grid.rect.width - _scrollerRect.width > 0 && Math.Abs(_grid.anchoredPosition.x) + _scrollerRect.width > _grid.rect.width)
                {
                    flag = true;
                }
            }
            else if (_grid.rect.height - _scrollerRect.height > 0 && Math.Abs(_grid.anchoredPosition.y) + _scrollerRect.height > _grid.rect.height)
            {
                flag = true;
            }
            if (flag)
            {
                moveToEndCallBack();
            }
        }
    }

    public void Clear()
    {
        var enumerator = _currentVisibleItemIdxRTMap.GetEnumerator();
        while (enumerator.MoveNext())
        {
            KeyValuePair current = enumerator.Current;
            current.Value.gameObject.SetActive(false);
            KeyValuePair pair2 = enumerator.Current;
            _cacheList.Add(pair2.Value);
        }
        Stack.Enumerator enumerator2 = _itemPool.GetEnumerator();
        while (enumerator2.MoveNext())
        {
            enumerator2.Current.gameObject.SetActive(false);
            _cacheList.Add(enumerator2.Current);
        }
        _itemPool.Clear();
        _totalItemCount = 0;
        _currentItemIdxSet.Clear();
        _currentVisibleItemIdxRTMap.Clear();
        _nextVisibleItemSet.Clear();
        _scroller.onValueChanged.RemoveAllListeners();
    }

    public void DisplayItemFromStartIdx(int index)
    {
        if (index < _totalItemCount)
        {
            Vector2 vector = CalcPosByIndex(index);
            Vector2 vector2 = (_moveType == Movement.Horizontal) ? new Vector2(-vector.x, 0f) : new Vector2(0f, -vector.y);
            _grid.anchoredPosition = vector2;
            OnScrollValueChanged(Vector2.zero);
        }
    }

    private Transform GetItem()
    {
        Transform item = null;
        if (_cacheList.Count > 0)
        {
            item = _cacheList[0];
            _cacheList.Remove(item);
        }
        if ((item == null) || (item.gameObject == null))
        {
            item = AddChild(_grid.gameObject, _itemPrefab).transform;
        }
        item.gameObject.SetActive(true);
        return item;
    }

    public void Init(OnScrollViewItemChanged onChange, int itemCount, Vector2 normalPosition = new Vector2())
    {
        InitScroller();
        InitCustomGrid();
        UpdateCustomGridSizeByItemCount(itemCount, false, normalPosition);
        InitChildren(onChange);
    }

    private void InitChild(RectTransform rt, int idx)
    {
        rt.anchorMax = new Vector2(0f, 1f);
        rt.anchorMin = new Vector2(0f, 1f);
        rt.pivot = new Vector2(0f, 1f);
        rt.sizeDelta = _cellSize;
        rt.anchoredPosition = CalcPosByIndex(idx);
    }

    private void InitChildren(OnScrollViewItemChanged onChange)
    {
        _onNewItemBecomeVisibleCallback = onChange;
        _visibleItemCount = _col * _row;
        if (_visibleItemCount > _totalItemCount)
        {
            _visibleItemCount = _totalItemCount;
        }
        for (int i = 0; i != _visibleItemCount; i++)
        {
            Transform item = GetItem();
            InitChild(item.GetComponent(), i);
            _onNewItemBecomeVisibleCallback(item, i);
            _currentItemIdxSet.Add(i);
            _currentVisibleItemIdxRTMap.Add(i, item.GetComponent());
        }
    }

    private void InitCustomGrid()
    {
        _cellSize = _grid.GetComponent().cellSize;
        _spacing = _grid.GetComponent().spacing;
        _grid.GetComponent().enabled = false;
    }

    private void InitScroller()
    {
        _scroller = base.GetComponent();
        _scrollerRect = _scroller.GetComponent().rect;
        if (_moveType == Movement.Horizontal)
        {
            _scroller.vertical = false;
            _scroller.horizontal = true;
        }
        else
        {
            _scroller.vertical = true;
            _scroller.horizontal = false;
        }
    }

    public void OnScrollValueChanged(Vector2 normalizedPosition)
    {
        CheckMoveToEnd();
        if (_visibleItemCount != _totalItemCount)
        {
            int firstVisibleIdx = 0;
            if (_moveType == Movement.Horizontal)
            {
                float num2 = -_grid.anchoredPosition.x;
                int num3 = (int) (num2 / ItemSize.x);
                firstVisibleIdx = num3 * _row;
            }
            else
            {
                int num5 = (int) (_grid.anchoredPosition.y / ItemSize.y);
                firstVisibleIdx = num5 * _col;
            }
            UpdateVisibleItemListByNewStartIdx(firstVisibleIdx);
        }
    }

    public void RefreshCurrent()
    {
        foreach (int num in _currentItemIdxSet)
        {
            if ((_onNewItemBecomeVisibleCallback != null) && _currentVisibleItemIdxRTMap.ContainsKey(num))
            {
                _onNewItemBecomeVisibleCallback(_currentVisibleItemIdxRTMap[num], num);
            }
        }
    }

    public void Reset()
    {
        if (_moveType == Movement.Horizontal)
        {
            SetX(_grid,0f);
        }
        else
        {
            SetY(_grid,0f);
        }
        UpdateVisibleItemListByNewStartIdx(0);
    }

    public void ResetSpacing()
    {
        _cellSize = _grid.GetComponent().cellSize;
        _spacing = _grid.GetComponent().spacing;
    }

    private void SpawnItemFromPoolByIdx(int spawnItemIdx)
    {
        if (_itemPool.Count > 0)
        {
            RectTransform transform = _itemPool.Pop();
            transform.anchoredPosition = CalcPosByIndex(spawnItemIdx);
            _currentVisibleItemIdxRTMap.Add(spawnItemIdx, transform);
            if (_onNewItemBecomeVisibleCallback != null)
            {
                _onNewItemBecomeVisibleCallback(transform, spawnItemIdx);
            }
        }
    }

    public void StopScrollMove()
    {
        if (null != _scroller)
        {
            _scroller.StopMovement();
        }
    }

    private void UpdateCustomGridSizeByItemCount(int itemCount,bool moveToEnd,Vector2 normalizedPosition)
    {
        _totalItemCount = itemCount;
        _col = Mathf.RoundToInt((_scrollerRect.width + _spacing.x) / ItemSize.x);
        _row = Mathf.RoundToInt((_scrollerRect.height + _spacing.y) / ItemSize.y);
        if (_moveType == Movement.Horizontal)
        {
            _col += 2;
        }
        else
        {
            _row += 2;
        }
        if (_moveType == Movement.Horizontal)
        {
            _grid.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, Mathf.CeilToInt(((float) _totalItemCount) / ((float) _row)) * ItemSize.x);
            if(moveToEnd)
            {
                Vector2 end = CalcPosByIndex(itemCount - 1);
                SetX(_grid, -end.x);
            }
            else
            {
                SetX(_grid, 0f);
            }           
        }
        else
        {
            _grid.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, Mathf.CeilToInt(((float) _totalItemCount) / ((float) _col)) * ItemSize.y);
            if(moveToEnd)
            {
                Vector2 end = CalcPosByIndex(itemCount - 1);
                SetY(_grid, -end.y);
            }
            else
            {
                SetY(_grid, 0f);
            }
            
        }
        _scroller.onValueChanged.AddListener(new UnityAction(OnScrollValueChanged));
    }

    public void UpdateGridSize()
    {
        UpdateCustomGridSizeByItemCount(_totalItemCount, false, new Vector2());
    }

    public void UpdateItemCount(int count, bool savePos = false, bool forceRefresh = false)
    {
        if (!forceRefresh && (_totalItemCount == count))
        {
            RefreshCurrent();
        }
        else
        {
            if (savePos)
            {
                if (_moveType == Movement.Horizontal)
                {
                    cachedPos = _grid.anchoredPosition.x;
                }
                else
                {
                    cachedPos = _grid.anchoredPosition.y;
                }
            }
            Clear();
            UpdateCustomGridSizeByItemCount(count, false, new Vector2());
            InitChildren(_onNewItemBecomeVisibleCallback);
            if (savePos)
            {
                if (_moveType == Movement.Horizontal)
                {
                    if (cachedPos < 0f)
                    {
                        cachedPos = 0f;
                    }
                    if (-cachedPos > (_grid.rect.width - _scrollerRect.width))
                    {
                        cachedPos = -(_grid.rect.width - _scrollerRect.width);
                    }
                    SetX(_grid,cachedPos);
                }
                else
                {
                    if (cachedPos < 0f)
                    {
                        cachedPos = 0f;
                    }
                    if (cachedPos > (_grid.rect.height - _scrollerRect.height))
                    {
                        cachedPos = Mathf.Max((float) (_grid.rect.height - _scrollerRect.height), (float) 0f);
                    }
                    SetY(_grid,cachedPos);
                }
                if (forceRefresh)
                {
                    OnScrollValueChanged(new Vector2());
                }
            }
        }
    }

    public void AddNewItem()
    {
        int count = _totalItemCount + 1;

        if (_moveType == Movement.Horizontal)
        {
            cachedPos = _grid.anchoredPosition.x;
        }
        else
        {
            cachedPos = _grid.anchoredPosition.y;
        }

        Clear();
        UpdateCustomGridSizeByItemCount(count,true, new Vector2());
        InitChildren(_onNewItemBecomeVisibleCallback);
        //Transform item = _itemPool.Pop();
        //InitChild(item.GetComponent(), count-1);
        //_currentItemIdxSet.Add(count - 1);
        //_currentVisibleItemIdxRTMap.Add(count - 1, item.GetComponent());
        //_onNewItemBecomeVisibleCallback(item, count - 1);

        if (_moveType == Movement.Horizontal)
        {
            if (cachedPos < 0f)
            {
                cachedPos = 0f;
            }
            if (-cachedPos > (_grid.rect.width - _scrollerRect.width))
            {
                cachedPos = -(_grid.rect.width - _scrollerRect.width);
            }
            SetX(_grid, cachedPos + ItemSize.x + _spacing.x);
        }
        else
        {
            if (cachedPos < 0f)
            {
                cachedPos = 0f;
            }
            if (cachedPos > (_grid.rect.height - _scrollerRect.height))
            {
                cachedPos = Mathf.Max((float)(_grid.rect.height - _scrollerRect.height), (float)0f);
            }
            SetY(_grid, cachedPos + ItemSize.y);
        }
    }

    private void UpdateVisibleItemListByNewStartIdx(int firstVisibleIdx)
    {
        _nextVisibleItemSet.Clear();
        for (int i = 0; i != _visibleItemCount; i++)
        {
            if (((i + firstVisibleIdx) < _totalItemCount) && ((i + firstVisibleIdx) >= 0))
            {
                _nextVisibleItemSet.Add(i + firstVisibleIdx);
            }
        }
        if (!_nextVisibleItemSet.SetEquals(_currentItemIdxSet))
        {
            IEnumerator minusSet = GetMinusSet(_nextVisibleItemSet, _currentItemIdxSet);
            IEnumerator enumerator2 = GetMinusSet(_currentItemIdxSet, _nextVisibleItemSet);
            while (enumerator2.MoveNext())
            {
                _itemPool.Push(_currentVisibleItemIdxRTMap[enumerator2.Current]);
                _currentVisibleItemIdxRTMap.Remove(enumerator2.Current);
            }
            while (minusSet.MoveNext())
            {
                SpawnItemFromPoolByIdx(minusSet.Current);
            }
            HashSet newSet = _currentItemIdxSet;
            _currentItemIdxSet = _nextVisibleItemSet;
            _nextVisibleItemSet = newSet;
        }
    }

    private void SetX(RectTransform rt, float x)
    {
        Vector2 anchoredPosition = rt.anchoredPosition;
        anchoredPosition.x = x;
        rt.anchoredPosition = anchoredPosition;
    }
    
    private void SetY(RectTransform rt, float y)
    {
        Vector2 anchoredPosition = rt.anchoredPosition;
        anchoredPosition.y = y;
        rt.anchoredPosition = anchoredPosition;
    }

    private GameObject AddChild(GameObject parent, GameObject child)
    {
        GameObject go = UnityEngine.Object.Instantiate(child);
        go.SetActive(true);
        if ((go != null) && (parent != null))
        {
            Transform transform = go.transform;
            transform.SetParent(parent.transform);
            transform.localPosition = Vector3.zero;
            transform.localRotation = Quaternion.identity;
            transform.localScale = Vector3.one;
            go.layer = parent.layer;
        }
        return go;
    }

    private IEnumerator GetMinusSet(HashSet target, HashSet except)
    {
        List list = new List();
        HashSet.Enumerator enumerator = target.GetEnumerator();
        while (enumerator.MoveNext())
        {
            if (!except.Contains(enumerator.Current))
            {
                list.Add(enumerator.Current);
            }
        }
        return list.GetEnumerator();
    }
}


#if UNITY_EDITOR
public static class GridScrollerExporter
{
    [LuaCallCSharp]
    public static List LuaCallCSharp = new List()
    {
        typeof(ScrollRect),
        typeof(GridScroller),
    };

    [CSharpCallLua]
    public static List CSharpCallLuaList = new List()
    {
         typeof(GridScroller.OnScrollViewItemChanged),
         typeof(GridScroller.MoveToEndCallback),
    };
}
#endif

你可能感兴趣的:(unity3d)