Unity UGUI 垂直循环复用滚动
一 基础类 在unity里面新建这几个类
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 垂直方向滚动
/// </summary>
public class CustomScroll:MonoBehaviour
{
public ScrollRect scrollRect;
protected List<object> list = new();
private RectTransform contentRect;
private GridLayoutGroup layoutGroup;
public GameObject item;
private List<CustomScrollItemMono> scrollTestItems = new();
private CustomVertial customVertial;
private int startIndex;
private int endIndex;
private int showItemCount = 14;
private void Awake()
{
contentRect = scrollRect.content.GetComponent<RectTransform>();
layoutGroup = contentRect.GetComponent<GridLayoutGroup>();
customVertial = contentRect.GetComponent<CustomVertial>();
scrollRect.horizontal = false;
scrollRect.vertical = true;
scrollRect.onValueChanged.AddListener((value) =>
{
layoutGroup.enabled = false;
customVertial.enabled = false;
for (int i = startIndex; i < itemPosList.Count; i++)
{
var y = contentRect.anchoredPosition3D.y;
if (i + layoutGroup.constraintCount < itemPosList.Count)
{
if (scrollRect.velocity.y > 0)//手指上滑
{
var targetY = -itemPosList[i + layoutGroup.constraintCount].y;
if (y >= targetY)
{
startIndex = i + layoutGroup.constraintCount;
endIndex = startIndex + showItemCount - layoutGroup.constraintCount;
break;
}
}
else if (scrollRect.velocity.y < 0)//手指下滑
{
if (startIndex > 0 && startIndex < itemPosDic.Count)
{
var targetY = -itemPosDic[startIndex].y;
if (y <= targetY)
{
startIndex = i - layoutGroup.constraintCount;
endIndex = startIndex + showItemCount - layoutGroup.constraintCount;
break;
}
}
}
}
}
Debug.Log($"bbb startIndex {startIndex} endIndex {endIndex}");
if (endIndex >= itemPosDic.Count) { return; }
int index = 0;
for (int i = startIndex; i < endIndex + layoutGroup.constraintCount; i++)
{
if (index < scrollTestItems.Count && i < itemPosDic.Count)
{
var item = scrollTestItems[index];
item.Init(i, list[i]);
var rect = item.gameObject.GetComponent<RectTransform>();
rect.anchoredPosition3D = itemPosDic[i];
index += 1;
}
}
});
}
private Dictionary<int, Vector3> itemPosDic = new();
private List<Vector3> itemPosList = new();
public void Init()
{
InitListData();
InitItemsPos();
InitContentSize();
InitShowItems();
}
public virtual void InitListData()
{
//必须要新建类 继承此类 重写此方法设置数据
}
private void InitItemsPos()
{
int prevRow = 0;
int initIndex = 0;
for (int i = 0; i < list.Count; i++)
{
int row = Mathf.CeilToInt((i + 1) * 1f / layoutGroup.constraintCount);
var pos = Vector3.zero;
if (row != prevRow)
{
pos = new Vector3(0, -((row - 1) * (layoutGroup.cellSize.y + layoutGroup.spacing.y) + layoutGroup.padding.top));
initIndex = 0;
}
else
{
initIndex += 1;
pos = new Vector3(initIndex * (layoutGroup.cellSize.x + layoutGroup.spacing.x) + layoutGroup.padding.left, -((row - 1) * (layoutGroup.cellSize.y + layoutGroup.spacing.y) + layoutGroup.padding.top));
}
itemPosList.Add(pos);
itemPosDic.Add(i, pos);
prevRow = row;
}
}
private void InitContentSize()
{
float width = layoutGroup.padding.left + layoutGroup.cellSize.x * layoutGroup.constraintCount +
(layoutGroup.constraintCount - 1) * layoutGroup.spacing.x;
float height = layoutGroup.padding.top + layoutGroup.cellSize.y * Mathf.Ceil(itemPosList.Count / layoutGroup.constraintCount) +
itemPosList.Count / layoutGroup.constraintCount * layoutGroup.spacing.y;
contentRect.sizeDelta = new Vector2(width, height);
customVertial.SetSize(new Vector2(width, height));
}
private void InitShowItems()
{
for (int i = 0; i < showItemCount; i++)
{
GameObject obj = Instantiate(item, contentRect);
RectTransform rect = obj.GetComponent<RectTransform>();
rect.anchorMin = new Vector2(0, 1);
rect.anchorMax = new Vector2(0, 1);
rect.pivot = new Vector2(0, 1);
obj.transform.name = i.ToString();
obj.SetActive(true);
CustomScrollItemMono testItem = obj.GetComponent<CustomScrollItemMono>();
testItem.Init(i, list[i]);
scrollTestItems.Add(testItem);
}
item.SetActive(false);
}
}
public class ScrollTestData
{
public int ID;
}
using System.Collections.Generic;
using UnityEngine;
public interface ICustomScrollItem
{
public void Init(int index,object data);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//这个类一定要挂在GridLayout下面 控制Content尺寸
public class CustomVertial : ContentSizeFitter
{
private Vector2 size = new();
private RectTransform rectTransform;
protected override void Awake()
{
base.Awake();
rectTransform = GetComponent<RectTransform>();
}
public void SetSize(Vector2 s)
{
size = s;
}
public override void SetLayoutVertical()
{
base.SetLayoutVertical();
rectTransform.sizeDelta = size;
}
}
using System.Collections.Generic;
using UnityEngine;
public class CustomScrollItemMono : MonoBehaviour,ICustomScrollItem
{
public void Init(int index, object data)
{
InitView(index, data);
}
public virtual void InitView(int index, object data)
{
}
}
二 具体使用实例类
1.挂在ScrollRect组件下面 在面板上设置好Scroll和Content和子元素Item
重写了InitListData 方法 设置需要的列表数据 这里测试放在Start里面启动列表
实际根据具体逻辑启动
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CustomScrollTest:CustomScroll
{
public override void InitListData()
{
base.InitListData();
for (int i = 0; i < 100; i++)
{
list.Add(new ScrollTestData() { ID = i });
}
}
private void Start()
{
Init();
}
}
2.具体的Item元素类 挂在Item上
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScrollTestItem : CustomScrollItemMono
{
public Text text;
private ScrollTestData Data= new();
private int Index;
public override void InitView(int index, object data)
{
ScrollTestData testData = (ScrollTestData)data;
Index = index;
Data = testData;
text.text = Index.ToString();
transform.name = Index.ToString();
}
}