Unity 3D ScrollRect 无限循环复用

一个简单的列表,一行一个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();
    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值