Unity实现TableView

news2025/3/17 1:08:01

基于Scrollview封装的TableView,实现对视野外的Cell回收利用,减少创建Cell的开销。

核心逻辑如下:

/***************************************
    动态使用cell核心逻辑开始
**************************************/

//计算所有cell的坐标信息
private void CaculateCellPosition()
{
    int cellsCount = _dataSource.NumberOfCellsInTableView(this);
    if (cellsCount > 0)
    {
        _cellsPositionsList.Clear();

        float currentPos = 0;
        for (int i = 0; i < cellsCount; i++)
        {
            _cellsPositionsList.Add(currentPos);
            float rowSize = _dataSource.TableCellSizeForIndex(this, i);
            currentPos += rowSize;
        }
        _cellsPositionsList.Add(currentPos);
    }
}

//更新内嵌容器的size
private void UpdateContentSize()
{
    int cellsCount = _dataSource.NumberOfCellsInTableView(this);
    if (cellsCount > 0)
    {
        float maxPosition = _cellsPositionsList[cellsCount];
        if (IsHorizontal)
        {
            if (maxPosition > TableViewSize.width)
            {
                InnerContainerSizeDelta = maxPosition - TableViewSize.width;
            }
        }
        else 
        {
            InnerContainerSizeDelta = maxPosition;
        }
    }
}

//获取指定位置对应cell的索引
private int GetIndexFromOffset(float offset)
{
    int index = 0;
    int maxIdx = _dataSource.NumberOfCellsInTableView(this) - 1;
    index = this.CaculateIndexFromOffset(offset);
    if (index != INVALID_INDEX)
    {
        index = Math.Max(0, index);
        if (index > maxIdx)
        {
            index = INVALID_INDEX;
        }
    }
    return index;
}

//计算指定位置对应cell的索引
private int CaculateIndexFromOffset(float offset)
{
    int low = 0;
    int high = _dataSource.NumberOfCellsInTableView(this) - 1;
    float search = offset;

    while (high >= low)
    {
        int index = low + (high - low) / 2;
        float cellStart = _cellsPositionsList[index];
        float cellEnd = _cellsPositionsList[index + 1];

        if (search >= cellStart && search <= cellEnd)
        {
            return index;
        }
        else if (search < cellStart)
        {
            high = index - 1;
        }
        else
        {
            low = index + 1;
        }
    }

    if (low <= 0)
    {
        return 0;
    }
    return INVALID_INDEX;
}

