【Unity3D】无限循环列表(扩展版)

  基础版:【Unity技术分享】UGUI之ScrollRect优化_ugui scrollrect 优化-CSDN博客

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

public delegate void OnBaseLoopListItemCallback(GameObject cell, int index);
public class BaseLoopList : MonoBehaviour
{
    public int m_Row = 1; //排
    public bool m_IsVertical = true;
    public float m_SpacingX = 0f; //间距
    public float m_SpacingY = 0f; //间距
    public GameObject m_CellGameObject; //指定的cell

    private Dictionary<GameObject, int> m_GameObjectNumDict;


    //-> 回调相关        
    public event OnBaseLoopListItemCallback onItemShow; //Lua 回调
    public event OnBaseLoopListItemCallback onItemHide; //Lua 回调

    protected RectTransform rectTrans;


    protected float m_PlaneWidth;
    protected float m_PlaneHeight;


    protected float m_ContentWidth;
    protected float m_ContentHeight;


    protected float m_CellObjectWidth;
    protected float m_CellObjectHeight;


    protected GameObject m_Content;
    protected RectTransform m_ContentRectTrans;


    private bool m_isInited = false;


    //记录 物体的坐标 和 物体 
    protected struct CellInfo
    {
        public Vector3 pos;
        public GameObject obj;
    };
    protected CellInfo[] m_CellInfos;


    protected bool m_IsInited = false;


    protected ScrollRect m_ScrollRect;


    protected int m_MaxCount = -1; //列表数量


    protected int m_MinIndex = -1;
    protected int m_MaxIndex = -1;


    protected bool m_IsClearList = false; //是否清空列表

    protected Vector4 m_Padding = Vector4.zero; //(left,top,right,bottom) 左,上 偏移Item(左上角为锚点) 右,下 扩大Content(宽度和高度)


    //c# 初始化
    public virtual void Init()
    {
        if (m_isInited)
            return;

        m_isInited = true;

        m_GameObjectNumDict = new Dictionary<GameObject, int>();
        m_Content = this.GetComponent<ScrollRect>().content.gameObject;


        if (m_CellGameObject == null)
        {
            //m_CellGameObject = m_Content.transform.GetChild(0).gameObject;
            Debug.LogError("m_CellGameObject 不能为 null");
        }
        /* Cell 处理 */
        //m_CellGameObject.transform.SetParent(m_Content.transform.parent, false);
        //SetPoolsObj(m_CellGameObject);

        RectTransform cellRectTrans = m_CellGameObject.GetComponent<RectTransform>();
        cellRectTrans.pivot = new Vector2(0f, 1f);
        CheckAnchor(cellRectTrans);
        cellRectTrans.anchoredPosition = Vector2.zero;


        //记录 Cell 信息
        m_CellObjectHeight = cellRectTrans.rect.height;
        m_CellObjectWidth = cellRectTrans.rect.width;


        //记录 Plane 信息
        rectTrans = GetComponent<RectTransform>();
        Rect planeRect = rectTrans.rect;
        m_PlaneHeight = planeRect.height;
        m_PlaneWidth = planeRect.width;


        //记录 Content 信息
        m_ContentRectTrans = m_Content.GetComponent<RectTransform>();
        Rect contentRect = m_ContentRectTrans.rect;
        SetContentHeight(contentRect.height);
        SetContentWidth(contentRect.width);


        m_ContentRectTrans.pivot = new Vector2(0f, 1f);
        //m_ContentRectTrans.sizeDelta = new Vector2 (planeRect.width, planeRect.height);
        //m_ContentRectTrans.anchoredPosition = Vector2.zero;
        CheckAnchor(m_ContentRectTrans);


        m_ScrollRect = this.GetComponent<ScrollRect>();


        m_ScrollRect.onValueChanged.RemoveAllListeners();
        //添加滑动事件
        m_ScrollRect.onValueChanged.AddListener(delegate (Vector2 value) { ScrollRectListener(value); });
    }

    public virtual void Destroy()
    {
        if (m_CellInfos != null)
        {
            for (int i = 0; i < m_CellInfos.Length; i++)
            {
                SetPoolsObj(m_CellInfos[i].obj);
                m_CellInfos[i].obj = null;
            }
        }
        m_GameObjectNumDict.Clear();
    }

