Unity3d实用无限滚动

参考网上很多的代码修改的
上代码

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[DisallowMultipleComponent]
[RequireComponent(typeof(ScrollRect))]
//Content 上不用加任何组件 
public class SuperScrollView : MonoBehaviour
{
    public int space;//间距
    public GameObject itemPrefab;//预制 
    public string nameHead = "";
    private enum ScrollRect_Direction : byte
    {
        Horizontal,
        Vertical
    }
    private ScrollRect_Direction m_Direction = ScrollRect_Direction.Horizontal;  //拖动方向. 
    private float itemHeight;//item的高度(全都一样)
    private float itemWidth;
    private ScrollRect mScrollRect;
    private RectTransform mContentRect;
    private Action<GameObject, int> itemUpdateCallBack;
    private List<GameObject> gameObjectList;
    private int datasNum;//当前数据数量
    //记录最上和最下的item索引
    private int firstIndex;
    private int lastIndex;
    public List<GameObject> itemList;
    private int needItemNum = 0;
    private int calItemNum = 0;
    private bool ready = false;
    private bool inited = false;
    private int reqItemNum = 2; 
    private void Awake()
    {
        if (!inited) Init();
    }

    private void Init()
    {
        itemList = new List<GameObject>();
        gameObjectList = new List<GameObject>();
        mScrollRect = transform.GetComponent<ScrollRect>();
        mContentRect = mScrollRect.content.transform.GetComponent<RectTransform>();
        mScrollRect.onValueChanged.AddListener((Vector2 vec) => OnScrollMove(vec));
        itemHeight = itemPrefab.GetComponent<RectTransform>().rect.height;
        itemWidth = itemPrefab.GetComponent<RectTransform>().rect.width; 
        if (nameHead == "")
        {
            nameHead = itemPrefab.name;
        }
        inited = true;
    }

    public void SetUpdateItemFunc(Action<GameObject, int> callBack)
    {
        itemUpdateCallBack = callBack;
    }

    private void UpdateSize()
    {
        if (mScrollRect.horizontal && !mScrollRect.vertical)
        {
            calItemNum = Math.Abs((int)Math.Floor(this.GetComponent<RectTransform>().rect.size.x / itemWidth)) + 2;
            m_Direction = ScrollRect_Direction.Horizontal;
        }
        else if (!mScrollRect.horizontal && mScrollRect.vertical)
        {
            m_Direction = ScrollRect_Direction.Vertical;
            calItemNum = Math.Abs((int)Math.Floor(this.GetComponent<RectTransform>().rect.size.y / itemHeight)) + 2;
        }
        else
        {
            Debug.LogError("not support both scroll type");
        }
    }

