【Unity】使用缓存池实现无限滚动视图

1. 使用缓存池的好处

  1. 不用频繁创建UI,重用不在视口内的UI(已经创建好的)
  2. 看下图,UI已经到了两万多个,可是创建的预制体也就20几个在这里插入图片描述
    直接看代码吧,希望对您的工作学习有所帮助~

2. CustomInfiniteScrollView.cs

using System.Collections.Generic;
using UnityEngine;

namespace DF.Demo
{
    public interface IItemBase<T>
    {
        void InitItem(T info);
    }

    public enum EScrollDirection
    {
        Horizontal, // 水平滑动
        Vertical, // 竖直滑动
    }

    public class CustomInfiniteScrollView<T, K> where K : MonoBehaviour, IItemBase<T>
    {
        // -- 1
        // 履带
        private RectTransform content;
        // 可视范围高度
        private Vector2 viewPortSize;
        // 行列数
        private Vector2Int rowColumn;
        // 横纵间距
        private Vector2Int padding;
        // 滑动方向
        private EScrollDirection direction;
        float rowOrColumn = 0; // 行数或列数

        // -- 2
        // 格子尺寸
        private Vector2 itemSize;
        // 格子资源路径
        private string itemResPath;
        private BagItem bagItem;

        // -- 3
        // 数据来源
        private List<T> items;

        // -- 4
        // 当前显示的格子对象
        public Dictionary<int, GameObject> nowShowItems = new Dictionary<int, GameObject>();
        // 缓存池, 存储不用的格子对象
        //public Stack<GameObject> itemGameObjStack = new Stack<GameObject>();
        // 上一次索引
        private int oldMinIndex;
        private int oldMaxIndex;

        public void InitScrollView(/*格子父节点*/RectTransform con,
            /*视口尺寸*/Vector2 viewPortSize,
            /*行列数*/Vector2Int rowColumn,
            /*格子横纵间距*/ Vector2Int padding,
            /*拖动方向*/EScrollDirection direction = EScrollDirection.Vertical)
        {
            content = con;
            this.viewPortSize = viewPortSize;
            this.rowColumn = rowColumn;
            this.padding = padding;
            this.direction = direction;
            switch (direction)
            {
                case EScrollDirection.Horizontal:
                    this.rowOrColumn = rowColumn.x;
                    break;
                case EScrollDirection.Vertical:
                    this.rowOrColumn = rowColumn.y;
                    break;
                default:
                    break;
            }
        }

        public void InitItemInfo(Vector2 itemSize, string itemResP, BagItem bagI)
        {
            this.itemSize = itemSize;
            itemResPath = itemResP;
            bagItem = bagI;
        }

        public void InitItemData(List<T> items)
        {
            this.items = items;
            content.sizeDelta = new Vector2(0, Mathf.CeilToInt(items.Count / rowOrColumn) * (itemSize.y + padding.y));
        }

        public void CheckShowOrHideItemAsync()
        {
            int minIndex = 0;
            int maxIndex = 0;
            switch (direction)
            {
                case EScrollDirection.Horizontal:
                    minIndex = (int)(content.anchoredPosition.x / (itemSize.x + padding.x) * rowColumn.x);
                    maxIndex = (int)((content.anchoredPosition.x + viewPortSize.x) / (itemSize.x + padding.x) * rowColumn.x + rowColumn.x - 1);
                    break;
                case EScrollDirection.Vertical:

                    minIndex = (int)(content.anchoredPosition.y / (itemSize.y + padding.y) * rowColumn.y);
                    maxIndex = Mathf.CeilToInt((content.anchoredPosition.y + viewPortSize.y) / (itemSize.y + padding.y)) * rowColumn.y + rowColumn.y - 1;
                    break;
                default:
                    break;
            }
            if (minIndex < 0)
                minIndex = 0;
            if (maxIndex >= items.Count)
                maxIndex = items.Count - 1;

            if (minIndex != oldMinIndex || maxIndex != oldMaxIndex)
            {
                // 删除视口上部溢出格子
                for (int i = oldMinIndex; i < minIndex; ++i)
                {
                    if (nowShowItems.ContainsKey(i))
                    {
                        if (nowShowItems[i] != null)
                        {
                            //itemGameObjStack.Push(nowShowItems[i]);
                            UICommonItemPool<K>.Instance.Push(nowShowItems[i].GetComponent<K>());
                        }
                        nowShowItems.Remove(i);
                    }
                }

                // 删除视口下部溢出格子
                for (int i = maxIndex + 1; i <= oldMaxIndex; ++i)
                {
                    if (nowShowItems.ContainsKey(i))
                    {
                        if (nowShowItems[i] != null)
                        {
                            //itemGameObjStack.Push(nowShowItems[i]);
                            UICommonItemPool<K>.Instance.Push(nowShowItems[i].GetComponent<K>());
                        }
                        nowShowItems.Remove(i);
                    }
                }
            }

            oldMinIndex = minIndex;
            oldMaxIndex = maxIndex;

            // 创建格子
            for (int i = minIndex; i <= maxIndex; ++i)
            {
                if (nowShowItems.ContainsKey(i))
                {
                    continue;
                }
                else
                {
                    int index = i;
                    nowShowItems.Add(index, null);
                    GameObject obj = null;
                    //if (itemGameObjStack.Count != 0)
                    //{
                    //    //obj = itemGameObjStack.Pop();
                    //}
                    //else
                    //{
                    //    // 这块可以改写使用异步方法加载资源
                    //    obj = GameObject.Instantiate(bagItem.gameObject);
                    //}
                    obj = UICommonItemPool<K>.Instance.Get().gameObject;
                    if (obj != null)
                    {

                        obj.transform.SetParent(content);
                        obj.transform.localScale = Vector3.one;
                        // 下标 取余 列数 = 第几列
                        // 下标 除以 列数 = 第几行
                        obj.transform.localPosition = new Vector3((index % rowOrColumn) * (itemSize.x + padding.x), -Mathf.FloorToInt(index / rowOrColumn) * (itemSize.x + padding.y), 0);

                        obj.transform.GetComponent<K>().InitItem(items[index]);
                    }
                    else
                    {
                        Debug.LogError("Obj is null");
                    }
                    if (nowShowItems.ContainsKey(index))
                    {
                        nowShowItems[index] = obj;
                    }
                    else
                    {
                        //itemGameObjStack.Push(obj);
                        UICommonItemPool<K>.Instance.Push(nowShowItems[i].GetComponent<K>());
                    }
                }
            }
        }
    }
}