    private void DisposeAll()
    {
        m_Padding = Vector4.zero;
        onItemShow = null;
        onItemHide = null;
        if (poolsObj != null)
        {
            foreach (var v in poolsObj)
            {
                if (v != null)
                {
                    GameObject.Destroy(v);
                }
            }
            poolsObj = null;
        }
        if (m_CellInfos != null)
        {
            foreach (var v in m_CellInfos)
            {
                if (v.obj != null)
                {
                    GameObject.Destroy(v.obj);
                }
            }
            m_CellInfos = null;
        }
    }

    //检查 Anchor 是否正确
    private void CheckAnchor(RectTransform rectTrans)
    {
        if (m_IsVertical)
        {
            if (!((rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(0, 1)) ||
                     (rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(1, 1))))
            {
                rectTrans.anchorMin = new Vector2(0, 1);
                rectTrans.anchorMax = new Vector2(1, 1);
            }
        }
        else
        {
            if (!((rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(0, 1)) ||
                     (rectTrans.anchorMin == new Vector2(0, 0) && rectTrans.anchorMax == new Vector2(0, 1))))
            {
                rectTrans.anchorMin = new Vector2(0, 0);
                rectTrans.anchorMax = new Vector2(0, 1);
            }
        }
    }

    public void SetPadding(float left, float top, float right, float bottom)
    {
        m_Padding = new Vector4(left, top, right, bottom);
        SetContentWidth(m_ContentWidth);
        SetContentHeight(m_ContentHeight);
    }

    private float GetPaddingX()
    {
        return m_Padding.x;
    }

    private float GetPaddingY()
    {
        return m_Padding.y;
    }

    private float GetExpandWidth()
    {
        return m_Padding.z;
    }

    private float GetExpandHeight()
    {
        return m_Padding.w;
    }

    public void SetContentWidth(float value)
    {
        m_ContentWidth = value + GetPaddingX() + GetExpandWidth();
        m_ContentWidth = m_ContentWidth < rectTrans.rect.width ? rectTrans.rect.width : m_ContentWidth;
    }

    public void SetContentHeight(float value)
    {
        m_ContentHeight = value + GetPaddingY() + GetExpandHeight();
        m_ContentHeight = m_ContentHeight < rectTrans.rect.height ? rectTrans.rect.height : m_ContentHeight;
    }

