Unity UGUI实现无限滚动列表

news2024/9/27 12:16:20

Demo链接​​​https://download.csdn.net/download/qq_41973169/89364284icon-default.png?t=N7T8http://Unity UGUI无限滚动列表

在游戏开发中,列表视图是一个常见的UI组件。实现一个高效的列表视图尤其重要,尤其是在需要展示大量数据时。本文将介绍如何在Unity中实现一个高效的无限滚动列表,包括两个关键脚本:InfiniteScroll 和 ListItem 两个脚本 话不多说直接上代码

Unity版本2022.3.X

InfiniteScroll.cs

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

public class InfiniteScroll : MonoBehaviour
{
    public delegate void ItemDelegate(int itemDataIndex, int slotID);

    public ItemDelegate OnUpdateItemImpl;// 更新Item回调函数

    public enum ScrollDirection
    {
        Vertical,
        Horizontal
    }

    public GameObject itemPrefab;
    [Header("横向滑动表示宽度; 纵向滑动表示高度")]
    public float itemSize;
    [Header("列表项间距")]
    public float spacing; // 列表项间距
    [Header("最大可见列表项数量")]
    public int maxVisibleItemCount;
    [Header("滑动方向")]
    public ScrollDirection scrollDirection = ScrollDirection.Vertical;

    private ScrollRect _scrollRect;
    private RectTransform _contentRectTransform;
    private int _firstVisibleIndex; // 第一个可见列表项的索引
    private int _lastVisibleIndex; // 最后一个可见列表项的索引
    private List<GameObject> _itemList; // 列表项对象列表
    private float _itemTotalSize; // 列表项总尺寸(包括间距)
    private int _totalItemDataCount; // item总共数据数量


    private void Awake()
    {
        _itemTotalSize = itemSize + spacing;
        _itemList = new List<GameObject>();
        _scrollRect = GetComponent<ScrollRect>();
        _contentRectTransform = _scrollRect.content.GetComponent<RectTransform>();
        _scrollRect.onValueChanged.AddListener(OnScrollChanged);
    }

    public void Init()
    {
        for (int i = 0; i < maxVisibleItemCount; i++)
        {
            int slotID = i + 1;
            GameObject obj = Instantiate(itemPrefab, _contentRectTransform);
            ListItem listItem = obj.GetComponent<ListItem>();
            listItem.Init(this);
            listItem.SetSlotID(slotID);

            obj.name = slotID.ToString();
            SetPivot(obj, _itemTotalSize * i);
            _itemList.Add(obj);
        }

        itemPrefab.SetActive(false);
    }

    private void SetPivot(GameObject obj, float position)
    {
        RectTransform rect = obj.GetComponent<RectTransform>();
        if (scrollDirection == ScrollDirection.Vertical)
        {
            rect.pivot = new Vector2(0.5f, 1);
            rect.anchorMax = new Vector2(0.5f, 1);
            rect.anchorMin = new Vector2(0.5f, 1);
            rect.anchoredPosition = new Vector2(0, -position);
        }
        else
        {
            rect.pivot = new Vector2(0, 0.5f);
            rect.anchorMax = new Vector2(0, 0.5f);
            rect.anchorMin = new Vector2(0, 0.5f);
            rect.anchoredPosition = new Vector2(position, 0);
        }
    }

    private void OnScrollChanged(Vector2 position)
    {
        if (scrollDirection == ScrollDirection.Vertical)
        {
            Func<bool> condition1 = () =>
            {
                // 如果Content的Y轴坐标大于上面第二个Item 并且最后的下标不是数据的最后一个则表示向下滑动可以更新Item
                return _contentRectTransform.anchoredPosition.y > (_firstVisibleIndex + 1) * _itemTotalSize && _lastVisibleIndex < _totalItemDataCount - 1;
            };
            Func<bool> condition2 = () =>
            {
                // 如果Content的Y轴坐标小于上面第一个Item 并且最上面的索引不为0 则表示向上滑动可以更新Item
                return _contentRectTransform.anchoredPosition.y < _firstVisibleIndex * _itemTotalSize && _firstVisibleIndex > 0;
            };
            UpdateItems(condition1, condition2);
        }
        else
        {
            Func<bool> condition1 = () =>
            {
                // 如果Content的X轴坐标大于右边第二个Item 并且最后的下标不是数据的最后一个则表示向右滑动可以更新Item
                return Mathf.Abs(_contentRectTransform.anchoredPosition.x) > (_firstVisibleIndex + 1) * _itemTotalSize && _lastVisibleIndex < _totalItemDataCount - 1;
            };
            Func<bool> condition2 = () =>
            {
                // 如果Content的X轴坐标小于左边第一个Item 并且最上面的索引不为0 则表示向左滑动可以更新Item
                return Mathf.Abs(_contentRectTransform.anchoredPosition.x) < _firstVisibleIndex * _itemTotalSize && _firstVisibleIndex > 0;
            };
            UpdateItems(condition1, condition2);
        }
    }

