unity的ugui-8.scroll view无限循环列表

Posted mr.chenyuelin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity的ugui-8.scroll view无限循环列表相关的知识,希望对你有一定的参考价值。

操作

之前文章有,简单写一下
新建scroll view,,,取消水平滑动

将viewport-》content修改至如下面版,间距可以自己调
在这里插入图片描述
在content下新建image,调至合适大小,做成预制体,注意观察预制体的rectTransfrom有没有变动,有的话改一下

用到的api

public void GetWorldCorners(Vector3[] fourCornersArray);
可以得到ui的recttransfrom的矩形区域的4个点坐标
在这里插入图片描述
transfrom.childCount
得到该物体下子物体的个数

gameObject.activeSelf
该物体是否激活

obj.transform.SetAsFirstSibling();
将obj物体设为父目录下的第一个

transform.localPosition
是相对于父物体的坐标

思路

无限循环,比如我们100个item页面,就实例化100个物体,那性能肯定直线下降,item是image

结果:当1000个item页面,我们实例化的物体都是固定的

比如1个content里有5个item子页面,当往下滑的时候,在第5个下面创建第6个item,而第一个item在慢慢的被隐藏,当第一个被隐藏时,而是将第一个item作为第7个的item,而不是重新创建。

总之来说:头部隐藏,加尾部。尾部隐藏,加头部。(核心

好的,你已经学会了,哈哈哈

代码部分

在scrollView上挂载LoopScrollView脚本其它脚本不用挂载

loopItem脚本:当的item页面在content对应位置所做的4种操作,这个脚本是在子物体身上的
一张图:
在这里插入图片描述
LoopScrollView脚本:主要是4种操作的功能实现以及数初始化
DataAdapter脚本:获取数据和移除数据
LoopDataItem脚本:存数据

代码较多,量力而行

LoopItem.cs

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

public class LoopItem : MonoBehaviour
{
    private RectTransform rect;
    private RectTransform parent;
    private Vector3[] rectCorners;
    private Vector3[] parentCorners;

    public Action onAddHead;
    public Action onRemoveHead;
    public Action onAddEnd;
    public Action onRemoveEnd;
    // Start is called before the first frame update
    void Start()
    {
        rect = transform.GetComponent<RectTransform>();
        parent = transform.GetComponentInParent<ScrollRect>().GetComponent<RectTransform>();
        if(parent==null)
        {
            throw new Exception("找不到父亲");
        }
        rectCorners = new Vector3[4];
        parentCorners = new Vector3[4];
    }

    private void Update()
    {
        LinstenerCorner();
    }
    // Update is called once per frame
    public void LinstenerCorner()
    {
        rect.GetWorldCorners(rectCorners);

        parent.GetWorldCorners(parentCorners);
        //头部
        if(isFirst())
        {
            //添加
            if(rectCorners[1].y < parentCorners[1].y)
            {
                //onaddhead不为空则调用
                onAddHead?.Invoke();
            }
            //去除
            if(rectCorners[0].y > parentCorners[1].y)
            {
                onRemoveHead?.Invoke();
            }
        }
        //尾部
        if(isLast())
        {
            
            //去除
            if (rectCorners[1].y < parentCorners[0].y)
            {
                onRemoveEnd?.Invoke();
            }
            //添加
            if (rectCorners[0].y > parentCorners[0].y)
            {
                onAddEnd?.Invoke();
            }
        }
    }

    public bool isFirst()
    {
        for (int i = 0; i < transform.parent.childCount; i++)
        {
            if(transform.parent.GetChild(i).gameObject.activeSelf)
            {
                //第一个是不是自己
                if(transform.parent.GetChild(i)==transform)
                    return true;
                break;
            }
        }
        return false;
    }

    public bool isLast()
    {

        for (int i = transform.parent.childCount - 1; i >= 0; i--)
        {
            if (transform.parent.GetChild(i).gameObject.activeSelf)
            {
                //最后一个
                if (transform.parent.GetChild(i) == transform)
                    return true;
                break;
            }
        }
        return false;
    }
}

LoopScrollView.cs

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

public class LoopScrollView : MonoBehaviour
{
    public GameObject childItem;

    private RectTransform content;

    private GridLayoutGroup grid;

    private ContentSizeFitter sizeFitter;

    private DataAdapter<LoopDataItem> dataAdapter;
    // Start is called before the first frame update
    private void Awake()
    {
        
        content = transform.Find("Viewport/Content").GetComponent<RectTransform>();
        if (content == null)
        {
            throw new System.Exception("没找到content");
        }
        grid = content.GetComponent<GridLayoutGroup>();
        sizeFitter = content.GetComponent<ContentSizeFitter>();

        dataAdapter = new DataAdapter<LoopDataItem>();
        //测试数据

        List<LoopDataItem> loopDatas = new List<LoopDataItem>();
        for (int i = 0; i < 100; i++)
        {
            loopDatas.Add(new LoopDataItem(i));
        }
        dataAdapter.InitData(loopDatas);
    }
    void Start()
    {
        OnAddHead();
        //延迟
        Invoke("EnableFalseGrid", 0.1f);

    }

    // Update is called once per frame
    void Update()
    {
       
    }
    //获取子节点
    public GameObject GetChildItem() 
    {
        //有则显示
        for (int i = 0; i < content.childCount; i++)
        {
            if(!content.GetChild(i).gameObject.activeSelf)
            {
                content.GetChild(i).gameObject.SetActive(true); 
                return content.GetChild(i).gameObject;
            }
        }
        //没有则实例化,父物体为content下创建
        GameObject obj = GameObject.Instantiate(childItem, content.transform);
   
        LoopItem loopItem = obj.AddComponent<LoopItem>();
        loopItem.onAddHead += OnAddHead;
        loopItem.onRemoveHead += OnRemoveHead;
        loopItem.onAddEnd += OnAddLast;
        loopItem.onRemoveEnd += OnRemoveLast;
        return obj;
    }

    public void OnAddHead()
    {
        LoopDataItem loopDataItem = dataAdapter.GetHeadData();
        if(loopDataItem!=null)
        {
            Transform first = FindFirst();
            GameObject obj = GetChildItem();
            //设为子物体第一个
            obj.transform.SetAsFirstSibling();
            //设置数据
            SetData(obj, loopDataItem);
            if (first != null)
            {
                //print(first.localPosition);          
                obj.transform.localPosition = first.localPosition + new Vector3(0, grid.cellSize.y + grid.spacing.y, 0);
            }
        }
    }
    public void OnRemoveHead()
    {
        if(dataAdapter.RemoveHeadData())
        {
            Transform first = FindFirst();
            if (first != null)
            {
                first.gameObject.SetActive(false);
            }
        }     
    }
    public void OnAddLast()
    {
        LoopDataItem loopDataItem = dataAdapter.GetLastData();
        if (loopDataItem != null)
        {
            Transform last = FindLast();

            GameObject obj = GetChildItem();
            //设为子物体第一个
            obj.transform.SetAsLastSibling();
            SetData(obj, loopDataItem);
            if (last != null)
            {
                obj.transform.localPosition = last.localPosition - new Vector3(0, grid.cellSize.y + grid.spacing.y, 0);
            }
            //增加conten高度
            if (IsNeedAddContentHeight(obj.transform))
            {
                content.sizeDelta += new Vector2(0, grid.cellSize.y + grid.spacing.y);
            }
        }
       
    }
    public void OnRemoveLast()
    {
        if(dataAdapter.RemoveLastData())
        {
            Transform last = FindLast();
            if (last != null)
            {
                last.gameObject.SetActive(false);
            }
        }      
    }

    public Transform FindFirst()
    {
        for (int i = 0; i < content.childCount; i++)
        {
            if(content.GetChild(i).gameObject.activeSelf)
            {
               
                return content.GetChild(i);
            }
        }
        return null;
    }

    public Transform FindLast()
    {
        for (int i = content.childCount - 1; i >= 0; i--)
        {
            if (content.GetChild(i).gameObject.activeSelf)
            {
                return content.GetChild(i);
            }
        }
        return null;
    }

    public void EnableFalseGrid()
    {
        grid.enabled = false;
        sizeFitter.enabled = false;
    }
    //是否下拉提升content高度
    public bool IsNeedAddContentHeight(Transform trans)
    {
        Vector3[] rectCorners = new Vector3[4];
        Vector3[] contentCorners = new Vector3[4];
        trans.GetComponent<RectTransform>().GetWorldCorners(rectCorners);
        content.GetWorldCorners(contentCorners);
        if(rectCorners[0].y<contentCorners[0].y)
        {
            return true;
        }
        return false;
    }
    //设置数据
    public void SetData(GameObject obj,LoopDataItem data)
    {
        obj.transform.Find("Text").GetComponent<Text>().text = data.id.ToString();
    }

}

DataAdapter.cs

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

public class DataAdapter<T>
{
    private List<T> allContent=new List<T>();
    private LinkedList<T> currentContent=new LinkedList<T>();

    public void InitData(T[] t)
    {
        allContent.Clear();
        currentContent.Clear();
        allContent.AddRange(t);
    }
    public void InitData(List<T> t)
    {
        InitData(t.ToArray());       
    }
    public void AddData(T[] t)
    {
        allContent.AddRange(t);
    }
    public void AddData(List<T> t)
    {
        AddData(t.ToArray());
    }
    public T GetHeadData()
    {
        
        if(allContent.Count==0)
        {
            return default;
        }
        if(currentContent.Count==0)
        {
            T head = allContent[0];
            currentContent.AddFirst(head);
            return head;
        }
        //获取当前第一个数据的上一个
        T t = currentContent.First.Value;
        int index = allContent.IndexOf(t);
        if(index!=0)
        {
            T head = allContent[index - 1];
            //加到当前显示数据中
            currentContent.AddFirst(head);
            return head;
        }

        return default(T);
    }
    //移除当前数据的第一个
    public bool RemoveHeadData()
    {
        //等于1的时候也不能移除,不然移除后,界面没有东西就无法加头和加尾了
        if(currentContent.Count!=0&&currentContent.Count!=1)
        {
            currentContent.RemoveFirst();
            return true;
        }
        return false;
    }
    //获取当前数据最后一个的下一个
    public T GetLastData()
    {
        if(allContent.Count==0)
        {
            return default;
        }
        if(currentContent.Count==0)
        {
            T last = allContent[0];
            currentContent.AddLast(last);
            return last;
        }
        T t = currentContent.Last.Value;
        int index = allContent.IndexOf(t);
        if(index!=allContent.Count-1)
        {
            T last = allContent[index + 1];
            currentContent.AddLast(last);
            return last;
        }
        return default;
    }
    //移除当前数据的最后一个
    public bool RemoveLastData()以上是关于unity的ugui-8.scroll view无限循环列表的主要内容,如果未能解决你的问题,请参考以下文章

Unity 中的 AI 角色与 Photon View

Unity Scroll View图文混编并自适应范围

Unity3D 灵巧小知识点☀️ | Unity UGUI组件Scroll View禁止 左右 或 上下 滑动

Unity工程无代码化

Unity 实用工具✨| Unity 十款 浏览器相关插件 整理(web view / browser)

unity 登录注册页面数据传递(无数据库)