Unity 中实现首尾无限循环的 ListView

之前已经实现过:

Unity 中实现可复用的 ListView-CSDN博客文章浏览阅读5.6k次,点赞2次,收藏27次。源码已放入我的 github,地址:Unity-ListView前言实现一个列表组件,表现方面最核心的部分就是重写布局(Layout)。对于简单的列表,尤其是"Cell数量固定且较少、没有超页滚动展示"一类的需求,使用UGUI自带的布局组件进行布局即可。分别为:水平布局组件(Horizontal Layout Group)、竖直布局组件(Vertical Layout Gro..._unity listviewhttps://blog.csdn.net/NRatel/article/details/100561203Unity 中实现可复用的 GridView-CSDN博客文章浏览阅读4k次。本文介绍了如何基于Unity的UGUI系统设计一个灵活的GridView组件。作者分析了GridLayoutGroup的参数,讨论了StartCorner和StartAxis的排布方式、Constraint的灵活性以及Padding与对齐方式的巧妙结合。在实现过程中,修改了ScrollRect的关联ScrollBar和布局接口,设计了适应不同滑动方向的布局,并实现了元素复用逻辑,包括四种滑动方向的计算。此外,还探讨了Content锚点、行列约束和对齐方式的调整,以提高组件的易用性。https://blog.csdn.net/NRatel/article/details/124063559首尾无限循环 的列表,还是以 ListView 为基础。

在此之前,先参照 GrideView 修改 ListView:

使其 继承 UIScrollRect(原因是必须修改部分源码)

并支持参数:

1、MovementAxis :

横向滑动 或 竖向滑动。

2、StartSide :

横向滑动时,可选 元素的排列方向:从左往右 或 从右往左;

竖向滑动时,可选 元素的排列方向:从上往下 或 从下往上。

3、ChildAligment :

横向滑动时,可选 元素的上下对齐方式:居上/居中/居下;

竖向滑动时,可选 元素的左右对齐方式:居左/居中/居右。

----------------------------------------- NRatel割 -----------------------------------------

Loop 首尾循环的改动要点:

(以下仅以 MovementAxis=Horizontal,StartSide=Left 的情形阐述)

先看这种情况:核心内容宽度 > viewport 宽度

1、使 Content 在移动时,非原核心内容区也能够显示 0~Count-1 范围内的 Cell元素,让越界索引不要提前retrun,而是在显示时转到 0~Count-1 之中。

如图:黑色区域为 Content, 当其继续往右滑动时,进入Viewport、但已超出 Content 的部分,仍能生成越界元素,并能将越界索引转到 界内索引之中。

需要修改 CalcIndexes,使其不要立刻拦截越界索引。而是在 DisAppearCells 和 AppearCells 时,根据 索引值判断是否越界,抽出两个方法:

cs 复制代码
//是否有效索引(只将显示索引显示到列表中,默认为 0~cellCount 之间)
//loop时,认为任意索引都是有效的,以使非 0~cellCount 的区域能够显示元素,之后再在 ConvertIndexToValid 转换
protected virtual bool IsValidIndex(int index)
{
    if (m_Loop) { return true; }
    else { return index >= 0 && index < m_CellCount; }
}
//转换索引至有效(默认无需处理)
//loop时,将任意索引数转到 [0~cellCount-1] 中
protected virtual int ConvertIndexToValid(int index)
{
    if (m_Loop) { return (index % m_CellCount + m_CellCount) % m_CellCount; }
    else { return index; }
}

处理完本条之后,理论上,将 Content的宽度设为 无限大,就可以直接支持首尾无限循环。

不过,最好还是选择位置重置的方案。

2、滑动时,从初始位置开始,只要向左/向右滑出超过1个重置单位,就将 Content 重置回起始位置。

(注意,滑动过程,完全无需考虑Cell显示问题,完全由①处理,可将Content想象为一张整图)

(注意这里说的 1个重置单位 = 1核心内容宽度 + 1个spacingX)

3、将 Content 宽度扩为原核心内容宽度扩展的N倍,使其满足位置重置的基本条件。

支持从初始位置开始,向左向右各滑动1个重置单位,需要在两侧至少各扩展出1个重置单位。

但为了避免 翻超1个重置单位触及边缘 触发回弹,可以额外多出1或2个重置单位。

注意,扩宽 Content 更多倍是毫无成本的!这里只是思考至少应该扩展几倍。

所以,直接定为 2+2=4倍。

4、再回头来思考 核心内容宽度 < viewport宽度 的倍数

在上面的基础上,只需简单处理:将 核心内容宽度 先翻倍,使超过 viewport宽度。

注意,这种情况下,在Viewport中会出现多个同一Cell,属于正常现象。

5、重置Content位置。但小心不能影响到 ScrollRect 内部的 速度值计算!!!

我用了不少时间才解决这个问题。

位置的重置,不能放在 OnScrollValueChanged。原因是:

①、ScrollRect 的 LateUpdate中,有逻辑为:

开启惯性速度选项,进行拖拽时,会根据Content相邻两帧的 位置确定后续的 惯性起始速度。

因此,如果在滑动过程中,如果只突然修改 Content 位置,将会导致 速度剧变。

②、在 OnScrollValueChanged 修改 Content 位置过晚,将使 其他注册 OnScrollValueChanged 的地方无法获取的真实 value。

因此,可在 SetContentAnchoredPosition 方法中,增加一个虚方法,供子类重写修改Content 的位置。

在具体修改Content位置时,还需注意:

①、要同时更新 m_PrevPosition,以使本帧 LateUpdate中 计算的 m_Velocity 不会因位置剧变而剧变。

②、要同时更新 m_ContentStartPosition,以使 OnDrag 中,Content位置跟随鼠标移动时,不反复触发此"位置超过一页的重置逻辑",否则下一帧 m_PrevPosition 又将执行一次偏移(上一行代码),还是会导致速度剧变。

cs 复制代码
//Content初始位置
float contentStartPosX = -m_CellStartOffsetOnMovementAxis;
//获取当前位置
float curContentPosX = m_Content.anchoredPosition.x;
//Content向左时,Content重置点坐标(初始位置左侧1个重置宽度)
float leftResetPosX = contentStartPosX - m_LoopResetSizeOnMovementAxis;
//Content向右时,Content重置点坐标(初始位置右侧1个重置宽度)
float rightResetPosX = contentStartPosX + m_LoopResetSizeOnMovementAxis;
if (curContentPosX < leftResetPosX)
{
    m_Content.anchoredPosition += Vector2.right * m_LoopResetSizeOnMovementAxis;
    //更新,以使本帧 LateUpdate中 计算的 m_Velocity 不会因位置剧变而剧变
    m_PrevPosition += Vector2.right * m_LoopResetSizeOnMovementAxis;
    //更新,以使 OnDrag 中,Content位置跟随鼠标移动时,不反复触发此"位置超过一页的重置逻辑",否则下一帧m_PrevPosition又将执行一次偏移(上一行代码),还是会导致速度剧变
    m_ContentStartPosition += Vector2.right * m_LoopResetSizeOnMovementAxis;
}
else if (curContentPosX > rightResetPosX)
{
    m_Content.anchoredPosition += Vector2.left * m_LoopResetSizeOnMovementAxis;
    //更新,以使本帧 LateUpdate中 计算的 m_Velocity 不会因位置剧变而剧变
    m_PrevPosition += Vector2.left * m_LoopResetSizeOnMovementAxis;
    //更新,以使 OnDrag 中,Content位置跟随鼠标移动时,不反复触发此"位置超过一页的重置逻辑",否则下一帧m_PrevPosition又将执行一次偏移(上一行代码),还是会导致速度剧变
    m_ContentStartPosition += Vector2.left * m_LoopResetSizeOnMovementAxis;
}

----------------------------------------- NRatel割 -----------------------------------------

实现效果:

源码:

https://github.com/NRatel/Unity-ListViewhttps://github.com/NRatel/Unity-ListView

相关推荐
C MIKE33 分钟前
unity资源下载
unity
Avalon71235 分钟前
Unity中自定义协程的实现
游戏·unity·c#·游戏引擎
jtymyxmz2 小时前
《Unity shader》10.1.5 菲涅尔反射
unity·游戏引擎
老朱佩琪!3 小时前
Unity文字排版错位问题
经验分享·unity·游戏引擎
jtymyxmz3 小时前
《Unity Shader》9.4.3 使用帧调试器查看阴影绘制过程
unity·游戏引擎
jtymyxmz3 小时前
《Unity Shader》10.3.1 在Unity中实现简单的程序纹理
unity·游戏引擎
jtymyxmz3 小时前
《Unity Shader》11.2.1 序列帧动画
unity·游戏引擎
UX201713 小时前
Unity中的Color.HSVToRGB
unity·游戏引擎
TO_ZRG13 小时前
Unity PackageManager
unity·gitlab
jtymyxmz15 小时前
《Unity Shader》10.1.2 创建用于环境映射的立方体纹理
unity·游戏引擎