    private void UpdateItemUI(GameObject obj, int dataIndex)
    {
        ListItem listItem = obj.GetComponent<ListItem>();
        listItem.SetDataIndex(dataIndex);
        listItem.UpdateUI();
    }

    private void UpdateItems(Func<bool> condition1, Func<bool> condition2)
    {
        while (condition1())
        {
            GameObject first = _itemList[0];
            RectTransform rectTrans = first.GetComponent<RectTransform>();
            _itemList.RemoveAt(0);
            _itemList.Add(first);
            rectTrans.anchoredPosition = scrollDirection == ScrollDirection.Horizontal ?
                new Vector2((_lastVisibleIndex + 1) * _itemTotalSize, 0) :
                new Vector2(0, -(_lastVisibleIndex + 1) * _itemTotalSize);
            _firstVisibleIndex += 1;
            _lastVisibleIndex += 1;

            UpdateItemUI(first, _lastVisibleIndex + 1);
        }

        while (condition2())
        {
            GameObject last = _itemList[_itemList.Count - 1];
            RectTransform rectTrans = last.GetComponent<RectTransform>();
            _itemList.RemoveAt(_itemList.Count - 1);
            _itemList.Insert(0, last);
            rectTrans.anchoredPosition = scrollDirection == ScrollDirection.Horizontal ? 
                new Vector2((_firstVisibleIndex - 1) * _itemTotalSize, 0) :
                new Vector2(0, -(_firstVisibleIndex - 1) * _itemTotalSize);
            _firstVisibleIndex -= 1;
            _lastVisibleIndex -= 1;

            UpdateItemUI(last, _firstVisibleIndex + 1);
        }
    }

    public void RefreshList()
    {
        _firstVisibleIndex = 0;
        for (int i = 0; i < _itemList.Count; i++)
        {
            GameObject obj = _itemList[i];
            obj.SetActive(i < _totalItemDataCount);
            if (i < _totalItemDataCount)
            {
                _lastVisibleIndex = i;
                UpdateItemUI(obj, i + 1);
            }
        }

        float size = _itemTotalSize * _totalItemDataCount - spacing;
        if (scrollDirection == ScrollDirection.Vertical)
        {
            _contentRectTransform.sizeDelta = new Vector2(_contentRectTransform.sizeDelta.x, size);
        }
        else
        {
            _contentRectTransform.sizeDelta = new Vector2(size, _contentRectTransform.sizeDelta.y);
        }

        _contentRectTransform.anchoredPosition = Vector2.zero;
    }

    public void SetTotalItemDataCount(int count)
    {
        _totalItemDataCount = count;
    }
}

ListItem.cs

using UnityEngine;
using UnityEngine.UI;

public class ListItem : MonoBehaviour
{
    public Text itemText;

    private InfiniteScroll _infiniteScroll;
    private int _slotID;
    private int _dataIndex;

    public void Init(InfiniteScroll infiniteScroll)
    {
        _infiniteScroll = infiniteScroll;
    }

    public void SetSlotID(int slotID)
    {
        _slotID = slotID;
    }

    public void SetDataIndex(int dataIndex)
    {
        _dataIndex = dataIndex;
    }

    public void UpdateUI()
    {
        itemText.text = $"{_dataIndex} SlotID{_slotID}";
        if (_infiniteScroll.OnUpdateItemImpl != null)
        {
            _infiniteScroll.OnUpdateItemImpl.Invoke(_dataIndex, _slotID);
        }
        else
        {
            Debug.LogError("InfiniteScroll.OnUpdateItemImpl == null");
        }
    }
}

测试代码

MyTest.cs

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

