Demo链接https://download.csdn.net/download/qq_41973169/89364284http://Unity UGUI无限滚动列表
在游戏开发中,列表视图是一个常见的UI组件。实现一个高效的列表视图尤其重要,尤其是在需要展示大量数据时。本文将介绍如何在Unity中实现一个高效的无限滚动列表,包括两个关键脚本:InfiniteScroll 和 ListItem 两个脚本 话不多说直接上代码
Unity版本2022.3.X
InfiniteScroll.cs
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using System;
public class InfiniteScroll : MonoBehaviour
{
public delegate void ItemDelegate(int itemDataIndex, int slotID);
public ItemDelegate OnUpdateItemImpl;// 更新Item回调函数
public enum ScrollDirection
{
Vertical,
Horizontal
}
public GameObject itemPrefab;
[Header("横向滑动表示宽度; 纵向滑动表示高度")]
public float itemSize;
[Header("列表项间距")]
public float spacing; // 列表项间距
[Header("最大可见列表项数量")]
public int maxVisibleItemCount;
[Header("滑动方向")]
public ScrollDirection scrollDirection = ScrollDirection.Vertical;
private ScrollRect _scrollRect;
private RectTransform _contentRectTransform;
private int _firstVisibleIndex; // 第一个可见列表项的索引
private int _lastVisibleIndex; // 最后一个可见列表项的索引
private List<GameObject> _itemList; // 列表项对象列表
private float _itemTotalSize; // 列表项总尺寸(包括间距)
private int _totalItemDataCount; // item总共数据数量
private void Awake()
{
_itemTotalSize = itemSize + spacing;
_itemList = new List<GameObject>();
_scrollRect = GetComponent<ScrollRect>();
_contentRectTransform = _scrollRect.content.GetComponent<RectTransform>();
_scrollRect.onValueChanged.AddListener(OnScrollChanged);
}
public void Init()
{
for (int i = 0; i < maxVisibleItemCount; i++)
{
int slotID = i + 1;
GameObject obj = Instantiate(itemPrefab, _contentRectTransform);
ListItem listItem = obj.GetComponent<ListItem>();
listItem.Init(this);
listItem.SetSlotID(slotID);
obj.name = slotID.ToString();
SetPivot(obj, _itemTotalSize * i);
_itemList.Add(obj);
}
itemPrefab.SetActive(false);
}
private void SetPivot(GameObject obj, float position)
{
RectTransform rect = obj.GetComponent<RectTransform>();
if (scrollDirection == ScrollDirection.Vertical)
{
rect.pivot = new Vector2(0.5f, 1);
rect.anchorMax = new Vector2(0.5f, 1);
rect.anchorMin = new Vector2(0.5f, 1);
rect.anchoredPosition = new Vector2(0, -position);
}
else
{
rect.pivot = new Vector2(0, 0.5f);
rect.anchorMax = new Vector2(0, 0.5f);
rect.anchorMin = new Vector2(0, 0.5f);
rect.anchoredPosition = new Vector2(position, 0);
}
}
private void OnScrollChanged(Vector2 position)
{
if (scrollDirection == ScrollDirection.Vertical)
{
Func<bool> condition1 = () =>
{
// 如果Content的Y轴坐标大于上面第二个Item 并且最后的下标不是数据的最后一个则表示向下滑动可以更新Item
return _contentRectTransform.anchoredPosition.y > (_firstVisibleIndex + 1) * _itemTotalSize && _lastVisibleIndex < _totalItemDataCount - 1;
};
Func<bool> condition2 = () =>
{
// 如果Content的Y轴坐标小于上面第一个Item 并且最上面的索引不为0 则表示向上滑动可以更新Item
return _contentRectTransform.anchoredPosition.y < _firstVisibleIndex * _itemTotalSize && _firstVisibleIndex > 0;
};
UpdateItems(condition1, condition2);
}
else
{
Func<bool> condition1 = () =>
{
// 如果Content的X轴坐标大于右边第二个Item 并且最后的下标不是数据的最后一个则表示向右滑动可以更新Item
return Mathf.Abs(_contentRectTransform.anchoredPosition.x) > (_firstVisibleIndex + 1) * _itemTotalSize && _lastVisibleIndex < _totalItemDataCount - 1;
};
Func<bool> condition2 = () =>
{
// 如果Content的X轴坐标小于左边第一个Item 并且最上面的索引不为0 则表示向左滑动可以更新Item
return Mathf.Abs(_contentRectTransform.anchoredPosition.x) < _firstVisibleIndex * _itemTotalSize && _firstVisibleIndex > 0;
};
UpdateItems(condition1, condition2);
}
}
private void UpdateItemUI(GameObject obj, int dataIndex)
{
ListItem listItem = obj.GetComponent<ListItem>();
listItem.SetDataIndex(dataIndex);
listItem.UpdateUI();
}
private void UpdateItems(Func<bool> condition1, Func<bool> condition2)
{
while (condition1())
{
GameObject first = _itemList[0];
RectTransform rectTrans = first.GetComponent<RectTransform>();
_itemList.RemoveAt(0);
_itemList.Add(first);
rectTrans.anchoredPosition = scrollDirection == ScrollDirection.Horizontal ?
new Vector2((_lastVisibleIndex + 1) * _itemTotalSize, 0) :
new Vector2(0, -(_lastVisibleIndex + 1) * _itemTotalSize);
_firstVisibleIndex += 1;
_lastVisibleIndex += 1;
UpdateItemUI(first, _lastVisibleIndex + 1);
}
while (condition2())
{
GameObject last = _itemList[_itemList.Count - 1];
RectTransform rectTrans = last.GetComponent<RectTransform>();
_itemList.RemoveAt(_itemList.Count - 1);
_itemList.Insert(0, last);
rectTrans.anchoredPosition = scrollDirection == ScrollDirection.Horizontal ?
new Vector2((_firstVisibleIndex - 1) * _itemTotalSize, 0) :
new Vector2(0, -(_firstVisibleIndex - 1) * _itemTotalSize);
_firstVisibleIndex -= 1;
_lastVisibleIndex -= 1;
UpdateItemUI(last, _firstVisibleIndex + 1);
}
}
public void RefreshList()
{
_firstVisibleIndex = 0;
for (int i = 0; i < _itemList.Count; i++)
{
GameObject obj = _itemList[i];
obj.SetActive(i < _totalItemDataCount);
if (i < _totalItemDataCount)
{
_lastVisibleIndex = i;
UpdateItemUI(obj, i + 1);
}
}
float size = _itemTotalSize * _totalItemDataCount - spacing;
if (scrollDirection == ScrollDirection.Vertical)
{
_contentRectTransform.sizeDelta = new Vector2(_contentRectTransform.sizeDelta.x, size);
}
else
{
_contentRectTransform.sizeDelta = new Vector2(size, _contentRectTransform.sizeDelta.y);
}
_contentRectTransform.anchoredPosition = Vector2.zero;
}
public void SetTotalItemDataCount(int count)
{
_totalItemDataCount = count;
}
}
ListItem.cs
using UnityEngine;
using UnityEngine.UI;
public class ListItem : MonoBehaviour
{
public Text itemText;
private InfiniteScroll _infiniteScroll;
private int _slotID;
private int _dataIndex;
public void Init(InfiniteScroll infiniteScroll)
{
_infiniteScroll = infiniteScroll;
}
public void SetSlotID(int slotID)
{
_slotID = slotID;
}
public void SetDataIndex(int dataIndex)
{
_dataIndex = dataIndex;
}
public void UpdateUI()
{
itemText.text = $"{_dataIndex} SlotID{_slotID}";
if (_infiniteScroll.OnUpdateItemImpl != null)
{
_infiniteScroll.OnUpdateItemImpl.Invoke(_dataIndex, _slotID);
}
else
{
Debug.LogError("InfiniteScroll.OnUpdateItemImpl == null");
}
}
}
测试代码
MyTest.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyTest : MonoBehaviour
{
public InfiniteScroll horizontalInfiniteScroll;
public InfiniteScroll verticalInfiniteScroll;
void Start()
{
horizontalInfiniteScroll.Init();
verticalInfiniteScroll.Init();
horizontalInfiniteScroll.OnUpdateItemImpl = (dataIndex, slotID) =>
{
Debug.LogError($"horizontalInfiniteScroll dataIndex:{dataIndex}, slotID:{slotID}");
};
verticalInfiniteScroll.OnUpdateItemImpl = (dataIndex, slotID) =>
{
Debug.LogError($"verticalInfiniteScroll dataIndex:{dataIndex}, slotID:{slotID}");
};
horizontalInfiniteScroll.SetTotalItemDataCount(100);
verticalInfiniteScroll.SetTotalItemDataCount(100);
horizontalInfiniteScroll.RefreshList();
verticalInfiniteScroll.RefreshList();
}
}
测试效果
场景树UI布局
脚本挂载