Unity-ScrollRect扩展
功能点简单说明
在原有的ScrollRect组件的基础上进行扩展:
1. 内部维护有对象池
2. 可以使用有限的子物体实现无限量的数据展示
3. 内部写有接口类,方便扩展
功能预览
屏幕录制 2023-12-07 162714
组件属性
代码
MyScrollRect.cs
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;
namespace SH
{
/// <summary>
/// 滑动列表子物体的控件接口
/// </summary>
/// <typeparam name="DataType"></typeparam>
public interface IDataControllerType<DataType>
{
void Init(int index, DataType data);
}
/// <summary>
/// 自定义滑动窗口组件
/// 只需要创建少量组件,即可完成大量数据展示(不采用翻页形式)</summary>
/// 支持上下滑动
/// 非继承MonoBehaviour类
/// <typeparam name="DataType">数据</typeparam>
/// <typeparam name="DataControllerType">UI控件类(需要继承IDataControllerType接口)</typeparam>
[System.Serializable]
public class MyScrollView<DataType, DataControllerType> : IDisposable where DataControllerType : MonoBehaviour, IDataControllerType<DataType>
{
[Tooltip("预生成的预制物数量"), ReadOnly]
public int dataCount = 18;
[Tooltip("当前窗口最大展示数量")]
public int curShowMaxCout = 16;
[Tooltip("滑动窗口的预制物")]
public GameObject prefab_data;
[Tooltip("滑动窗口组件")]
public ScrollRect scrollRect;
[Tooltip("垂直滑动条")]
public Scrollbar scrollbar;
public List<DataType> dataList = new(); //当前可以展示的数据集合
List<DataControllerType> dataControllerList = new(); //当前所有控件
float orcVal = 1;
int minIndex = 0;
int maxIndex = 0;
public MyScrollView()
{
dataCount = curShowMaxCout + 2;
}
public void Dispose()
{
dataList.Clear();
dataList = null;
for (int i = 0; i < dataControllerList.Count; i++)
{
GameObject.Destroy(dataControllerList[i].gameObject);
}
dataControllerList.Clear();
dataControllerList = null;
}
bool isInit = false;
public void Init()
{
if(isInit) return;
isInit = true;
Check();
scrollbar.onValueChanged.AddListener(UpdataPeoples);
UpdataPeoples(1);
}
void Check()
{
Assert.IsTrue(dataCount >= curShowMaxCout + 2);
Assert.IsTrue(prefab_data != null);
Assert.IsTrue(scrollRect != null);
Assert.IsTrue(scrollbar != null);
}
void UpdataPeoples(float val)
{
if (dataControllerList.Count <= 0)
{
for (int i = 0; i < dataCount; i++)
{
GameObject go = GameObject.Instantiate(prefab_data);
go.transform.SetParent(scrollRect.content, false);
var controller = go.AddComponent<DataControllerType>();
dataControllerList.Add(controller);
}
}
if (dataList == null || dataList.Count <= dataCount)
{
for (int i = 0; i < dataCount; i++)
{
if (dataList.Count > i)
{
dataControllerList[i].gameObject.SetActive(true);
dataControllerList[i].Init(i + 1, dataList[i]);
}
else
{
dataControllerList[i].gameObject.SetActive(false);
}
}
return;
}
else
{
float curDataSize = scrollbar.size;
if (val == -1)
{
orcVal = 1;
scrollRect.content.anchorMin = Vector2.zero;
for (int i = 0; i < dataCount; i++)
{
dataControllerList[i].Init(i + 1, dataList[i]);
}
minIndex = 1;
maxIndex = Math.Min(dataCount, dataControllerList.Count);
return;
}
int start = 0;
int last = dataCount - 1;
float size = scrollbar.size;
while (true)
{
//处理滑动效果(移动content组件)
float offset = val - orcVal;
if (Math.Abs(offset) < size)
{
scrollRect.content.anchoredPosition -= new Vector2(0, 50 * offset / size);
orcVal += offset;
}
else
{
scrollRect.content.anchoredPosition -= new Vector2(0, offset > 0 ? 50 : -50);
orcVal += size * (offset > 0 ? 1 : -1);
}
//处理子物体位置,保证持续移动后,子物体位置正确
float curContentY = scrollRect.content.anchoredPosition.y;
if (curContentY >= 50 && maxIndex < dataList.Count)
{
var startData = dataControllerList[start];
dataControllerList.RemoveAt(start);
startData.transform.SetAsLastSibling();
dataControllerList.Add(startData);
int newIndex = maxIndex;
if (newIndex < dataList.Count)
{
startData.Init(newIndex + 1, dataList[newIndex]);
scrollRect.content.anchoredPosition -= new Vector2(0, 50);
maxIndex += 1;
minIndex += 1;
}
}
else if (curContentY < 0 && minIndex > 0)
{
var lastData = dataControllerList[last];
dataControllerList.RemoveAt(last);
lastData.transform.SetAsFirstSibling();
dataControllerList.Insert(0, lastData);
int newIndex = minIndex;
if (newIndex >= dataCount - curShowMaxCout)
{
lastData.Init(newIndex - 1, dataList[newIndex - dataCount + curShowMaxCout]);
scrollRect.content.anchoredPosition -= new Vector2(0, -50);
maxIndex -= 1;
minIndex -= 1;
}
}
if (Math.Abs(val - orcVal) < 0.01f)
{
break;
}
}
}
}
/// <summary>
/// 更新数据集合,刷新列表
/// </summary>
/// <param name="dataList">新的数据源</param>
public void FlushDataList(ref List<DataType> dataList)
{
Init();
this.dataList.Clear();
this.dataList.AddRange(dataList);
UpdataPeoples(1);
}
}
}
使用
1. 添加一个滑动列表子物体的控件类(继承 IDataControllerType 接口)
//替换DataType为自己有的类名或者结构体名
public class DataController: MonoBehaviour, IDataControllerType<DataType>
{
/*
UI 组件
*/
void Awake()
{
/*
UI 组件初始化
*/
}
public void Init(int index, DataType data)
{
/*
用data的数据给 UI 组件赋值
*/
}
}
2.添加一个MyScrollView对象
//替换DataType为自己有的类名或者结构体名(与上面保持一致)
MyScrollView<DataType, DataController> scrollView = new MyScrollView<DataType, DataController>();
3.UI设置
关于ScrollRect部分的UI,是按照常规的UI布局来得
这个是控件类的属性贴图