//更新视野内cell的显示
private void UpdateCellsShow()
{
    int countOfItems = _dataSource.NumberOfCellsInTableView(this);
    if (0 == countOfItems)
    {
        return;
    }

    int startIdx = 0, endIdx = 0, idx = 0, maxIdx = 0;
    float offset = this.GetContentOffset();
    maxIdx = Mathf.Max(countOfItems - 1, 0);

    endIdx = this.GetIndexFromOffset(offset);
    if (endIdx == INVALID_INDEX)
    {
        endIdx = countOfItems - 1;
    }

    offset -= IsHorizontal ? -TableViewSize.width : this.TableViewSize.height;
    startIdx = this.GetIndexFromOffset(offset);
    if (startIdx == -1)
    {
        startIdx = countOfItems - 1;
    }

    if (IsHorizontal)
    {
        //横向与纵向相反
        int tmp = startIdx;
        startIdx = endIdx;
        endIdx = tmp;
    }

    //--
    _usingCellsList.Sort(new TableViewCellComparer());
    //--检测可回收的cell--BEGIN
    if (_usingCellsList.Count > 0)
    {
        var cell = _usingCellsList[0];
        idx = cell.Index;

        while (idx < startIdx)
        {
            this.MoveCellOutOfSight(cell);
            if (_usingCellsList.Count > 0)
            {
                cell = _usingCellsList[0];
                idx = cell.Index;
            }
            else
            {
                break;
            }
        }
    }
    if (_usingCellsList.Count > 0)
    {
        var cell = _usingCellsList[_usingCellsList.Count - 1];
        idx = cell.Index;

        while (idx <= maxIdx && idx > endIdx)
        {
            this.MoveCellOutOfSight(cell);
            if (_usingCellsList.Count > 0)
            {
                cell = _usingCellsList[_usingCellsList.Count - 1];
                idx = cell.Index;
            }
            else
            {
                break;
            }
        }
    }
    //--检测可回收的cell--END

    for (int i = startIdx; i <= endIdx; i++)
    {
        if (_cellUsingIdxs.Contains(i))
        {
            continue;
        }
        this.UpdateCellByIndex(i);
    }
}
//更新指定cell的显示
private void UpdateCellByIndex(int index)
{
    TableViewCell cell = _dataSource.TableCellAtIndex(this, index);
    cell.SetIndex(index);
    cell.ClickEvent.RemoveListener(CellDidClick);
    cell.ClickEvent.AddListener(CellDidClick);
    if (cell.gameObject.activeSelf == false)
    {
        cell.gameObject.SetActive(true);
    }
    //--
    float cellSize = _dataSource.TableCellSizeForIndex(this, index);
    Vector2 pos = new Vector2();
    if (IsHorizontal)
    {
        pos.x = _cellsPositionsList[index] - InnerContainerSizeDelta * 0.5f - 0.5f * TableViewSize.width + cellSize * 0.5f;
        pos.y = 0;
    }
    else 
    {
        pos.x = 0;
        pos.y = _cellsPositionsList[index] - InnerContainerSizeDelta * 0.5f + cellSize * 0.5f;
    }
    cell.gameObject.GetComponent<RectTransform>().anchoredPosition = pos;
    //--
    _usingCellsList.Add(cell);
    _cellUsingIdxs.Add(cell.Index);
}

//回收视野外的cell
private void MoveCellOutOfSight(TableViewCell cell)
{
    _freedCellsStack.Push(cell);
    _usingCellsList.Remove(cell);
    _cellUsingIdxs.Remove(cell.Index);

    cell.ClickEvent.RemoveListener(CellDidClick);
    cell.ResetCell();
}

/***************************************
    动态使用cell核心逻辑结束
**************************************/

如何使用呢?按照下面的流程操作即可。

1.创建Test 脚本,脚本继承ITableViewDataSource并实现对应的方法。【ITableViewDelegate根据具体情况决定继承与否】
 public interface ITableViewDataSource
 {
    int NumberOfCellsInTableView(TableView tableView);
    float TableCellSizeForIndex(TableView tableView, int index);
    TableViewCell TableCellAtIndex(TableView tableView, int index);
 }
 public class Test : MonoBehaviour, ITableViewDataSource, ITableViewDelegate
    {
        public TableView tableView;
        public GameObject hCell = null;
        public GameObject vCell = null;
        void Start()
        {

        }

        public int NumberOfCellsInTableView(TableView tableView)
        {
            return 20;
        }

        public float TableCellSizeForIndex(TableView tableView, int index)
        {
            return 90;
        }

        public TableViewCell TableCellAtIndex(TableView tableView, int index)
        {
            TableViewCell cell = tableView.ReusableCell();
            if (cell == null)
            {
                GameObject obj = Instantiate(tableView.IsHorizontal ? hCell : vCell, tableView.InnerContainerContent().transform);
                cell = obj.GetComponent<TableViewCell>();
            }
            cell.name = "Cell " + index;
            return cell;
        }

        public void TableCellClicked(TableView tableView, int index)
        {
            Debug.Log("TableViewDidSelectCellForRow : " + index);
        }
    }
 2.脚本挂载到载体,并绑定对应的变量【HCell和VCell只是为了方便测试横/纵向滚动】

 3.创建TestCell脚本,并继承TableViewCell
  public class TestCell : TableViewCell
  {
      public Text txt;

      public override void UpdateDisplay()
      {
          txt.text = "Index " + Index;
      }
  }
 4.将TestCell脚本挂载到Scrollview中显示的预制体上
 5.Test脚本中设置TableView的必要属性,调用ReloadData()接口
