一个简单的列表,一行一个item,垂直滑动。
看看代码,简单修改一下可以实现多列,水平滑动等等,或者是加各种回调,就不写了。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public interface IReuseScrollRect
{
float GetItemHeight();
float GetItemGap();
int GetAllDataCount();
List<Transform> CreateUIItems(int count);
void UpdateItem(int itemIndex, int dataIndex);
}
public class ItemStruct
{
public int itemIndex;
public int dataIndex;
public Transform transform;
}
[RequireComponent(typeof(ScrollRect))]
public class ReuseScrollRect : MonoBehaviour
{
private IReuseScrollRect reuseScroll;
private float itemHeight;
private float itemGap;
private float heightAndGap;
private float viewWindowHeight;
private int allDataCount;
private ScrollRect scrollRect;
private RectTransform content;
private List<Transform> uiItems;
private List<ItemStruct> itemStructs;
private ItemStruct headItem;
private ItemStruct tailItem;
private float scrollDragY = 1;
private bool isUp = true;
public void Init(IReuseScrollRect reuseScroll)
{
scrollRect = GetComponent<ScrollRect>();
content = scrollRect.content;
scrollRect.onValueChanged.AddListener(OnScrollRectDrag);
this.reuseScroll = reuseScroll;
itemHeight = reuseScroll.GetItemHeight();
itemGap = reuseScroll.GetItemGap();
heightAndGap = itemHeight + itemGap;
allDataCount = reuseScroll.GetAllDataCount();
float contentY = heightAndGap * allDataCount;
content.sizeDelta = new Vector2(content.sizeDelta.x, contentY);
content.anchoredPosition = Vector2.zero;
viewWindowHeight = scrollRect.GetComponent<RectTransform>().rect.height;
int needCreateItemCount = Mathf.CeilToInt(viewWindowHeight / heightAndGap) + 3;
uiItems = reuseScroll.CreateUIItems(needCreateItemCount);
InitItems();
}
private void OnScrollRectDrag(Vector2 vector2)
{
isUp = scrollDragY > vector2.y ? true : false;
scrollDragY = vector2.y;
float y = Mathf.Clamp01(1 - vector2.y);
float moveLength = -y * (content.sizeDelta.y - viewWindowHeight);
while (true)
{
if (isUp)
{
if (tailItem.dataIndex < allDataCount - 1 && headItem.transform.localPosition.y - moveLength > heightAndGap * 2)
{
DownHeadItem();
UpdateUIData(tailItem.itemIndex, tailItem.dataIndex);
continue;
}
}
else
{
if (headItem.dataIndex > 0 && tailItem.transform.localPosition.y - moveLength < -(itemStructs.Count - 1) * heightAndGap)
{
UpTailItem();
UpdateUIData(headItem.itemIndex, headItem.dataIndex);
continue;
}
}
break;
}
}
private void InitItems()
{
itemStructs = new List<ItemStruct>();
for (int i = 0; i < uiItems.Count; i++)
{
ItemStruct item = new ItemStruct()
{
itemIndex = i,
dataIndex = i,
transform = uiItems[i]
};
itemStructs.Add(item);
Vector3 pos = item.transform.localPosition;
pos = new Vector3(pos.x, -(i * heightAndGap + itemHeight * 0.5f), pos.z);
item.transform.localPosition = pos;
UpdateUIData(i, i);
}
headItem = itemStructs[0];
tailItem = itemStructs[itemStructs.Count - 1];
}
private void UpdateUIData(int itemIndex, int dataIndex)
{
reuseScroll.UpdateItem(itemIndex, dataIndex);
}
private void DownHeadItem()
{
ItemStruct newTail = headItem;
newTail.dataIndex = tailItem.dataIndex + 1;
Vector3 pos = tailItem.transform.localPosition;
pos = new Vector3(pos.x, pos.y - heightAndGap, pos.z);
newTail.transform.localPosition = pos;
tailItem = newTail;
headItem = itemStructs[(headItem.itemIndex + 1) % itemStructs.Count];
}
private void UpTailItem()
{
ItemStruct newHead = tailItem;
newHead.dataIndex = headItem.dataIndex - 1;
Vector3 pos = headItem.transform.localPosition;
pos = new Vector3(pos.x, pos.y + heightAndGap, pos.z);
newHead.transform.localPosition = pos;
headItem = newHead;
tailItem = itemStructs[(tailItem.itemIndex - 1 + itemStructs.Count) % itemStructs.Count];
}
}
数据类或者UI类二选一实现接口。
item的预制体锚点不同,初始位置算法简单改改就行。本例默认锚点不动。
简单说一下滑动回调,加while循环是防止滑动过快出现“空当”,条件判断既要限制数据不越界,也要限制移动距离。
加一个示范吧。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScrollTestUI : UIBase, IReuseScrollRect
{
private ScrollRect rect;
private List<int> dataList = new List<int>();
private List<TestRankItemUI> itemsList = new List<TestRankItemUI>();
private ReuseScrollRect reuse;
private int dataCount = 200;
private void Awake()
{
ResourcesMgr.Init();
for (int i = 0; i < dataCount; i++)
{
dataList.Add(i + 1);
}
InitUI();
reuse = GetComp<ReuseScrollRect>("Scroll View");
reuse.Init(this);
}
public override void InitUI()
{
base.InitUI();
rect = GetComp<ScrollRect>("Scroll View");
}
public float GetItemHeight()
{
return 100;
}
public float GetItemGap()
{
return 30;
}
public int GetAllDataCount()
{
return dataCount;
}
public List<Transform> CreateUIItems(int count)
{
List<Transform> items = new List<Transform>();
itemsList.Clear();
for (int i = 0; i < count; i++)
{
GameObject go = ResourcesMgr.Instance.LoadUIPrefab("Prefabs/TestRankItem");
TestRankItemUI itemUI = go.AddComponent<TestRankItemUI>();
itemUI.transform.SetParent(rect.content, false);
itemUI.Awake();
items.Add(itemUI.gameObject.transform);
itemsList.Add(itemUI);
}
return items;
}
public void UpdateItem(int itemIndex, int dataIndex)
{
itemsList[itemIndex].UpdateUI(dataList[dataIndex]);
}
}
public class TestRankItemUI : UIBase
{
private Text dataText;
public void Awake()
{
InitUI();
}
public override void InitUI()
{
base.InitUI();
dataText = GetComp<Text>("DataText");
}
public void UpdateUI(int data)
{
dataText.text = data.ToString();
}
}