public class MyTest : MonoBehaviour
{
    public InfiniteScroll horizontalInfiniteScroll;
    public InfiniteScroll verticalInfiniteScroll;
    void Start()
    {
        horizontalInfiniteScroll.Init();
        verticalInfiniteScroll.Init();
        horizontalInfiniteScroll.OnUpdateItemImpl = (dataIndex, slotID) =>
        {
            Debug.LogError($"horizontalInfiniteScroll dataIndex:{dataIndex}, slotID:{slotID}");
        };
        verticalInfiniteScroll.OnUpdateItemImpl = (dataIndex, slotID) =>
        {
            Debug.LogError($"verticalInfiniteScroll dataIndex:{dataIndex}, slotID:{slotID}");
        };

        horizontalInfiniteScroll.SetTotalItemDataCount(100);
        verticalInfiniteScroll.SetTotalItemDataCount(100);
        horizontalInfiniteScroll.RefreshList();
        verticalInfiniteScroll.RefreshList();
    }
}

 测试效果

场景树UI布局 

脚本挂载

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1705606.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

爬虫在金融领域的应用:股票数据收集

介绍 在金融领域&#xff0c;准确及时的数据收集对于市场分析和投资决策至关重要。股票价格作为金融市场的重要指标之一&#xff0c;通过网络爬虫技术可以高效地从多个网站获取实时股票价格信息。本文将介绍网络爬虫在金融领域中的应用&#xff0c;重点讨论如何利用Scrapy框架…

优思学院:质量工程师必备技能清单,你具备了吗?

想要了解质量工程师需要具备哪些技能和知识&#xff0c;最直接且实际的方法就是分析招聘广告中的关键词&#xff0c;这比道听途说更加有效。为此&#xff0c;优思学院搜集了大量关于质量工程师职位的招聘信息&#xff0c;并为大家进行详细分析。我们通常选择中高级职位进行分析…

颜色空间的选择

1.选择Gamma颜色空间&#xff0c;Web平台或者不支持线性空间&#xff0c;或者追求高饱和度的 2.选择Linear&#xff0c;追求真实光照和物理准确

阿贝云免费虚拟主机及免费云服务器评测

阿贝云是一家提供免费虚拟主机和免费云服务器的公司&#xff0c;其服务质量备受用户好评。用户可以通过阿贝云的网站 https://www.abeiyun.com 进行申请并获得免费服务。首先&#xff0c;我们来看看阿贝云的免费虚拟主机服务。免费虚拟主机提供了足够的存储空间和带宽&#xff…

WGCLOUD部署好后,怎么登录WGCLOUD界面

WGCLOUD的server启动完成后&#xff0c;我们在浏览器里输入URL&#xff0c;如下 http://[server主机IP]:9999 注意默认端口就是9999&#xff0c;如果修改过&#xff0c;那么把端口改成自己的实际端口 这样就可以看到登录页面了&#xff0c;默认账号密码是&#xff1a;admin/…

饮料添加剂新型褪色光照试验仪器太阳光模拟器

太阳光模拟器的定义和功能 太阳光模拟器是一种高科技设备&#xff0c;它可以模拟太阳光的光谱、光强和光照条件&#xff0c;用于实验室环境中对太阳能电池、光电器件以及其他需要太阳光条件的设备和材料进行评估。太阳光模拟器的主要功能包括模拟太阳光的光谱分布、辐照度、光…

【Mongo】索引结构

结论 Mongo3.2版本开始&#xff0c;索引的结构默认是B树。 起因 面试的时候&#xff0c;面试官问为什么Mongo DB底层使用B树而不是B树&#xff1f; 面试完赶紧恶补&#xff0c;结果发现面试官好像给我埋了个坑。。。 MongoDB官方描述&#xff1a; 翻译一下就是&#xff1…

Spark中RDD概述及RDD算子详解

一、RDD概述 1、RDD: 弹性的分布式数据集 弹性&#xff1a;RDD 中的数据即可以缓存在内存中, 也可以缓存在磁盘中, 也可以缓存在外部存储中 分布式&#xff1a;数据可以分布在多台服务器中&#xff0c;RDD中的分区来自于block块&#xff0c;而block块会来自不同的datanode 数…

大数据开发面试题【ClickHouse篇】

170、clickhouse介绍以及架构 clickhouse一个分布式列式存储数据库&#xff0c;主要用于在线分析查询 171、列式存储和行式存储有什么区别&#xff1f; 行式存储&#xff1a; 1、数据是按行存储的 2、没有建立索引的查询消耗很大的IO 3、建立索引和视图花费一定的物理空间和…

OpenAI 再次刷新认知边界:GPT-4 颠覆语音助手市场,流畅度直逼真人互动?