    public void InitScrollViewData(int dataCount)
    {
        if (!inited) Init();
        UpdateSize();
        datasNum = dataCount; 
        if (m_Direction == ScrollRect_Direction.Vertical)
        {
            mContentRect.sizeDelta = new Vector2(mContentRect.sizeDelta.x, (itemHeight + space) * datasNum);
        }
        else if (m_Direction == ScrollRect_Direction.Horizontal)
        {
            mContentRect.sizeDelta = new Vector2((itemWidth + space) * datasNum, mContentRect.sizeDelta.y);
        }
        if (dataCount == 0)
        {
            for (int i = 0; i < itemList.Count; i++)
            {
                ReturnGameObjectToPool(itemList[i].gameObject);
            }
        }
        else
        {
            FreshItemChilds();
        }
    }
    //fresh item num
    private void FreshItemChilds()
    {
        ready = false;
        if (calItemNum > datasNum)
        { //自动计算的数量比数据个数多,只需要实例化数据个数的物体
            needItemNum = datasNum;
        }
        else
        {//否则按照自动计算的个数实例化物体
            needItemNum = calItemNum;
        }

        for (int i = 0; i < itemList.Count; i++)
        {
            ReturnGameObjectToPool(itemList[i].gameObject);
        }
        firstIndex = 0;
        itemList.Clear();
        for (int i = 0; i < needItemNum; i++)
        {
            GameObject obj = GetGameObjectFromPool();
            if (obj == null)
            {
                obj = Instantiate(itemPrefab);
            }
            obj.transform.SetParent(mContentRect.transform);
            obj.name = nameHead + i.ToString();
            RectTransform rectTrans = obj.GetComponent<RectTransform>();
            rectTrans.localScale = Vector3.one;
             
            if (m_Direction == ScrollRect_Direction.Vertical)
            {
                rectTrans.anchorMax = new Vector2(0.5f, 1);
                rectTrans.anchorMin = new Vector2(0.5f, 1);
                rectTrans.pivot = new Vector2(0.5f, 1);
                rectTrans.anchoredPosition = new Vector2(0, -(itemHeight + space) * i);
            }
            else if (m_Direction == ScrollRect_Direction.Horizontal)
            {
                rectTrans.anchorMax = new Vector2(0, 1);
                rectTrans.anchorMin = new Vector2(0, 1);
                rectTrans.pivot = new Vector2(0f, 1f);

                rectTrans.anchoredPosition = new Vector2((itemWidth + space) * i, 0);
            }
            obj.gameObject.SetActive(true);
            lastIndex = i;
            itemList.Add(obj);
            itemUpdateCallBack(obj.gameObject, i);
        }
        ready = true;
    }

    private void OnScrollMove(Vector2 pVec)
    {
        if (!ready) return;
        if (m_Direction == ScrollRect_Direction.Horizontal)//左右滚动
        { 
            Debug.Log(mContentRect.anchoredPosition.x + " " + firstIndex + " " + lastIndex);
            if (mContentRect.anchoredPosition.x < -(firstIndex + 1) * (itemWidth + space) && lastIndex != datasNum - 1)
            {
                GameObject first = itemList[0];
                RectTransform rectTrans = first.GetComponent<RectTransform>(); 
                itemList.RemoveAt(0);
                itemList.Add(first); 
                rectTrans.anchoredPosition = new Vector2((lastIndex + 1) * (itemWidth + space), 0);
                firstIndex += 1;
                lastIndex += 1;
                first.name = nameHead + lastIndex.ToString();
                itemUpdateCallBack(first.gameObject, lastIndex);
            }
            if (mContentRect.anchoredPosition.x > -firstIndex * (itemWidth + space) && firstIndex != 0)
            {
                GameObject last = itemList[itemList.Count - 1];
                RectTransform rectTrans = last.GetComponent<RectTransform>();
                itemList.RemoveAt(itemList.Count - 1);
                itemList.Insert(0, last);
                rectTrans.anchoredPosition = new Vector2((firstIndex - 1) * (itemWidth + space), 0);
                firstIndex -= 1;
                lastIndex -= 1;
                last.name = nameHead + firstIndex.ToString();
                itemUpdateCallBack(last.gameObject, firstIndex);
            }
            return;
        }
         //向上滚动,视口的y坐标大于当前第一个序号*子物体高度得出对应高度值
        if (mContentRect.anchoredPosition.y > (firstIndex + 1) * (itemHeight + space) && lastIndex != datasNum - 1)
        {
            int step = Mathf.CeilToInt((mContentRect.anchoredPosition.y - (firstIndex + 1) * (itemHeight + space)) / (itemHeight + space));
            for (int i = 0; i < step; i++)
            {
                GameObject first = itemList[0];
                RectTransform rectTrans = first.GetComponent<RectTransform>();
                //将首个物体移出List,再添加到List最后端
                itemList.RemoveAt(0);
                itemList.Add(first);
                //将这个物体移到最下方
                rectTrans.anchoredPosition = new Vector2(0, -(lastIndex + 1) * (itemHeight + space));
                firstIndex += 1;
                lastIndex += 1;
                first.name = nameHead + lastIndex.ToString();
                itemUpdateCallBack(first.gameObject, lastIndex);
                if (lastIndex >= datasNum - reqItemNum)
                {
                    if (mReqType == DragEvent.Down)
                    {
                        reqFunc?.Invoke();
                    }
                    break;
                }
            }
        }
        //向下滚动
        if (mContentRect.anchoredPosition.y < firstIndex * (itemHeight + space) && firstIndex != 0)
        {
            int step = Mathf.CeilToInt(((firstIndex + 1) * (itemHeight + space) - mContentRect.anchoredPosition.y) / (itemHeight + space));
            for (int i = 0; i < step; i++)
            {
                GameObject last = itemList[itemList.Count - 1];
                RectTransform rectTrans = last.GetComponent<RectTransform>();
                itemList.RemoveAt(itemList.Count - 1);
                itemList.Insert(0, last);
                rectTrans.anchoredPosition = new Vector2(0, -(firstIndex - 1) * (itemHeight + space));
                firstIndex -= 1;
                lastIndex -= 1;
                last.name = nameHead + firstIndex.ToString();
                itemUpdateCallBack(last.gameObject, firstIndex);
                if (firstIndex <= reqItemNum)
                {
                    if (mReqType == DragEvent.Up)
                    {
                        reqFunc?.Invoke();
                    }
                    break;
                }
            }
        }
    }

