unity 常规无限滚动的使用需要注意的几个地方(坑!)

无限滚动是很常用的一个东西,平时设置好就没再管过, 偶尔用到的时候缺被几个小坑绊了一跤, 总结如下:

1,也是最基本的一点 数量要设置对;如下图

2.挂在脚本的物体要稍稍大于cell的数量

不然滑动的时候会出现各种恶心的状况 比如拉上去又弹回来什么的

3.写代码的时候 更要注意 几点:

 void CreateHBlist(Dictionary<string, string> dic)
    {
        Sql<HouseYZList>(_HttpType.Get, WD._url + "style/getStuffList", list =>
        {
            HouseYZList = list.result;
            InitHouseYZList();
        }, dic);
    }
    public List<YZInfo> HouseYZList = new List<YZInfo>();
    int amount ;//Content下最多能容纳的格子数量
    private InfinityGridLayoutGroup YZGrid;
    void InitHouseYZList()
    {
        YZGrid = gameObject.FindDeepChild("Panel_Scroll/Panel_Grid").GetComponent<InfinityGridLayoutGroup>();
        YZGrid.SetAmount(HouseYZList.Count);
        amount = HouseYZList.Count;//一定要设置这个数量!
        YZGrid.updateChildrenCallback = Update_YZGrid;
    }
    /// <summary>
    /// 无限滚动列表回调
    /// </summary>
    /// <param name="index"></param>
    /// <param name="trans"></param>
    void Update_YZGrid(int index, Transform trans)
    {
        if (amount == 0) return;
        index = index % amount;
        RawImage raw = trans.gameObject.GetComponent<RawImage>();
        DestroyImmediate(raw.texture, true);
        zz_wd_CacheManager.ins.LoadImage(HouseYZList[index].previewUrl, tex => { raw.texture = tex; });
        _3D_YZPage_Item item = trans.GetComponent<_3D_YZPage_Item>();
        item.data = HouseYZList[index];
        item.MyType = NowType;
    }

4.如果滑动的时候卡顿  可以将最大数量多设置几个 提前加载就不会卡了

最后奉上我用的这个无限滚动的代码

 

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

[RequireComponent(typeof(GridLayoutGroup))]//网格布局
[RequireComponent(typeof(ContentSizeFitter))]//自适应
public class InfinityGridLayoutGroup : MonoBehaviour 
{
    [SerializeField]
    int minAmount = 0;//实现无限滚动,需要的最少的child数量。屏幕上能看到的+一行看不到的,比如我在屏幕上能看到 2 行,每一行 2 个。则这个值为 2行*2个 + 1 行* 2个 = 6个。
    RectTransform rectTransform;
    GridLayoutGroup gridLayoutGroup;
    ContentSizeFitter contentSizeFitter;

    ScrollRect scrollRect;//滚动框

    List<RectTransform> children=new List<RectTransform>();

    Vector2 startPosition;//开始锚点位置

    int amount = 0;

    public delegate void UpdateChildrenCallbackDelegate(int index, Transform trans);
    public UpdateChildrenCallbackDelegate updateChildrenCallback = null;

    int realIndex = -1;
   // int realIndexUp = -1; //从下往上;