    public virtual void SetCount(int num)
    {
        m_MinIndex = -1;
        m_MaxIndex = -1;


        //-> 计算 Content 尺寸
        if (m_IsVertical)
        {
            float contentSize = (m_SpacingY + m_CellObjectHeight) * Mathf.CeilToInt((float)num / m_Row);
            SetContentHeight(contentSize);
            SetContentWidth(m_ContentRectTrans.rect.width);
            m_ContentRectTrans.sizeDelta = new Vector2(m_ContentWidth, m_ContentHeight);
            if (num != m_MaxCount)
            {
                m_ContentRectTrans.anchoredPosition = new Vector2(m_ContentRectTrans.anchoredPosition.x, 0);
            }
        }
        else
        {
            float contentSize = (m_SpacingX + m_CellObjectWidth) * Mathf.CeilToInt((float)num / m_Row);
            SetContentWidth(contentSize);
            SetContentHeight(m_ContentRectTrans.rect.height);
            m_ContentRectTrans.sizeDelta = new Vector2(m_ContentWidth, m_ContentHeight);
            if (num != m_MaxCount)
            {
                m_ContentRectTrans.anchoredPosition = new Vector2(0, m_ContentRectTrans.anchoredPosition.y);
            }
        }

        //-> 计算 开始索引
        int lastEndIndex = 0;


        //-> 过多的物体 扔到对象池 ( 首次调 ShowList函数时 则无效 )
        if (m_IsInited)
        {
            lastEndIndex = num - m_MaxCount > 0 ? m_MaxCount : num;
            lastEndIndex = m_IsClearList ? 0 : lastEndIndex;


            int count = m_IsClearList ? m_CellInfos.Length : m_MaxCount;
            for (int i = lastEndIndex; i < count; i++)
            {
                if (m_CellInfos[i].obj != null)
                {
                    SetPoolsObj(m_CellInfos[i].obj);
                    if (onItemHide != null)
                    {
                        int objNum = 0;
                        m_GameObjectNumDict.TryGetValue(m_CellInfos[i].obj, out objNum);
                        onItemHide.Invoke(m_CellInfos[i].obj, objNum);
                    }
                    m_CellInfos[i].obj = null;
                }
            }
        }


        //-> 以下四行代码 在for循环所用
        CellInfo[] tempCellInfos = m_CellInfos;
        m_CellInfos = new CellInfo[num];


        //-> 1: 计算 每个Cell坐标并存储 2: 显示范围内的 Cell
        for (int i = 0; i < num; i++)
        {
            // * -> 存储 已有的数据 ( 首次调 ShowList函数时 则无效 )
            if (m_MaxCount != -1 && i < lastEndIndex)
            {
                CellInfo tempCellInfo = tempCellInfos[i];
                //-> 计算是否超出范围
                float rPos = m_IsVertical ? tempCellInfo.pos.y : tempCellInfo.pos.x;
                if (!IsOutRange(rPos))
                {
                    //-> 记录显示范围中的 首位index 和 末尾index
                    m_MinIndex = m_MinIndex == -1 ? i : m_MinIndex; //首位index
                    m_MaxIndex = i; // 末尾index


                    if (tempCellInfo.obj == null)
                    {
                        tempCellInfo.obj = GetPoolsObj();
                    }
                    tempCellInfo.obj.transform.GetComponent<RectTransform>().anchoredPosition = tempCellInfo.pos;
                    if (!m_GameObjectNumDict.ContainsKey(tempCellInfo.obj))
                    {
                        m_GameObjectNumDict.Add(tempCellInfo.obj, i);
                    }
                    else
                    {
                        m_GameObjectNumDict[tempCellInfo.obj] = i;
                    }
                    tempCellInfo.obj.SetActive(true);


                    //-> 回调 Lua 函数
                    Func(tempCellInfo.obj);
                }
                else
                {
                    if (tempCellInfo.obj != null)
                    {
                        SetPoolsObj(tempCellInfo.obj);
                        if (onItemHide != null)
                        {
                            int objNum = 0;
                            m_GameObjectNumDict.TryGetValue(tempCellInfo.obj, out objNum);
                            onItemHide.Invoke(tempCellInfo.obj, objNum);
                        }
                        tempCellInfo.obj = null;
                    }
                }
                m_CellInfos[i] = tempCellInfo;
                continue;
            }


            CellInfo cellInfo = new CellInfo();


            float pos = 0;  //坐标( isVertical ? 记录Y : 记录X )
            float rowPos = 0; //计算每排里面的cell 坐标


            // * -> 计算每个Cell坐标
            if (m_IsVertical)
            {
                pos = m_CellObjectHeight * Mathf.FloorToInt(i / m_Row) + m_SpacingY * Mathf.FloorToInt(i / m_Row);
                rowPos = m_CellObjectWidth * (i % m_Row) + m_SpacingX * (i % m_Row);
                cellInfo.pos = new Vector3(rowPos + GetPaddingX(), -pos - GetPaddingY(), 0);
            }
            else
            {
                pos = m_CellObjectWidth * Mathf.FloorToInt(i / m_Row) + m_SpacingX * Mathf.FloorToInt(i / m_Row);
                rowPos = m_CellObjectHeight * (i % m_Row) + m_SpacingY * (i % m_Row);
                cellInfo.pos = new Vector3(pos + GetPaddingX(), -rowPos - GetPaddingY(), 0);
            }


            //-> 计算是否超出范围
            float cellPos = m_IsVertical ? cellInfo.pos.y : cellInfo.pos.x;
            if (IsOutRange(cellPos))
            {
                cellInfo.obj = null;
                m_CellInfos[i] = cellInfo;
                continue;
            }


            //-> 记录显示范围中的 首位index 和 末尾index
            m_MinIndex = m_MinIndex == -1 ? i : m_MinIndex; //首位index
            m_MaxIndex = i; // 末尾index


            //-> 取或创建 Cell
            GameObject cell = GetPoolsObj();
            cell.transform.GetComponent<RectTransform>().anchoredPosition = cellInfo.pos;
            if (!m_GameObjectNumDict.ContainsKey(cell.gameObject))
            {
                m_GameObjectNumDict.Add(cell.gameObject, i);
            }
            else
            {
                m_GameObjectNumDict[cell.gameObject] = i;
            }


            //-> 存数据
            cellInfo.obj = cell;
            m_CellInfos[i] = cellInfo;


            //-> 回调 Lua 函数
            Func(cell);
        }


        m_MaxCount = num;
        m_IsInited = true;


    }


    //实时刷新列表时用
    public virtual void UpdateList()
    {
        NormalPerformanceMode();
    }