前言 近日&#xff0c;美国人工智能研究公司 OpenAI 发布了其最新旗舰模型 GPT-4o&#xff0c;这一革命性的进展不仅标志着人工智能领域的新突破&#xff0c;更预示着即将步入一个全新的交互时代&#xff1f;GPT-4o 的发布&#xff0c;对于我们来说&#xff0c;意味着人工智能…

分库分表最全详解(图文全面总结)

分库分表 分库分表是数据库设计、和管理中的一种策略&#xff0c;主要解决随着数据量、和并发访问量的增加而带来的性能、和扩展性问题。 分库分表&#xff0c;主要就是两种常用手段&#xff1a;“分库”、和“分表”。 如下图所示&#xff1a; 分库&#xff08;Database S…

ML307R OpenCPU 网络初始化流程介绍

一、网络初始化流程 二、函数介绍 三、示例代码 四、代码下载地址 一、网络初始化流程 模组的IMEI/SN获取接口可在include\cmiot\cm_sys.h中查看,SIM卡IMSI/ICCID获取接口可以在include\cmiot\cm_sim.h中查看,PDP激活状态查询可以在include\cmiot\cm_modem.h中查看 二、函…

#12松桑前端后花园周刊-SolidStart、Vercel融资、Angular18、Nextjs15RC、p5.js、ChromeDevTools引入AI

⚡️行业动态 SolidStart 1.0 元框架发布 Solidjs 核心团队发布其元框架 SolidStart 1.0 正式版&#xff0c;其特点如下&#xff1a;基于文件系统的路由&#xff1b;支持SSR、流式SSR、CSR、SSG渲染模式&#xff1b;通过代码分割、树摇和无用代码删除构建优化&#xff1b;基于…

大屏表格实现无限滚动效果

实现效果 实现思路 首先固定最外层的高度&#xff0c;并且设置超出高度后隐藏设置每一行的高度为固定35PX&#xff0c;默认显示10行&#xff0c;所以最外层高度就是 35 * 10 表头的高度遍历时克隆一份表格数据&#xff0c;用于视差效果显示设置滚动动画&#xff0c;让表格行所…

docker image分析利器之dive

dive是一个用于研究 Docker 镜像、层内容以及发现缩小 Docker/OCI 镜像大小方法的开源工具. 开源地址: dive github 为了有个直观的印象, 可以先看一下repo文档中的gif图: 安装 在Ubuntu/Debian系统下&#xff0c;可以使用deb包安装: DIVE_VERSION$(curl -sL "https:/…

Transformer模型的简单学习

前言 Transformer 来源于一篇论文&#xff1a;Attention is all you need TRM在做一件什么事情呢&#xff1f;其实一开始它是被用于机器翻译的&#xff1a; 更详细的&#xff1a; 更详细的&#xff1a; 从上图可以看出&#xff0c;一个Encoders 下面包含了 n 个 Encoder&…

Python Anaconda环境复制

虚拟环境复制 conda-pack 第一种方式 conda打包 在打包之前如果没有conda-pack包的话&#xff0c;需要安装pip install conda-pack打包 conda pack -n py36 -o py366.tar.gz -o就是给导出得到的压缩包就在当前目录下 传输到另外一台服务器上 有两台linux服务器&#xff0c…

详析河南道路与桥梁乙级资质新办条件

河南道路与桥梁乙级资质新办条件详析如下&#xff1a; 一、企业基本条件 独立企业法人资格&#xff1a; 申请人必须是具有独立企业法人资格的单位。注册资金&#xff1a; 企业的注册资金应不少于100万元人民币。社会信誉&#xff1a; 申请人应具有良好的社会信誉&#xff0c;无…

RunnerGo V4.6.0 多项新增功能,快看看有没有你想要的!

RunnerGo V4.6.0版本上线&#xff0c;不仅对现有功能进行了深度优化和改进&#xff0c;还带来了诸多新功能。 UI 插件&#xff1a;浮窗升级&#xff0c;优化浏览体验 此次更新中&#xff0c;UI插件全新升级至V2.1版本。新版取消了页面内右下角按钮的设计&#xff0c;在浏览器右…

postman调用Grpc

环境&#xff1a; .net6.0 一、准备 安装nuget&#xff1a; Grpc.AspNetCore Google.Protobuf Grpc.Core.Api Grpc.Tools Grpc.AspNetCore.Server.Reflection Program.cs&#xff1a; public class Program{public static void Main(string[] args){var builder WebApplicat…