unity基于UGUI的虚拟列表

Unity自带的滚动列表如果创建过多,如同时创建上千,上万,易引起卡顿,为解决这种现象的发生,这里实现虚拟列表来处理这种情况。原理就是初始化固定个数的item数,当滚动列表向下滚动至可视范围外的时候将第一个Item移动至最底层,如此循环执行,向上滚动如是,于是我们只用关注数据的变化,动态改变item的数据即可。

站在巨人的肩膀上才能看的更远(拿来主义与总结)

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

public class VirtualListFacade : MonoBehaviour
{

    public VirtualList uiList;
    // Use this for initialization
    void Start()
    {
        uiList.onSelectedEvent = OnSelectedEventHandler;
        List listData = new List();
        for (int i = 0; i < 1000; i++)
        {
            listData.Add(i);
        }
        uiList.Data(listData);
    }

    private void OnSelectedEventHandler(ListItem item)
    {
        Debug.LogError("选择的单元数据为:" + item.GetData().ToString());
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            List listData = new List();
            int count = Random.Range(100, 1000);
            for (int i = 0; i < count; i++)
            {
                listData.Add(i);
            }
            uiList.Data(listData);
        }
    }
}

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

public class VirtualList : MonoBehaviour
{
    /// 
    /// 滚动的方向
    /// 
    enum Direction
    {
        Horizontal,//水平方向
        Vertical//垂直方向
    }
    /// 
    /// 离上边的距离
    /// 
    [SerializeField]
    private float topPadding = 0;
    /// 
    /// 离下边的距离
    /// 
    [SerializeField]
    private float bottomPadding = 0;
    [SerializeField]
    private RectTransform m_Cell;
    [SerializeField]
    private Vector2 m_Page = new Vector2(1, 1);//必须设置;几行几列
    [SerializeField]
    Direction direction = Direction.Horizontal;
    [SerializeField, Range(4, 10)]
    private int m_BufferNo;                   //缓存的Item数(在手动设置的基础上增加缓存格子数)
    /// 
    /// 间隔
    /// 
    [SerializeField]
    private float cellGapX = 0f;
    [SerializeField]
    private float cellGapY = 0f;
    public delegate void OnSelectedEvent(ListItem item);
    /// 
    /// 选择事件 但组件上一定要有Button组件 设置要在Data()设置数据前
    /// 
    public OnSelectedEvent onSelectedEvent;//
    private List m_InstantiateItems = new List();
    private List m_oldItems = new List();
    private IList m_Datas;
    public Vector2 CellRect
    {
        get
        {
            return m_Cell != null ? new Vector2(cellGapX + m_Cell.sizeDelta.x, cellGapY + m_Cell.sizeDelta.y) : new Vector2(100, 100);
        }
    }
    public float CellScale { get { return direction == Direction.Horizontal ? CellRect.x : CellRect.y; } }
    private float m_PrevPos = 0;

    //当前移动的方向位置
    public float DirectionPos { get { return direction == Direction.Horizontal ? m_Rect.anchoredPosition.x : m_Rect.anchoredPosition.y; } }
    private int m_CurrentIndex;//页面的第一行(列)在整个conten中的位置
    private Vector2 m_InstantiateSize = Vector2.zero;
    public Vector2 InstantiateSize
    {
        get
        {
            if (m_InstantiateSize == Vector2.zero)
            {
                float rows, cols;
                if (direction == Direction.Horizontal)
                {
                    rows = m_Page.x;                         //行数
                    cols = m_Page.y + (float)m_BufferNo;     //列数(+缓存的格子数)
                }
                else
                {
                    rows = m_Page.x + (float)m_BufferNo;
                    cols = m_Page.y;
                }
                m_InstantiateSize = new Vector2(rows, cols);
            }
            return m_InstantiateSize;
        }
    }
    public int PageCount { get { return (int)m_Page.x * (int)m_Page.y; } }
    public int PageScale { get { return direction == Direction.Horizontal ? (int)m_Page.x : (int)m_Page.y; } }
    private ScrollRect m_ScrollRect;
    private RectTransform m_Rect;
    public int InstantiateCount { get { return (int)InstantiateSize.x * (int)InstantiateSize.y; } }   //实际应该生成的格子数(行x列)
    protected void Awake()
    {
        m_ScrollRect = GetComponentInParent();
        m_ScrollRect.horizontal = direction == Direction.Horizontal;
        m_ScrollRect.vertical = direction == Direction.Vertical;
        m_Rect = GetComponent();
        if (m_Cell.transform.parent != null)
            m_Cell.gameObject.SetActive(false);
        //设置左上角为原点
        m_Rect.anchorMax = Vector2.up;
        m_Rect.anchorMin = Vector2.up;
        m_Rect.pivot = Vector2.up;
        m_Rect.anchoredPosition = new Vector2(0f, 0f);
    }