    //刷新某一项
    public void UpdateCell(int index)
    {
        CellInfo cellInfo = m_CellInfos[index - 1];
        if (cellInfo.obj != null)
        {
            float rangePos = m_IsVertical ? cellInfo.pos.y : cellInfo.pos.x;
            if (!IsOutRange(rangePos))
            {
                Func(cellInfo.obj);
            }
        }
    }


    // 更新滚动区域的大小
    public void UpdateSize()
    {
        Rect rect = GetComponent<RectTransform>().rect;
        m_PlaneHeight = rect.height;
        m_PlaneWidth = rect.width;
    }


    //滑动事件
    protected virtual void ScrollRectListener(Vector2 value)
    {
        NormalPerformanceMode(); //普通性能模式
    }

    //普通性能模式
    private void NormalPerformanceMode()
    {
        if (m_CellInfos == null)
            return;

        //检查超出范围
        for (int i = 0, length = m_CellInfos.Length; i < length; i++)
        {
            CellInfo cellInfo = m_CellInfos[i];
            GameObject obj = cellInfo.obj;
            Vector3 pos = cellInfo.pos;


            float rangePos = m_IsVertical ? pos.y : pos.x;
            //判断是否超出显示范围
            if (IsOutRange(rangePos))
            {
                //把超出范围的cell 扔进 poolsObj里
                if (obj != null)
                {
                    SetPoolsObj(obj);
                    if (onItemHide != null)
                    {
                        int objNum = 0;
                        m_GameObjectNumDict.TryGetValue(obj, out objNum);
                        onItemHide.Invoke(obj, objNum);
                    }
                    m_CellInfos[i].obj = null;
                }
            }
            else
            {
                if (obj == null)
                {
                    //优先从 poolsObj中 取出 (poolsObj为空则返回 实例化的cell)
                    GameObject cell = GetPoolsObj();
                    cell.transform.localPosition = pos;
                    if (!m_GameObjectNumDict.ContainsKey(cell.gameObject))
                    {
                        m_GameObjectNumDict.Add(cell.gameObject, i);
                    }
                    else
                    {
                        m_GameObjectNumDict[cell.gameObject] = i;
                    }
                    m_CellInfos[i].obj = cell;


                    Func(cell);
                }
            }
        }
    }


    //判断是否超出显示范围
    protected bool IsOutRange(float pos)
    {
        Vector3 listP = m_ContentRectTrans.anchoredPosition;
        if (m_IsVertical)
        {
            if (pos + listP.y > m_CellObjectHeight || pos + listP.y < -rectTrans.rect.height)
            {
                return true;
            }
        }
        else
        {
            if (pos + listP.x < -m_CellObjectWidth || pos + listP.x > rectTrans.rect.width)
            {
                return true;
            }
        }
        return false;
    }


    //对象池 机制  (存入, 取出) cell
    protected Stack<GameObject> poolsObj = new Stack<GameObject>();
    //取出 cell
    protected virtual GameObject GetPoolsObj()
    {
        GameObject cell = null;
        if (poolsObj.Count > 0)
        {
            cell = poolsObj.Pop();
        }


        if (cell == null)
        {
            cell = Instantiate(m_CellGameObject) as GameObject;
        }
        cell.transform.SetParent(m_Content.transform);
        cell.transform.localScale = Vector3.one;
        SetActive(cell, true);


        return cell;
    }
    //存入 cell
    protected virtual void SetPoolsObj(GameObject cell)
    {
        if (cell != null)
        {
            poolsObj.Push(cell);
            SetActive(cell, false);
        }
    }


    //回调
    protected void Func(GameObject selectObject)
    {
        int num = 0;
        m_GameObjectNumDict.TryGetValue(selectObject, out num);
        onItemShow?.Invoke(selectObject, num);
    }

    void SetActive(GameObject cell, bool isShow)
    {
        cell.SetActive(isShow);
    }

    protected void OnDestroy()
    {
        DisposeAll();
    }
}

其他2个使用案例C#脚本代码:

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

public class LoopListManager : MonoBehaviour
{
    public BaseLoopList baseLoopList;
    private Dictionary<int, BaseLoopListItem> loopListItemDict;

    void Start()
    {
        baseLoopList.onItemShow += OnShow;
        baseLoopList.onItemHide += OnHide;
        loopListItemDict = new Dictionary<int, BaseLoopListItem>();
        baseLoopList.Init();
        //baseLoopList.SetPadding(20, 30, 0, 0);//支持Padding四个方向的偏移
        baseLoopList.SetCount(50);
    }

