如果一个scrollView中显示的按钮总数多达四五百,可以一次性全部生成吗?不能!!会非常非常卡。因此需要只生成一定小数量的按钮,然后动态调整这些按钮的位置和显示内容以及绑定事件来达到显示海量数据的办法。废话不多说,直接上。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL6NmaNdXW650MNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1kTN0ITMyATM5EzMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
/// <summary>
/// 挂载在ScrollView游戏物体上 这个类懒得写注释了。
/// </summary>
public class LoopList : MonoBehaviour
{
/// <summary>
/// 自己创建的一个临时存储的列表,模拟一个简单的数据表或者数据库
/// </summary>
List<string> data = new List<string>();
/// <summary>
/// 要动态生成的预制体按钮
/// </summary>
public GameObject prefabButton;
/// <summary>
/// 滚动卷轴组件
/// </summary>
ScrollRect scrollRect;
/// <summary>
/// 滚动卷轴的transform组件
/// </summary>
RectTransform scrollRectTransform;
/// <summary>
/// content的transform组件
/// </summary>
RectTransform contentTransform;
private List<LoopListItem> _items=new List<LoopListItem>();
int itemMaxNum = 0;
float itemHeight = 0;
public float offsetY = 0;
private void Awake()
{
scrollRectTransform = transform as RectTransform;
scrollRect = GetComponent<ScrollRect>();
contentTransform = scrollRect.transform.Find("Viewport/Content") as RectTransform;
}
// Use this for initialization
void Start ()
{
//填充模拟的数据表,共317个数据
for (int i = 1; i <= 317; i++)
{
data.Add("按钮" + i);
}
itemHeight = (prefabButton.transform as RectTransform).rect.height;
itemMaxNum = GetShowItemNum();
SetContentSize();
SpawnItem();
scrollRect.onValueChanged.AddListener(Refresh);
}
private void Refresh(Vector2 delta)
{
foreach (LoopListItem item in _items)
{
item.OnValueChangedEvent();
}
}
private int GetShowItemNum()
{
return Mathf.CeilToInt(scrollRectTransform.rect.height / (itemHeight + offsetY))+1;
}
void SpawnItem()
{
GameObject temp = null;
LoopListItem itemTemp = null;
for (int i = 0; i < itemMaxNum; i++)
{
temp = Instantiate<GameObject>(prefabButton, contentTransform, false);
itemTemp = temp.AddComponent<LoopListItem>();
itemTemp._getData += GetData;
itemTemp.Init(i,offsetY,itemMaxNum,contentTransform);
_items.Add(itemTemp);
}
}
string GetData(int index)
{
if(index<0||index>=data.Count)
{
return "";
}
return data[index];
}
void SetContentSize()
{
float y = data.Count * (itemHeight + offsetY)-offsetY;
contentTransform.sizeDelta = new Vector2(contentTransform.sizeDelta.x, y);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
/// <summary>
/// 按钮自身的管理器,挂载在每一个生成按钮上
/// </summary>
public class LoopListItem : MonoBehaviour {
/// <summary>
/// 事件绑定,用来获取按钮对应的数据
/// </summary>
public event Func<int, string> _getData;
/// <summary>
/// 当前按钮的ID
/// </summary>
int id=-1;
/// <summary>
/// 当前按钮的transform组件
/// </summary>
RectTransform rect;
public RectTransform rectTransform
{
get
{
if(rect==null)
{
rect = GetComponent<RectTransform>();
}
return rect;
}
}
/// <summary>
/// 当前按钮的文字显示组件
/// </summary>
Text des;
public Text Des
{
get
{
if(des==null)
{
des = transform.Find("Lable").GetComponent<Text>();
}
return des;
}
}
/// <summary>
/// 滚动卷轴列表的content游戏物体的transform组件
/// </summary>
RectTransform content;
/// <summary>
/// 按钮之间的间距,变量名或许应该叫spacing
/// </summary>
float offsetY;
/// <summary>
/// 当前的scrollView的视口中能放置按钮的最大个数
/// </summary>
int itemMaxNum;
/// <summary>
/// 初始化按钮函数,也即是在按钮被生成时调用的函数,在LoopList脚本中被调用
/// </summary>
/// <param name="_id"></param>
/// <param name="_offsetY"></param>
/// <param name="_itemMaxNum"></param>
/// <param name="_content"></param>
public void Init(int _id, float _offsetY, int _itemMaxNum, RectTransform _content)
{
//LoopList脚本中已经获取该组件,传值进来接收即可。
content= _content;
//最大值以获取,传值过来接收即可
itemMaxNum = _itemMaxNum;
//按钮之间的间距,同上
offsetY = _offsetY;
//设置该按钮的初始ID
id = _id;
//根据id获取对应的按钮名称文本并设置
Des.text = _getData(_id);
//根据id设置按钮对应的位置
rectTransform.anchoredPosition = new Vector2(0, -id * (rectTransform.rect.height + offsetY));
//将按钮名称同样改变,这个有没有无所谓,具体需求根据个人来讲
gameObject.name = _getData(_id);
}
/// <summary>
/// ScrollView上下滑动时,也即滑动值或者说Content游戏物体位置产生变化时,自动调用的函数。此函数在LoopList脚本中被调用.
/// </summary>
public void OnValueChangedEvent()
{
//根据content的位置计算出content与viePort重叠区域内,对应的顶端ID是多少。比如每个按钮100宽度,当前content位置为1000,那么顶端的按钮应该就是数量十,即ID为9.
if (!gameObject.activeSelf)
{
return;
}
float y = content.anchoredPosition.y;
if (y < 0)
{
y = 0;
}
int startId = Mathf.FloorToInt(y / (rectTransform.rect.height + offsetY));
//将重叠区域内的首个ID和当前按钮的初始ID相加,就是临时用的目标ID。比如当前初始ID为0,那么它就应当是当前区域内的最顶端按钮。 其它按钮以此类推。很简单的逻辑。
int tempID = id + startId;
//根据临时目标id设置按钮对应的位置,千万注意,在此过程中初始ID不发生改变
rectTransform.anchoredPosition = new Vector2(0, -tempID * (rectTransform.rect.height + offsetY));
Des.text = _getData(tempID);
gameObject.name = _getData(tempID);
}
}