3. BagItem.cs

using UnityEngine;
using UnityEngine.UI;

namespace DF.Demo
{
    public class BagItem : MonoBehaviour, IItemBase<Item>
    {
        private int id;
        [SerializeField] Text text;

        public void InitItem(Item info)
        {
            id = info.id;
            text.text = info.num.ToString();
        }
    }
}

4. UICommonItemPool.cs

using System.Collections.Generic;
using UnityEngine;
namespace DF.Demo
{
    public class UICommonItemPool<T> where T : MonoBehaviour
    {
        private GameObject itemRes;
        public List<T> m_freeItems = new List<T>();
        public List<T> m_usingItems = new List<T>();

        private static UICommonItemPool<T> _instance = null;

        public static UICommonItemPool<T> Instance
        {
            get
            {
                if (_instance == null) _instance = new UICommonItemPool<T>();
                return _instance;
            }
        }

        public GameObject ItemRes { set => itemRes = value; }

        public T Get()
        {
            if (m_freeItems.Count == 0)
            {
                // 这块可以改写成异步方法创建
                var p = GameObject.Instantiate(itemRes);
                m_freeItems.Add(p.GetComponent<T>());
            }
            var item = m_freeItems[0];
            item.gameObject.SetActive(true);
            m_usingItems.Add(item);
            m_freeItems.Remove(item);
            return item;
        }

        public void Push(T item)
        {
            item.gameObject.SetActive(false);
            m_usingItems.Remove(item);
            m_freeItems.Add(item);
        }

        public void Clear()
        {
            for (int i = m_freeItems.Count - 1; i >= 0; i--)
            {
                var item = m_freeItems[i];
                GameObject.Destroy(item.gameObject);
            }
            for (int i = m_usingItems.Count - 1; i >= 0; i--)
            {
                var item = m_usingItems[i];
                GameObject.Destroy(item.gameObject);
            }
            m_freeItems.Clear();
            m_usingItems.Clear();
        }
    }
}

5. TestCustomSV

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

namespace DF.Demo
{
    public class Item
    {
        public int id;
        public int num;
    }

    public class TestCustomSV : MonoBehaviour
    {

        [SerializeField] ScrollRect scrolRect;
        [SerializeField] RectTransform content;
        [SerializeField] BagItem bagItem;
        [SerializeField] Button showCacheStackCountBtn;

        CustomInfiniteScrollView<Item, BagItem> customSV;

        private void Awake()
        {
            showCacheStackCountBtn.onClick.AddListener(ShowCacheStackCount);
        }

        private void ShowCacheStackCount()
        {
            //Debug.LogError("customSV.itemGameObjStack.Count : " + customSV.itemGameObjStack.Count);
            //Debug.LogError("customSV.nowShowItems.Count : " + customSV.nowShowItems.Count);
            Debug.LogError("UICommonItemPool<BagItem>.Instance.m_freeItems.Count  : " + UICommonItemPool<BagItem>.Instance.m_freeItems.Count);
            Debug.LogError("UICommonItemPool<BagItem>.Instance.m_usingItems.Count : " + UICommonItemPool<BagItem>.Instance.m_usingItems.Count);
            Debug.LogError("--------------------------------------------------");
        }

        void Start()
        {
            UICommonItemPool<BagItem>.Instance.ItemRes = bagItem.gameObject;

            customSV = new CustomInfiniteScrollView<Item, BagItem>();
            var size = (bagItem.transform as RectTransform).sizeDelta;

            customSV.InitScrollView(content, new Vector2(0, 1140), new Vector2Int(0, 3), new Vector2Int(60, 10));

            customSV.InitItemInfo(size, "Closet/BagItem", bagItem);

            customSV.InitItemData(GetItems());

            //scrolRect.onValueChanged.AddListener(CheckShowOrHideItem);
        }

        private void Update()
        {
            customSV.CheckShowOrHideItemAsync();
        }

        //private async void CheckShowOrHideItem(Vector2 arg0)
        //{
        //    customSV.CheckShowOrHideItemAsync();
        //}

        private List<Item> GetItems()
        {
            List<Item> items = new List<Item>();
            for (int i = 0; i < 100000; i++)
            {
                Item item = new Item();
                item.id = i;
                item.num = i;
                items.Add(item);
            }
            return items;
        }
    }
}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值