    private BaseLoopListItem GetItem(GameObject cell, int index)
    {
        BaseLoopListItem item;
        loopListItemDict.TryGetValue(index, out item);
        if (item == null)
        {
            item = cell.GetComponent<BaseLoopListItem>();
            loopListItemDict.Add(index, item);
        }
        return item;
    }

    public void OnShow(GameObject cell, int index)
    {
        Debug.Log("Show:" + index);
        BaseLoopListItem item = GetItem(cell, index);
        item.OnShow(index);
    }

    public void OnHide(GameObject cell, int index)
    {
        Debug.Log("Hide:" + index);
        BaseLoopListItem item = GetItem(cell, index);
        item.OnHide(index);

        //注意: 无限循环列表的Item是重复利用的物体,逻辑上Hide之后必须从缓存中移除,否则会一定会出现Item刷新异常;
        //原因: 若不移除会出现Show(index物体)时会拿到一个不正确位置的物体, 甚至很大可能是一个已显示中的物体(形成覆盖效果)
        loopListItemDict.Remove(index);
    }
}
using UnityEngine;
using TMPro;
public class BaseLoopListItem : MonoBehaviour
{
    public TextMeshProUGUI text;

    public void OnShow(int index)
    {
        text.text = index.ToString();
    }

    public void OnHide(int index)
    {
        text.text = "";
    }
}

注意事项:无限循环列表的Item物体是重复利用的,因此OnHide回调后必须将传递进来的物体所有相关的缓存引用清空,例如案例是将其从字典移除loopListItemDict.Remove(index);

Content选择左上角锚点(看情况可选其他,但不要用自适应stretch) 以及不要挂任何自动控制布局组件,如:VerticalLayoutGroup、HorizontalLayoutGroup、GridLayoutGroup、ContentSizeFitter等...(因为会破坏无限循环列表对Content的控制)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Elasticsearch】使用阿里云 infererence API 及 semantic text 进行向量搜索

原作者&#xff1a;Elastic布道师 刘晓国 在之前的文章 “Elasticsearch 开放推理 API 新增阿里云 AI 搜索支持”&#xff0c;它详细描述了如何使用 Elastic inference API 来针对阿里的密集向量模型&#xff0c;稀疏向量模型&#xff0c; 重新排名及 completion 进行展示。在…

景联文科技:精准语音标注,驱动语音技术新发展

在人工智能迅速发展的今天&#xff0c;语音技术的应用已经渗透到我们生活的方方面面。从智能音箱、语音助手到自动语音识别系统&#xff0c;高质量的语音数据是这些应用成功的关键。景联文科技作为领先的AI数据服务提供商&#xff0c;专注于为客户提供高精度、高效的语音标注服…

windows免登录linux

windows 生成秘钥文件 ssh-keygen -t rsa 将公钥传送到服务器 scp C:\Users\xx/.ssh/id_rsa.pub xxxx:/home/ruoyi/id_rsa.pub linux 使用ssh-copy-id -i ~/.ssh/id_rsa.pub userhost 如果禁用root登录&#xff0c;先开启 vim /etc/ssh/sshd_config PermitRootLogin yes …

基于容器的云原生,让业务更自由地翱翔云端

无论是要构建一个应用或开发一个更庞大的解决方案&#xff0c;在技术选型时&#xff0c;技术的开放性和可移植性已经成为很多企业优先考虑的问题之一。毕竟没人希望自己未来的发展方向和成长速度被自己若干年前选择使用的某项技术所限制或拖累。 那么当你的业务已经上云&#x…

Visual Studio 使用 GitHub Copilot 协助调试

&#x1f380;&#x1f380;&#x1f380;【AI辅助编程系列】&#x1f380;&#x1f380;&#x1f380; Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码Visual Studio 安装和管理 GitHub CopilotVisual Studio 使用 GitHub Copilot 扩展Visual Studio 使用 GitHu…

springboot限流注解

我们在工作中 有些接口访问量过大 为了保证服务的正常运行可以增加限流 第一步 引入aop和redis <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency…

MySQL多表查询时有哪些连接方式?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL多表查询时有哪些连接方式?】面试题。希望对大家有帮助&#xff1b; MySQL多表查询时有哪些连接方式? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 MySQL 中进行多表查询时&#xff0c;常见的连接方式有以下…