    bool hasInit = false;//是否初始化过
    Vector2 gridLayoutSize;
    Vector2 gridLayoutPos;
    //存储Gird的锚点集合<child的index,锚点位置>
    Dictionary<Transform, Vector2> childsAnchoredPosition = new Dictionary<Transform, Vector2>();
    //存储Gird的节点在所有兄弟节点中的位置<序号index,节点位置>
    Dictionary<Transform, int> childsSiblingIndex = new Dictionary<Transform, int>();
    IEnumerator InitChildren()
    {
        yield return 0;
        ///没有初始化进行初始化   并获得相应的组件设置锚点和调整锚点的大小增量   
        ///给父体添加滚动框组件   并给滚动框添加回调点击事件   
        ///遍历每一个孩子获取每一孩子的角标  使得每个自孩子添加<RectTransform>锚点位置组件
        ///获取Gird的锚点位置以及节点   
        if (!hasInit)//如果没有初始化
        {
            //获取Grid的宽度;
            rectTransform = GetComponent<RectTransform>();

            gridLayoutGroup = GetComponent<GridLayoutGroup>();
            gridLayoutGroup.enabled = false;
            contentSizeFitter = GetComponent<ContentSizeFitter>();
            contentSizeFitter.enabled = false;
            //锚点位置
            gridLayoutPos = rectTransform.anchoredPosition;
            //大小增量:矩阵变换的大小相对于锚点之间的距离。 
            gridLayoutSize = rectTransform.sizeDelta;
      
            //注册ScrollRect滚动回调;
            scrollRect = transform.parent.GetComponent<ScrollRect>();//给盒子的父物体添加ScrollRect滚动框组件
            scrollRect.onValueChanged.AddListener((data) => { ScrollCallback(data);});

            //获取所有child anchoredPosition 以及 SiblingIndex;
            for (int index = 0; index < transform.childCount; index++)
            {
                Transform child=transform.GetChild(index);
                RectTransform childRectTrans= child.GetComponent<RectTransform>();
                childsAnchoredPosition.Add(child, childRectTrans.anchoredPosition);

                childsSiblingIndex.Add(child, child.GetSiblingIndex());
            }
        }
        else
        {
            rectTransform.anchoredPosition = gridLayoutPos;
            rectTransform.sizeDelta = gridLayoutSize;

            children.Clear();

            realIndex = -1;
           // realIndexUp = -1;

            //children重新设置上下顺序;
            foreach (var info in childsSiblingIndex)
            {
                info.Key.SetSiblingIndex(info.Value);
            }

            //children重新设置anchoredPosition;
            for (int index = 0; index < transform.childCount; index++)
            {
                Transform child = transform.GetChild(index);
                
                RectTransform childRectTrans = child.GetComponent<RectTransform>();
                if (childsAnchoredPosition.ContainsKey(child))
                {
                    childRectTrans.anchoredPosition = childsAnchoredPosition[child];
                }
                else
                {
                    Debug.Log("childsAnchoredPosition no contain "+child.name);
                }
            }
        }

        //获取所有child;
        for (int index = 0; index < transform.childCount; index++)
        {
            Transform trans = transform.GetChild(index);
            trans.gameObject.SetActive(true);

            children.Add(transform.GetChild(index).GetComponent<RectTransform>());
            //children.Add(Transform.GetChild(index).GetChild(1).GetComponent<Text>);

            //初始化前面几个;
            UpdateChildrenCallback(children.Count - 1, transform.GetChild(index));
        }

        startPosition = rectTransform.anchoredPosition;

        realIndex = children.Count - 1;

        //Debug.Log( scrollRect.transform.TransformPoint(Vector3.zero));

       // Debug.Log(transform.TransformPoint(children[0].localPosition));

        hasInit = true;

        //如果需要显示的个数小于设定的个数;
        for (int index = 0; index < minAmount; index++)
        {
            children[index].gameObject.SetActive(index < amount);
            //Debug.Log("第" + index + "个Gird, " + "minAmount个数为" + minAmount);
        }
        //如果纵向排列 设置的最小Grid数量和所需要显示的Grid数量对比结果进行锚框的高度设置
        if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
        {
            //如果小了一行,则需要把GridLayout的高度减去一行的高度;
            int row = (minAmount - amount) / gridLayoutGroup.constraintCount;
            //Debug.Log("Row的值为" + row);
            if (row > 0)
            {
                rectTransform.sizeDelta -= new Vector2(0, (gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y) * row);
            }
        }
        //否则横向排列 对锚框进行宽的设置
        else
        {
            //如果小了一列,则需要把GridLayout的宽度减去一列的宽度;
            int column = (minAmount - amount) / gridLayoutGroup.constraintCount;
            if (column > 0)
            {
                rectTransform.sizeDelta -= new Vector2((gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x) * column, 0);
            }
        }
    }
    void ScrollCallback(Vector2 data)
    {
        UpdateChildren();
    }