    private Utils.Util.NoneParamFunction reqFunc;
    private DragEvent mReqType;
    //1-到顶请求 2- 到底请求
    public void SetReqDataEvent(DragEvent reqType, Utils.Util.NoneParamFunction callback, int reqNum)
    {
        mReqType = reqType;
        reqFunc = callback;
        reqItemNum = reqNum;
    }

    public void TopOnIndex(int targetIndex, bool center = false, Utils.Util.NoneParamFunction callback = null)
    {
        Utils.Util.DelayExecute(() =>
        { 
            if (firstIndex == targetIndex || targetIndex >= datasNum || (datasNum - firstIndex) < needItemNum)
            {//就在第一;数组越界;下面不够移动,上面不多(比如最后一个不能移动到最上面)
             //Debug.LogError("不够移动" + firstIndex + " -" + targetIndex + " =" + datasNum + " ..>" + needItemNum); 
             //不需要移动的情况直接执行回调
                callback?.Invoke();
                return;
            }
            float halfSize = 0;
            if (needItemNum % 2 == 0 && center)
            {
                halfSize = (itemHeight + space) / 2;
            }
            mContentRect.GetComponent<RectTransform>().localPosition = new Vector3(mContentRect.GetComponent<RectTransform>().localPosition.x, (targetIndex + 1) * (itemHeight + space) + halfSize, mContentRect.GetComponent<RectTransform>().localPosition.z);
            callback?.Invoke();
        }, () =>
        {
            return ready;
        });
    }

    public void CenterOnIndex(int targetIndex, Utils.Util.NoneParamFunction callback = null)
    {
        TopOnIndex(targetIndex - needItemNum / 2 + 1, true, callback);
    }

    private void ReturnGameObjectToPool(GameObject obj)
    {
        if (gameObjectList.IndexOf(obj) == -1)
        {
            obj.transform.parent = null;
            obj.gameObject.SetActive(false);
            gameObjectList.Add(obj);
        }
        else
        {
            obj.gameObject.SetActive(false);
        }
    }

    private GameObject GetGameObjectFromPool()
    {
        if (gameObjectList.Count > 0)
        {
            GameObject obj = gameObjectList[gameObjectList.Count - 1];
            gameObjectList.RemoveAt(gameObjectList.Count - 1);
            return obj;
        }
        return null;
    }

    public void ForceFresh()
    {
        int num = mContentRect.childCount;
        for (int i = 0; i < num; i++)
        {
            GameObject c = mContentRect.GetChild(i).gameObject;
            itemUpdateCallBack(c, firstIndex + i);
        }
    }
}

好了,就这样.

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值