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组件上即可