void Start()
{
     tableView.Delegate = this;
     tableView.DataSource = this;
     tableView.ReloadData();
}

效果如下:  

u3d-demo

项目地址:https://github.com/jjinglover/UnityTableView

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

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

相关文章

《python编程从入门到实践》day38

# 昨日知识点回顾 定义、迁移模型Entry # 今日知识点学习 18.2.7 Django shell 每次修改模型后&#xff0c;看到重启后的效果需要重启shell&#xff0c;退出shell会话Windows系统按ctrlZ或者输入exit() 18.3 创建页面&#xff1a;学习笔记主页 创建页面三阶段&#xf…

【Windows系统】解决Intel 6代CPU安装win7系统过程中无法操作键盘鼠标的问题

问题 微软表示&#xff0c;从 2016 年 7 月 17 日起&#xff0c;新的 Intel、AMD 和Qualcomm 处理器将仅支持 Windows 10&#xff0c;不再支持 Windows 7 和 8.1。因此&#xff0c;Intel 6代以后的CPU因为没有USB驱动无法完成win7系统的安装。 下文核心思想是通过老毛桃PE系统…

AI PC 的曙光:微软大胆出击与苹果竞争

AI PC 的曙光&#xff1a;微软大胆出击与苹果竞争 AI PC 的曙光&#xff1a;微软大胆出击与苹果竞争 概述 微软已正式进入 AI PC 时代&#xff0c;并且毫不避讳地直接向苹果的 MacBook 发起攻击。随着代号为“Copilot”的笔记本电脑的推出&#xff0c;微软准备彻底改变我们与…

Android 实现竖排文本(垂直方向显示)

Android 实现竖排文本-垂直方向显示 前言效果图代码实现方式一 Custom View1. 自定义视图 VerticalTextView2. 在xml布局文件中使用3. 设置文本内容 方式二 使用 TextView 的 rotation属性方式三 使用带有跨距文本的TextView1. 自定义视图 VerticalTextView2. 在xml布局文件中使…

ML307R OpenCPU UART使用

一、串口使用流程图 二、串口相关函数介绍 三、实现串口收发 一、串口使用流程图 OneMO ML307R模组提供了2路UART给开发者用于通讯开发&#xff0c;以及1路DBG UART用于log的打印。UART Demo示例可以在SDK&#xff1a;examples\uart\src\cm_demo_uart.c中查看。 串口使用流…

视频号小店的保证金是多少钱?2024最新收费标准,一篇了解!

哈喽~我是电商月月 现实社会&#xff0c;干什么都需要交钱&#xff0c;就连上班&#xff0c;路费也得掏钱 想要入驻视频号小店&#xff0c;在视频号里卖货赚钱&#xff0c;就要缴纳类目保证金 那到底要缴多少钱呢&#xff1f; 今天&#xff0c;月月就把最新的收费标准分享给…

Android Low Storage机制之DeviceStorageMonitorService

一、Android 版本 Android 13 二、low storage简介(DeviceStorageMonitorService) 设备存储监视器服务是一个模块&#xff0c;主要用来&#xff1a; 1.监视设备存储&#xff08;“/ data”&#xff09;。 2.每60秒扫描一次免费存储空间(谷歌默认值) 3.当设备的存储空间不足…

Ollama:一个在本地部署、运行大型语言模型的工具

Ollama&#xff1a;一个在本地部署、运行大型语言模型的工具 Ollama部署、运行大型语言模型概述安装配置Ollama命令模型库使用示例自定义模型从GGUF导入自定义提示从PyTorch或Safetensors导入 开启服务REST API卸载Ollama One-API概述One-API管理本地模型 Open WebUI概述Docker…

VUE3 学习笔记(十)查看vue版本

