小工具,留着备用~
实现思路:
1.生成列表之前将所有数据准备好,放入List中,content的高度在初始化时根据数据长度设置好。
2.滚动时取索引区间的数据,有几个索引就显示几行item。
3.滚动列表只负责显示,每个item上的内容由调用者控制:SetItemInfo(Transform item, int index)。
效果:

调用方式:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/*
des:循环列表测试
author:lfw
*/
public class TestPanel : MonoBehaviour
{
InfinityScrollList m_scrollList; //无限滚动列表
List<string> m_dataList = new List<string>(100); //准备好的数据
public override void Awake()
{
var scroll = Transform.Find("Scroll View").GetComponent<ScrollRect>();
var item = ObjectManager.Instance.InstantiateObject("Assets/Res/Prefabs/Item.prefab");
for(int i = 0; i < 20; i++)
m_dataList.Add("数据" + i);
m_scrollList = new Util.InfinityScrollList(scroll);
m_scrollList.Init(item, 6, m_dataList.Count, SetItemInfo); //初始化
}
//自定义填充函数
private void SetItemInfo(Transform item, int index)
{
item.Find("Text").GetComponent<Text>().text = m_dataList[index];
//var btn = item.transform.Find("Button").GetComponent<Button>();
//btn.onClick.RemoveAllListeners();
//btn.onClick.AddListener(
// () =>
// {
// m_dataList.RemoveAt(index);
// m_scrollList.DataCount -= 1;
// m_scrollList.DoForceUpdate(false);
// });
}
}
代码实现:
InfinityScrollList .cs
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/*
des:循环列表类
author:lfw
*/
class InfinityScrollList
{
int m_headIndex = 0; //顶部显示的数据索引
int m_tailIndex = 0; //最底部显示数据索引
float m_itemHeight = 0; //item高度
float m_topPading = 0; //距顶部边距
float m_spacing = 0; //行间距
int m_maxItemCount; //最大item数量(最好总高度刚好超过滚动框高度)
Action<Transform, int> m_setInfoCallback; //给每个item设置显示内容的函数
List<Transform> m_itemList = new List<Transform>(); //item列表
GameObject m_itemPrefab;
ScrollRect m_scrollView;
Transform m_content;
RectTransform m_contentRect;
public int DataCount { get; set; } = 0; //待填充数据数量
public InfinityScrollList(ScrollRect scrollView)
{
m_headIndex = 0;
m_tailIndex = 0;
m_scrollView = scrollView;
m_content = scrollView.content;
m_contentRect = m_content.GetComponent<RectTransform>();
m_scrollView.onValueChanged.AddListener(this.OnValueChanged);
}
/// <summary>
/// 初始化必要数据
/// </summary>
/// <param name="itemPrefab">item预制体</param>
/// <param name="maxItem">最大item数量</param>
/// <param name="maxData">数据数量</param>
/// <param name="SetItemCallBack">修改item信息的回调</param>
public void Init(GameObject itemPrefab, int maxItem, int maxData, Action<Transform, int> setItemCallback = null)
{
m_itemPrefab = itemPrefab;
m_headIndex = 0;
m_tailIndex = 0;
m_maxItemCount = maxItem;
DataCount = maxData;
m_setInfoCallback = setItemCallback;
m_itemHeight = itemPrefab.GetComponent<RectTransform>().rect.height + m_spacing;
m_contentRect.sizeDelta = new Vector2(m_contentRect.sizeDelta.x, m_itemHeight * DataCount + m_topPading);
UpdateContent();
}
/// <summary>
/// 数据发生变化时刷新item显示内容
/// </summary>
/// <param name="toTop"> 是否回到顶部</param>
public void DoForceUpdate(bool toTop)
{
if (toTop)
{
m_headIndex = 0;
}
UpdateContent();
}
/// <summary>
/// 根据首尾数据索引,刷新item内容
/// </summary>
private void UpdateContent()
{
for (int i = 0; i < m_itemList.Count; i++)
{
if (i >= DataCount)
m_itemList[i].gameObject.SetActive(false);
}
int itemCount = Mathf.Clamp(DataCount, 0, m_maxItemCount);
//修正从后向前移除节点时,尾部索引越界的bug
if (m_headIndex + itemCount > DataCount)
{
m_headIndex = DataCount - itemCount;
}
Transform tran;
for (Int32 i = 0; i < itemCount; i++)
{
if (i < m_itemList.Count)
{
tran = m_itemList[i];
}
else
{
var item = m_itemPrefab.Instantiate();
tran = item.transform;
tran.GetComponent<RectTransform>().anchorMin = new Vector2(0.5f, 1);
tran.GetComponent<RectTransform>().anchorMax = new Vector2(0.5f, 1);
tran.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 1);
tran.SetParent(m_content, false);
m_itemList.Add(tran);
}
int index = m_headIndex + i;
tran.name = index.ToString();
tran.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, -m_itemHeight * index - m_topPading);
m_setInfoCallback?.Invoke(tran, index); //更新内容
}
m_tailIndex = m_headIndex + (itemCount - 1);
m_contentRect.sizeDelta = new Vector2(m_contentRect.sizeDelta.x, m_itemHeight * DataCount + m_topPading);
}
private void OnValueChanged(Vector2 v2)
{
if (m_itemList.Count <= 0)
{
return;
}
//向上滚动
while (m_contentRect.anchoredPosition.y > (m_headIndex + 1) * m_itemHeight && m_tailIndex != DataCount - 1)
{
Transform _first = m_itemList[0];
RectTransform _firstRect = _first.GetComponent<RectTransform>();
//将顶部item移到底部
m_itemList.RemoveAt(0);
m_itemList.Add(_first);
m_headIndex++;
m_tailIndex++;
_firstRect.anchoredPosition = new Vector2(0, -m_tailIndex * m_itemHeight - m_topPading);
//修改显示
_first.name = m_tailIndex.ToString();
m_setInfoCallback?.Invoke(_first, m_tailIndex);
}
//向下滚动
while (m_contentRect.anchoredPosition.y < m_headIndex * m_itemHeight && m_headIndex != 0)
{
Transform _last = m_itemList[m_itemList.Count - 1];
RectTransform _lastRect = _last.GetComponent<RectTransform>();
m_itemList.RemoveAt(m_itemList.Count - 1);
m_itemList.Insert(0, _last);
m_headIndex--;
m_tailIndex--;
_lastRect.anchoredPosition = new Vector2(0, -m_headIndex * m_itemHeight - m_topPading);
_last.name = m_headIndex.ToString();
m_setInfoCallback?.Invoke(_last, m_headIndex);
}
}
}
网友评论