Python | 虚拟环境01 - 什么是虚拟环境、它的由来

导言 python3真的不是安装了就完事&#xff0c;必须理解虚拟环境是什么才算是初步掌握python环境。 学习python3虚拟环境&#xff0c;建议参考B站教程。这位博主用了6个视频&#xff0c;每一个视频仅仅几分钟。居然把python3的虚拟环境讲明白了。 虚拟环境&#xff08;Virtual…

【已解决】在Visual Studio里将应用与Microsoft Store关联时提示网络异常

发布Windows应用时。在Visual Studio里点击"发布“&#xff0c;将应用与Microsoft Store关联时&#xff0c;一直提示网络错误。 查了一下论坛&#xff0c;发现之前也经常出现&#xff0c;但我是第一次遇到。 不能就这样一直被卡着呀&#xff0c;研究了一下&#xff0c;还…

html基础-认识html

1.什么是html html是浏览器可以识别的的标记语言&#xff0c;我们在浏览器浏览的网页就是一个个的html文档 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>认识html</title> </head> <body><h1…

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(四)

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(四) 你好,我是拉依达。 感谢所有阅读关注我的同学支持,目前博客累计阅读 27w,关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析(持续更新)-CSDN博客》已经是 Linux驱动 相关内容搜索的推荐首位,感谢大家支持。 《拉…

MySQL 深入理解隔离性

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 MySQL 深入理解隔离性 收录于专栏[MySQL] 本专栏旨在分享学习MySQL的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 由于之前在 MySQL 事务特…

Nacos 3.0 考虑升级到 Spring Boot 3 + JDK 17 了!

Nacos 由阿里开源&#xff0c;是 Spring Cloud Alibaba 中的一个重要组件&#xff0c;主要用于发现、配置和管理微服务。 由于 Spring Boot 2 的维护已于近期停止&#xff0c;Nacos 团队考虑升级到 Spring Boot 3 JDK 17&#xff0c;目前正在征求意见和建议。 这其实是一件好…

【硬件接口】I2C总线接口

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、概述 I2C总线是一种非常常用的总线&#xff0c;其多用于一个主机&#xff08;或多个&#xff09;与单个或多个从设备通讯…

监控视频汇聚融合云平台一站式解决视频资源管理痛点

随着5G技术的广泛应用&#xff0c;各领域都在通信技术加持下通过海量终端设备收集了大量视频、图像等物联网数据&#xff0c;并通过人工智能、大数据、视频监控等技术方式来让我们的世界更安全、更高效。然而&#xff0c;随着数字化建设和生产经营管理活动的长期开展&#xff0…

GEE+本地XGboot分类

GEE本地XGboot分类 我想做提取耕地提取&#xff0c;想到了一篇董金玮老师的一篇论文&#xff0c;这个论文是先提取的耕地&#xff0c;再做作物分类&#xff0c;耕地的提取代码是开源的。 但这个代码直接在云端上进行分类&#xff0c;GEE会爆内存&#xff0c;因此我准备把数据下…

Spring Boot 集成 MyBatis 全面讲解

Spring Boot 集成 MyBatis 全面讲解 MyBatis 是一款优秀的持久层框架&#xff0c;与 Spring Boot 集成后可以大大简化开发流程。本文将全面讲解如何在 Spring Boot 中集成 MyBatis&#xff0c;包括环境配置、基础操作、高级功能和最佳实践。 一、MyBatis 简介 1. SqlSession …

Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:电影院后台管理系统(前后端源码 + 数据库 sql 脚本)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 项目介绍 2.0 用户登录功能 3.0 用户管理功能 4.0 影院管理功能 5.0 电影管理功能 6.0 影厅管理功能 7.0 电影排片管理功能 8.0 用户评论管理功能 9.0 用户购票功…

【字符串匹配算法——BF算法】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 BF算法介绍及过程演示代码实现过程下节预告KMP算法利用next数组存储子串中j回退的位置&#xff08;…

单幅图像合成 360° 3D 场景的新方法:PanoDreamer,可同时生成全景图像和相应的深度信息。

论文介绍了一种从单幅图像合成 360 3D 场景的新方法。该方法以连贯的方式生成全景图及其相应的深度&#xff0c;解决了现有最先进方法&#xff08;如 LucidDreamer 和 WonderJourney 的局限性。这些方法按照生成轨迹依次添加细节&#xff0c;通常在循环回输入图像时导致可见的接…