命令&#xff1a; npm list vue(空) (在项目的根目录下执行以下命令即可查看项目所使用的vue版本) npm list vue version(空) npm info vue (全局查看vue版本号&#xff0c;详细) npm list vue -g(全局查看vue版本号&#xff0c;简单) npm view vue version(查看项目依赖的vue…

Jeecg | 如何解决 ERR Client sent AUTH, but no password is set 问题

最近在尝试Jeecg低代码开发&#xff0c;但是碰到了超级多的问题&#xff0c;不过总归是成功运行起来了。 下面说说碰到的最后一个配置问题&#xff1a;连接redis失败 Error starting ApplicationContext. To display the conditions report re-run your application with deb…

解决鼠标滚动时element-ui下拉框错位的问题

问题描述&#xff1a;elementUi的el-select下拉选择框,打开之后,直到失去焦点才会自动关闭。 在有滚动条的弹窗中使用时就会出现打开下拉框,滚动弹窗,el-select下拉框会超出弹窗范围的问题. 解决方案&#xff1a; 1、先在util文件夹下创建个hideSelect.js文件&#xff0c;代码…

内网穿透--Nps-自定义-上线

免责声明:本文仅做技术交流与学习... 目录 Nps项目: 一图通解: 1-下载nps/npc 2-服务端启动 访问web网页: 添加客户端&#xff0c;生成密匙. 3-kali客户端连接服务端 4-添加协议隧道. 5-kali生成后门&#xff1a; 6-kali创建监听: Nps项目: https://github.com/ehang…

算法刷题笔记 高精度乘法(C++实现)

文章目录 题目描述解题思路解题代码 题目描述 给定两个非负整数&#xff08;不含前导0&#xff09;A和B&#xff0c;请你计算 AB的值。 输入格式 共两行&#xff0c;第一行包含整数 A&#xff0c;第二行包含整数 B。 输出格式 共一行&#xff0c;包含AB的值。 数据范围 …

Putty: 随心御剑——远程启动服务工具plink

一、引言:如何远程控制 也许你会有这样的场景,交互程序(以下简称UI程序)跑在windows端,而控制程序跑在Linux上。我们想要通过windows端 UI程序来启动Linux下面的服务,来一场酣畅淋漓的御剑飞行咋办,难道要自己十年磨一剑,在Linux下编写一个受控服务程序么.计算机科技发…

最新:windows下安装pcl点云库

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

【问题解决】ImportError: generic_type: cannot initialize type “ExternalAllocator“

一、问题描述 我的环境是Ubuntu20.04&#xff0c;Cuda版本是11.4&#xff0c;在复现OpenPCDet的时候遇到了下面问题&#xff1a; Traceback (most recent call last):File "train.py", line 7, in <module>from test import repeat_eval_ckptFile "/mnt…

Scala的简单学习一

一 相关知识 1.1 scala的安装 1.在idea中导入依赖&#xff0c;并在Idea下载scala插件 1.2 scala基础知识点 1.scala代码中一行语句的结束是以换行符为标准&#xff0c;可以不用写分号 2.class是一个普通的类&#xff0c;object相当于一个单例对象&#xff0c;object类中的…

git分支策略(github-flow VS git flow,如何选择)

一. 结论 Github flow&#xff1a;最简单 小型项目&#xff0c;持续部署&#xff0c;自动化测试程度高&#xff0c;发布流程简单 Git flow&#xff1a;复杂但最常用 大型项目&#xff0c;发布周期长&#xff0c;需要同时维护多个版本&#xff0c;发布流程复杂 表格提供了不…

36PE启动盘新秀:Ventoy(附各种PE的ISO下载)

PE启动盘新秀:Ventoy(附各种PE的ISO下载) 在我们以前的认知中,一个U盘只能制作包含一个系统的启动盘.比如,安装了微PE工具箱的U盘就不能安装其他什么PE工具箱了.这有时候让我们很无奈,只能买好多U盘,一个U盘一个PE系统. 这个问题的本质是什么?事实上,笔者认为,就是单个的ISO文…