    //刷新子物体
    void UpdateChildren()
    {
        //如果挂有滚动框组件物体的孩子数量小于设定的最小数量
        if (transform.childCount < minAmount) return;
        Vector2 currentPos = rectTransform.anchoredPosition;//获取当前锚点位置
        //设置滑动方向
        if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
        {
            float offsetY = currentPos.y - startPosition.y;//纵向滑动偏移量

            if (offsetY > 0)
            {
                //向上拉,向下扩展;
                {
                    if (realIndex >= amount - 1)
                    {
                        startPosition = currentPos;
                        return;
                    }
                    //**************************核心算法********************************//
                    //主要是通过获得Panel_Scroll的锚点PosY的值,这是不变的,因为开始设置的即是屏幕的高1080
                    //再通过他的子物体格子的PoxY,由于格子的锚点定的是居于Content的左上角,所以这里记录为childBottomLeft
                    //通过获得Panel_Grid.y的世界坐标Y即是Panel_Scroll的位置,此时与Panel_Scroll的恒定值1080对比
                    //通过对比childBottom与恒定的1080值
                    //当格子走过的路径大于等于1080时设置格子的层级SetAsLastSibling

                    //调整布局
                    //transform.TransformPoint 变换位置从局部坐标到世界坐标。
                    //获得Panel_Scroll的RectTransform PosY=1080
                    float scrollRectUp = scrollRect.transform.TransformPoint(Vector3.zero).y;
                    //纵向滑动改变的只是格子锚点的PosY
                    Vector3 childBottomLeft = new Vector3(children[0].anchoredPosition.x, children[0].anchoredPosition.y - gridLayoutGroup.cellSize.y, 0f);    
                    float childBottom = transform.TransformPoint(childBottomLeft).y;
                    //Debug.Log("scrollRectUp====" + scrollRectUp);
                    //Debug.Log("childBottomLeft===" + childBottomLeft);
                    //Debug.Log("childBottom====" + childBottom);
                    if (childBottom >= scrollRectUp)
                    {
                        //Debug.Log("childBottom >= scrollRectUp");

                        //移动到底部;
                        for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                        {
                            children[index].SetAsLastSibling();
                            children[index].anchoredPosition = new Vector2(children[index].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y - gridLayoutGroup.cellSize.y - gridLayoutGroup.spacing.y);
                            //Debug.Log("这是格子的PosX" + children[index].anchoredPosition.x);
                            //Debug.Log("这是格子的PosY" + children[children.Count - 1]);
                            //Debug.Log("第几个啦" + gridLayoutGroup.constraintCount);
                            realIndex++;

                            if (realIndex > amount - 1)
                            {
                                children[index].gameObject.SetActive(false);
                            }
                            else
                            {
                                UpdateChildrenCallback(realIndex, children[index]);
                            }
                        }

                        //GridLayoutGroup 底部加长;
                        rectTransform.sizeDelta += new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);

                        //更新child;
                        for (int index = 0; index < children.Count; index++)
                        {
                            children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                        }
                    }
                }
            }
            else
            {
                //Debug.Log("Drag Down");
                //向下拉,下面收缩;
                if (realIndex + 1 <= children.Count)
                {
                    startPosition = currentPos;
                    return;
                }
                RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
                Vector3 scrollRectAnchorBottom = new Vector3(0, -scrollRectTransform.rect.height - gridLayoutGroup.spacing.y, 0f);
                float scrollRectBottom = scrollRect.transform.TransformPoint(scrollRectAnchorBottom).y;

                Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);

                float childUp = transform.TransformPoint(childUpLeft).y;

                if (childUp < scrollRectBottom)
                {
                    //Debug.Log("childUp < scrollRectBottom");

                    //把底部的一行 移动到顶部
                    for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                    {
                        children[children.Count - 1 - index].SetAsFirstSibling();

                        children[children.Count - 1 - index].anchoredPosition = new Vector2(children[children.Count - 1 - index].anchoredPosition.x, children[0].anchoredPosition.y + gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);

                        children[children.Count - 1 - index].gameObject.SetActive(true);

                        UpdateChildrenCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
                    }

                    realIndex -= gridLayoutGroup.constraintCount;

                    //GridLayoutGroup 底部缩短;
                    rectTransform.sizeDelta -= new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);

                    //更新child;
                    for (int index = 0; index < children.Count; index++)
                    {
                        children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                    }
                }
            }
        }
        else
        {
            float offsetX = currentPos.x - startPosition.x;

            if (offsetX < 0)
            {
                //向左拉,向右扩展;
                {
                    if (realIndex >= amount - 1)
                    {
                        startPosition = currentPos;
                        return;
                    }

                    float scrollRectLeft = scrollRect.transform.TransformPoint(Vector3.zero).x;

                    Vector3 childBottomRight = new Vector3(children[0].anchoredPosition.x+ gridLayoutGroup.cellSize.x, children[0].anchoredPosition.y, 0f);
                    float childRight = transform.TransformPoint(childBottomRight).x;

                   // Debug.LogError("childRight=" + childRight);

                    if (childRight <= scrollRectLeft)
                    {
                        //Debug.Log("childRight <= scrollRectLeft");

                        //移动到右边;
                        for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                        {
                            children[index].SetAsLastSibling();

                            children[index].anchoredPosition = new Vector2(children[children.Count - 1].anchoredPosition.x + gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, children[index].anchoredPosition.y);

                            realIndex++;

                            if (realIndex > amount - 1)
                            {
                                children[index].gameObject.SetActive(false);
                            }
                            else
                            {
                                UpdateChildrenCallback(realIndex, children[index]);
                            }
                        }

                        //GridLayoutGroup 右侧加长;
                        rectTransform.sizeDelta += new Vector2(gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x,0);

                        //更新child;
                        for (int index = 0; index < children.Count; index++)
                        {
                            children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                        }
                    }
                }
            }
            else
            {
                //Debug.Log("Drag Down");
                //向右拉,右边收缩;
                if (realIndex + 1 <= children.Count)
                {
                    startPosition = currentPos;
                    return;
                }
                RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
                Vector3 scrollRectAnchorRight = new Vector3(scrollRectTransform.rect.width + gridLayoutGroup.spacing.x, 0, 0f);
                float scrollRectRight = scrollRect.transform.TransformPoint(scrollRectAnchorRight).x;

                Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);

                float childLeft = transform.TransformPoint(childUpLeft).x;

                if (childLeft >= scrollRectRight)
                {
                    //Debug.LogError("childLeft > scrollRectRight");

                    //把右边的一行 移动到左边;
                    for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                    {
                        children[children.Count - 1 - index].SetAsFirstSibling();

                        children[children.Count - 1 - index].anchoredPosition = new Vector2(children[0].anchoredPosition.x - gridLayoutGroup.cellSize.x - gridLayoutGroup.spacing.x,children[children.Count - 1 - index].anchoredPosition.y);

                        children[children.Count - 1 - index].gameObject.SetActive(true);

                        UpdateChildrenCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
                    }

                    //GridLayoutGroup 右侧缩短;
                    rectTransform.sizeDelta -= new Vector2(gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, 0);

                    //更新child;
                    for (int index = 0; index < children.Count; index++)
                    {
                        children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                    }

                    realIndex -= gridLayoutGroup.constraintCount;
                }
            }
        }

        startPosition = currentPos;
    }

    void UpdateChildrenCallback(int index,Transform trans)
    {
        if (updateChildrenCallback != null)
        {
            updateChildrenCallback(index, trans);
        }
    }
    /// <summary>
    /// 设置总的个数;
    /// </summary>
    /// <param name="count"></param>
    public void SetAmount(int count)
    {
        amount = count;
        StartCoroutine(InitChildren());
    }
}

 