    /// 
    /// 设置数据 数据格式为IList
    /// 
    /// Data.
    public void Data(object data)
    {
        Reset();
        m_Datas = data as IList;
        if (m_Datas.Count > PageCount)
        {
            setBound(getRectByNum(m_Datas.Count));
        }
        else
        {
            setBound(m_Page);
        }
        if (m_Datas.Count > InstantiateCount)
        {
            while (m_InstantiateItems.Count < InstantiateCount)
            {
                createItem(m_InstantiateItems.Count);
            }
        }
        else
        {
            while (m_InstantiateItems.Count > m_Datas.Count)
            {
                removeItem(m_InstantiateItems.Count - 1);
            }
            while (m_InstantiateItems.Count < m_Datas.Count)
            {
                createItem(m_InstantiateItems.Count);
            }
        }
        if (m_Datas.Count > 0)
        {
            int count = Mathf.Min(m_InstantiateItems.Count, m_Datas.Count);
            for (int i = 0; i < count; i++)
            {
                updateItem(i, m_InstantiateItems[i].gameObject);
            }
        }
    }

    private void Reset()
    {

        for (int i = 0; i < m_InstantiateItems.Count; i++)
        {
            m_InstantiateItems[i].gameObject.SetActive(false);
            m_oldItems.Add(m_InstantiateItems[i]);
        }
        m_InstantiateItems.Clear();
        m_PrevPos = 0;
        m_CurrentIndex = 0;
        selectedObject = null;
        selectedItem = null;
        this.GetComponent().anchoredPosition = new Vector2(0f, 0);
    }
    public void SetIndexToBottom(int itemIndex)
    {
        this.GetComponent().anchoredPosition = new Vector2(0f, CellRect.y * itemIndex - m_ScrollRect.GetComponent().sizeDelta.y + CellRect.y * 2 + topPadding + bottomPadding);
    }
    private void createItem(int index)
    {
        RectTransform item = null;
        if (m_oldItems.Count > 0)
        {
            item = m_oldItems[0];
            m_oldItems.Remove(item);
        }
        else
        {
            item = GameObject.Instantiate(m_Cell);
            item.SetParent(transform, false);
            item.anchorMax = Vector2.up;
            item.anchorMin = Vector2.up;
            item.pivot = Vector2.up;
        }

        item.name = "item" + index;
        item.anchoredPosition = getPosByIndex(index);
        m_InstantiateItems.Add(item);
        item.gameObject.SetActive(true);
        //updateItem(index, item.gameObject);
    }
    private void removeItem(int index)
    {
        RectTransform item = m_InstantiateItems[index];
        m_InstantiateItems.Remove(item);
        item.gameObject.SetActive(false);
        m_oldItems.Add(item);
        //RectTransform.Destroy(item.gameObject);
    }
    /// 
    /// 由格子数量获取多少行多少列
    /// 
    /// 格子个数
    /// 
    private Vector2 getRectByNum(int num)
    {
        return direction == Direction.Horizontal ?
            new Vector2(m_Page.x, Mathf.CeilToInt(num / m_Page.x)) :
                new Vector2(Mathf.CeilToInt(num / m_Page.y), m_Page.y);
    }
    /// 
    /// 设置content的大小
    /// 
    /// 行数
    /// 列数
    private void setBound(Vector2 bound)
    {
        m_Rect.sizeDelta = new Vector2(bound.y * CellRect.x, bound.x * CellRect.y + bottomPadding + topPadding);
    }
    public float MaxPrevPos
    {
        get
        {
            float result;
            Vector2 max = getRectByNum(m_Datas.Count);
            if (direction == Direction.Horizontal)
            {
                result = max.y - m_Page.y;
            }
            else
            {
                result = max.x - m_Page.x;
            }
            return result * CellScale;
        }
    }
    public float scale { get { return direction == Direction.Horizontal ? 1f : -1f; } }    //当坐标系位于左上角时的相对位置
    private bool isFirst = true;
    void Update()
    {
        if (isFirst == true)
        {
            isFirst = false;
            return;
        }
        while (scale * DirectionPos - m_PrevPos < -CellScale * 2)
        {
            //Down
            if (m_PrevPos <= -MaxPrevPos) return;
            m_PrevPos -= CellScale;
            List range = m_InstantiateItems.GetRange(0, PageScale);
            m_InstantiateItems.RemoveRange(0, PageScale);
            m_InstantiateItems.AddRange(range);
            for (int i = 0; i < range.Count; i++)
            {
                moveItemToIndex(m_CurrentIndex * PageScale + m_InstantiateItems.Count + i, range[i]);
            }
            m_CurrentIndex++;
        }
        while (scale * DirectionPos - m_PrevPos > -CellScale)
        {
            //Up
            if (Mathf.RoundToInt(m_PrevPos) >= 0) return;
            m_PrevPos += CellScale;
            m_CurrentIndex--;
            if (m_CurrentIndex < 0) return;
            List range = m_InstantiateItems.GetRange(m_InstantiateItems.Count - PageScale, PageScale);
            m_InstantiateItems.RemoveRange(m_InstantiateItems.Count - PageScale, PageScale);
            m_InstantiateItems.InsertRange(0, range);
            for (int i = 0; i < range.Count; i++)
            {
                moveItemToIndex(m_CurrentIndex * PageScale + i, range[i]);
            }
        }
    }
    private void moveItemToIndex(int index, RectTransform item)
    {
        item.anchoredPosition = getPosByIndex(index);
        updateItem(index, item.gameObject);
    }
    private Vector2 getPosByIndex(int index)
    {
        return direction == Direction.Horizontal ?
            new Vector2(Mathf.Floor(index / InstantiateSize.x) * CellRect.x, -(index % InstantiateSize.x) * CellRect.y) :
                new Vector2((index % InstantiateSize.y) * CellRect.x, -Mathf.Floor(index / InstantiateSize.y) * CellRect.y - topPadding);
        //float x, y;
        //if(direction == Direction.Horizontal)
        //{
        //    x = index % m_Page.x;
        //    y = Mathf.FloorToInt(index / m_Page.x);
        //}
        //else
        //{
        //    x = Mathf.FloorToInt(index / m_Page.y);
        //    y = index % m_Page.y;
        //}
        //return new Vector2(y * CellRect.x, -x * CellRect.y);
    }
    private object selectedObject = null;
    [System.NonSerialized]
    public ListItem selectedItem = null;
    private void updateItem(int index, GameObject item)
    {
        item.SetActive(index < m_Datas.Count);
        if (item.activeSelf)
        {
            ListItem lit = item.GetComponent();
            lit.UpdateItem(index, item);
            lit.Data(m_Datas[index]);
            if (selectedObject == m_Datas[index])
            {
                lit.SetSelected(true);
            }
            else
            {
                lit.SetSelected(false);
            }
            if (lit.GetComponent

将VirtualList挂载到Content组件上即可

 

你可能感兴趣的:(unity技术类)