Unity无限滚动列表通常使用ScrollRect组件来实现。以下是一个基本的步骤: 1.在场景中创建一个ScrollView,给ScrollView添加ScrollRect组件; 2.在ScrollView中创建一个Viewport,设置Viewport的Anchor为Stretch,设置Pivot为(0,1),设置Width和Height为滚动列表中的一项的大小; 3.在Viewport中创建一个Content,设置Content的Anchor为TopLeft,设置Pivot为(0,1),设置宽度为Viewport的宽度,高度为所有项的高度之和; 4.在Content中创建多个子对象,每个子对象代表列表中的一项; 5.为每个子对象添加一个LayoutElement组件,设置该组件的Preferred Height和Min Height为该项的高度; 6.编写脚本进行列表的控制,脚本需要继承自MonoBehaviour,并包含以下变量和方法: ``` [SerializeField] private GameObject listItemPrefab; // 列表项的模版 [SerializeField] private int totalItemCount; // 列表项的总数 [SerializeField] private float listItemHeight; // 列表项的高度 [SerializeField] private Transform content; // Content对象 [SerializeField] private List<GameObject> spawnedItems; // 已经生成的列表项 [SerializeField] private float spawnYPosition = 0f; // 第一个生成的列表项的y坐标 [SerializeField] private float destroyYPosition = -100f; // 超出该y坐标的列表项将被销毁 private Vector2 lastContentPos; // 上一帧Content的位置 private void Awake() { spawnedItems = new List<GameObject>(); SpawnItems(); } private void Update() { UpdateVisibleItems(); } private void SpawnItems() { float spawnXPosition = 0f; for (int i = 0; i < totalItemCount; i++) { GameObject spawnedItem = Instantiate(listItemPrefab, content); spawnedItem.transform.localPosition = new Vector2(spawnXPosition, spawnYPosition); spawnedItem.GetComponentInChildren<Text>().text = "Item " + i; spawnYPosition -= listItemHeight; spawnedItems.Add(spawnedItem); } } private void UpdateVisibleItems() { Vector2 contentPos = content.anchoredPosition; if (contentPos != lastContentPos) { float contentTopY = contentPos.y + content.rect.yMin; float contentBottomY = contentPos.y + content.rect.yMax; for (int i = 0; i < spawnedItems.Count; i++) { float itemY = spawnedItems[i].GetComponent<RectTransform>().anchoredPosition.y; if (itemY + listItemHeight < contentTopY || itemY > contentBottomY) { spawnedItems[i].SetActive(false); } else { spawnedItems[i].SetActive(true); } if (itemY < destroyYPosition && spawnedItems[i].activeSelf) { spawnedItems[i].SetActive(false); } } lastContentPos = contentPos; } } ``` 7.在脚本中添加SpawnItems和UpdateVisibleItems方法,SpawnItems用于生成所有的列表项,UpdateVisibleItems用于更新当前在ScrollView中可见的列表项; 以上就是一个基本的Unity无限滚动列表的实现,可以根据实际情况进行修改和扩展。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值